Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[explorev2] chart and controls #1251

Merged
merged 29 commits into from
Oct 6, 2016
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2a5a3ed
create structure for new forked explore view (#1099)
Sep 14, 2016
7c77286
Created store and reducers (#1108)
vera-liu Sep 21, 2016
9931490
do use bootstrap data for now
Sep 23, 2016
7175952
don't deal with bootstrap data for now
Sep 23, 2016
25d70d6
use victory as a base
Sep 26, 2016
085b424
import fake line data, add fake panels, make chart fixed
Sep 26, 2016
03b6316
add fetch support
Sep 26, 2016
c1aed1d
get slice data from json endpoint
Sep 26, 2016
3e4375e
render chart with slicejson
Sep 26, 2016
f3cf9a9
update chart and label demo
Sep 27, 2016
dc9a705
remove fetch config
Sep 29, 2016
e29bca9
remove dummy control panels
Sep 29, 2016
e5cc1c5
should be a func
Sep 29, 2016
fa6a480
make TimeSeriesLineChart
Sep 29, 2016
e036d95
add a comment
Sep 29, 2016
dc1669a
inner height for height
Sep 29, 2016
6741c4e
add line chart, dummy data, v2 query and save bans
Oct 4, 2016
dc9702b
don't need fetch yet
Oct 4, 2016
4bacd74
trailing comma breaks in package json
Oct 4, 2016
65aa771
pass in viz data from props
Oct 4, 2016
445b432
add style sheet
Oct 5, 2016
aa7502b
set height on explore container
Oct 5, 2016
f0a7f10
add legend
Oct 5, 2016
84492bb
make chart responsive to window resize
Oct 5, 2016
ed767f9
can't use head_css in template bc overrides head_css in basic
Oct 5, 2016
76eb092
fix linting
Oct 5, 2016
142ec07
break labelItem into own SFC, make legend SFC
Oct 5, 2016
c716043
add propTypes and fix linter
Oct 6, 2016
6f289a3
Merge branch 'master' into alanna-chart-and-controls
Oct 6, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions caravel/assets/javascripts/components/VictoryTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
const { assign } = Object;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated question, if we are not using babel-polyfill are we relying on Object.assign coming from chrome / the browser?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use babel and these presets so Object.assign is polyfilled.

 "babel": "^6.3.26",
    "babel-core": "^6.10.4",
    "babel-loader": "^6.2.4",
    "babel-preset-airbnb": "^2.0.0",
    "babel-preset-react": "^6.11.1",


const A11Y_BABU = '#00A699';
const AXIS_LINE_GRAY = '#484848';
const LABEL_TEXT_GRAY = '#767676';
const GRID_LINE_GRAY = '#DBDBDB';

// Colors
const colors = [
'#ffffff',
'#f0f0f0',
'#d9d9d9',
'#bdbdbd',
'#969696',
'#737373',
'#525252',
'#252525',
'#000000',
];

const charcoal = '#484848';

// Typography
const sansSerif = '"Roboto", sans-serif';
const letterSpacing = 'normal';
const fontSize = 8;

// Layout
const baseProps = {
width: 450,
height: 300,
padding: 50,
colorScale: colors,
};

// Labels
const baseLabelStyles = {
fontFamily: sansSerif,
fontSize,
letterSpacing,
padding: 10,
fill: charcoal,
stroke: 'transparent',
};

// Strokes
const strokeLinecap = 'round';
const strokeLinejoin = 'round';

// Create the theme
const theme = {
area: assign({
style: {
data: {
fill: charcoal,
},
labels: baseLabelStyles,
},
}, baseProps),
axis: assign({
style: {
axis: {
fill: 'none',
stroke: AXIS_LINE_GRAY,
strokeWidth: 1,
strokeLinecap,
strokeLinejoin,
},
axisLabel: assign({}, baseLabelStyles, {
padding: 25,
}),
grid: {
fill: 'none',
stroke: 'transparent',
},
ticks: {
fill: 'none',
padding: 10,
size: 1,
stroke: 'transparent',
},
tickLabels: baseLabelStyles,
},
}, baseProps),
bar: assign({
style: {
data: {
fill: A11Y_BABU,
padding: 10,
stroke: 'transparent',
strokeWidth: 0,
width: 8,
},
labels: baseLabelStyles,
},
}, baseProps),
candlestick: assign({
style: {
data: {
stroke: A11Y_BABU,
strokeWidth: 1,
},
labels: assign({}, baseLabelStyles, {
padding: 25,
textAnchor: 'end',
}),
},
candleColors: {
positive: '#ffffff',
negative: charcoal,
},
}, baseProps),
chart: baseProps,
errorbar: assign({
style: {
data: {
fill: 'none',
stroke: charcoal,
strokeWidth: 2,
},
labels: assign({}, baseLabelStyles, {
textAnchor: 'start',
}),
},
}, baseProps),
group: assign({
colorScale: colors,
}, baseProps),
line: assign({
style: {
data: {
fill: 'none',
stroke: A11Y_BABU,
strokeWidth: 2,
},
labels: assign({}, baseLabelStyles, {
textAnchor: 'start',
}),
},
}, baseProps),
pie: {
style: {
data: {
padding: 10,
stroke: 'none',
strokeWidth: 1,
},
labels: assign({}, baseLabelStyles, {
padding: 200,
textAnchor: 'middle',
}),
},
colorScale: colors,
width: 400,
height: 400,
padding: 50,
},
scatter: assign({
style: {
data: {
fill: charcoal,
stroke: 'transparent',
strokeWidth: 0,
},
labels: assign({}, baseLabelStyles, {
textAnchor: 'middle',
}),
},
}, baseProps),
stack: assign({
colorScale: colors,
}, baseProps),
};

export default theme;
61 changes: 53 additions & 8 deletions caravel/assets/javascripts/explorev2/components/ChartContainer.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,56 @@
import React from 'react';
import { Panel } from 'react-bootstrap';
import TimeSeriesLineChart from './charts/TimeSeriesLineChart';

const ChartContainer = function () {
return (
<Panel header="Chart title">
chart goes here
</Panel>
);
};
export default ChartContainer;
export default class ChartContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
params: this.getParamsFromUrl(),
data: props.viz.data,
height: window.innerHeight,
label1: 'Label 1',
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should you add this.getParamsFromUrl = this.getParamsFromUrl.bind(this) + same for formatDates?

Copy link
Author

@ascott ascott Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since getParamsFromUrl and formatDates don't need access to this we don't need to bind this to the methods. typically we would use bind(this) if we were passing a method to an event callback that needed access to the components context.

}

getParamsFromUrl() {
const hash = window.location.search;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious if we've thought about using react router for url state management? I used it in dataportal and liked it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good suggestion... let's keep it in mind as we continue the refactor

const params = hash.split('?')[1].split('&');
const newParams = {};
params.forEach((p) => {
const value = p.split('=')[1].replace(/\+/g, ' ');
const key = p.split('=')[0];
newParams[key] = value;
});
return newParams;
}

formatDates(values) {
const newValues = values.map((val) => {
return {
x: moment(new Date(val.x)).format('MMM D'),
Copy link
Contributor

@williaster williaster Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI d3 also has really good date formatting utilities, not sure if it'd be more lightweight than moment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i'm not sure either.. let's use moment for now and we can change to use d3 time utils in the future if that makes sense

y: val.y,
};
});
return newValues;
}

render() {
return (
<div className="chart-container">
<Panel
style={{ height: this.state.height }}
header={
<div className="panel-title">{this.props.viz.form_data.slice_name}</div>
}
>
<TimeSeriesLineChart
data={this.state.data}
label1="Percentage"
height={this.state.height}
/>
</Panel>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,45 @@ import ChartContainer from './ChartContainer';
import ControlPanelsContainer from './ControlPanelsContainer';
import QueryAndSaveButtons from './QueryAndSaveButtons';

const ExploreViewContainer = function () {
return (
<div className="container-fluid">
<div className="row">
<div className="col-sm-3">
<QueryAndSaveButtons
canAdd="True"
onQuery={() => { console.log('clicked query'); }}
/>
<br /><br />
<ControlPanelsContainer />
</div>
<div className="col-sm-9">
<ChartContainer />
export default class ExploreViewContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
height: this.getHeight(),
};
}

getHeight() {
const navHeight = 90;
return (window.innerHeight - navHeight) + 'px';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we use template literals anywhere or is this just a preference you have?
${window.innerHeight - navHeight}px

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template literals are nice, will change. mostly just old habit here :)

}

render() {
return (
<div
className="container-fluid"
style={{
height: this.state.height,
overflow: 'hidden',
}}
>
<div className="row table-body">
<div className="table-cell col-sm-4">
<QueryAndSaveButtons
canAdd="True"
onQuery={() => {}}
/>
<br /><br />
<ControlPanelsContainer />
</div>
<div className="table-cell col-sm-8">
<ChartContainer
viz={this.props.data.viz}
height={this.state.height}
/>
</div>
</div>
</div>
</div>
);
);
}
};

export default ExploreViewContainer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { PropTypes } from 'react';
import classnames from 'classnames';

const propTypes = {
canAdd: PropTypes.string.isRequired,
onQuery: PropTypes.func.isRequired,
};

export default function QueryAndSaveBtns({ canAdd, onQuery }) {
const saveClasses = classnames('btn btn-default btn-sm', {
'disabled disabledButton': canAdd !== 'True',
});

return (
<div className="btn-group query-and-save">
<button type="button" className="btn btn-primary btn-sm" onClick={onQuery}>
<i className="fa fa-bolt"></i> Query
</button>
<button
type="button"
className={saveClasses}
data-target="#save_modal"
data-toggle="modal"
>
<i className="fa fa-plus-circle"></i> Save as
</button>
</div>
);
}

QueryAndSaveBtns.propTypes = propTypes;
33 changes: 33 additions & 0 deletions caravel/assets/javascripts/explorev2/components/charts/Legend.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { PropTypes } from 'react';

const propTypes = {
data: PropTypes.array.isRequired,
keysToColorsMap: PropTypes.object.isRequired,
};

export default class Legend extends React.Component {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not a pure function here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, will simplify this

legendItem(key) {
return (
<li style={{ float: 'left' }} key={key}>
<i
className="fa fa-circle"
style={{ color: this.props.keysToColorsMap[key] }}
></i> &nbsp;&nbsp;
<span>{key}</span>
</li>
);
}

render() {
const legendEls = this.props.data.map((d) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eventually it'd be cool to make the legend expandable in the case that there are many series, then it wouldn't dominate most of the real estate in a dashboard chart

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

return this.legendItem(d.key);
});
return (
<ul className="list-unstyled list-inline">
{legendEls}
</ul>
);
}
}

Legend.propTypes = propTypes;
Loading