diff --git a/src/App.css b/src/DatasetteVega.css similarity index 100% rename from src/App.css rename to src/DatasetteVega.css diff --git a/src/App.js b/src/DatasetteVega.js similarity index 54% rename from src/App.js rename to src/DatasetteVega.js index 1f9dd53..9fb3946 100644 --- a/src/App.js +++ b/src/DatasetteVega.js @@ -1,12 +1,12 @@ import React, { Component } from 'react'; import vegaEmbed from 'vega-embed'; -import './App.css'; +import './DatasetteVega.css'; const serialize = obj => Object.keys(obj).map( key => obj[key] ? `g.${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}` : '' ).join('&'); -const unserialize = s => { +const unserialize = (s, prefix) => { if (s && s[0] === '#') { s = s.slice(1); } @@ -14,19 +14,20 @@ const unserialize = s => { return {}; } var obj = {}; - s.split('&').forEach(bit => { + s.split('&').filter(bit => bit.slice(0, prefix.length + 1) === `{prefix).`).forEach(bit => { let pair = bit.split('='); obj[decodeURIComponent(pair[0]).replace(/^g\./, '')] = decodeURIComponent(pair[1]); }); return obj; }; -class App extends Component { +class DatasetteVega extends Component { state = { + columns: this.props.columns || [], mark: "bar", - x_column: this.props.columns[0], + x_column: null, x_type: "ordinal", - y_column: this.props.columns[1], + y_column: null, y_type: "quantitative", color_column: "", size_column: "" @@ -53,16 +54,61 @@ class App extends Component { this.renderGraph(); }); } + jsonUrl() { + let url = this.props.base_url; + if (/\?/.exec(url)) { + url += '&'; + } else { + url += '?'; + } + url += '_shape=array'; + return url; + } componentDidMount() { window.onpopstate = this.onPopStateChange.bind(this); - this.renderGraph(); + // Load the columns + let url = this.jsonUrl(); + if (this.props.columns) { + return; + } + fetch(url).then(r => r.json()).then(data => { + if (data.length > 1) { + // Set columns to first item's keys + const columns = Object.keys(data[0]); + console.log(columns); + this.setState({ + columns: columns, + x_column: columns[0], + y_column: columns[1], + }, this.renderGraph); + } + }); + } + serializeState() { + return serialize((({ + mark, + x_column, + x_type, + y_column, + y_type, + color_column, + size_column + }) => ({ + mark, + x_column, + x_type, + y_column, + y_type, + color_column, + size_column + }))(this.state)); } onPopStateChange(ev) { window.lastPopEv = ev; - let expected = '#' + serialize(this.state); + const expected = '#' + this.serializeState(); if (expected !== document.location.hash) { this.setState( - unserialize(document.location.hash), this.renderGraph.bind(this) + unserialize(document.location.hash, 'g'), this.renderGraph.bind(this) ); } } @@ -83,14 +129,15 @@ class App extends Component { } const spec = { data: { - url: this.props.json_url + url: this.jsonUrl() }, mark: this.state.mark, encoding: encoding } - if (spec.mark) { + console.log('spec= ', spec); + if (spec.mark && spec.encoding.x.field && spec.encoding.y.field) { vegaEmbed("#vis", spec, {theme: 'quartz', tooltip: true}); - document.location.hash = '#' + serialize(this.state); + document.location.hash = '#' + this.serializeState() this.setState({spec: spec}); } } @@ -105,52 +152,45 @@ class App extends Component { } render() { const onChangeSelect = this.onChangeSelect.bind(this); - if (!this.state.mark) { - return null; - } + const columns = this.state.columns; + console.log(columns); return ( -
+ : null ); } } -export default App; +export default DatasetteVega; diff --git a/src/App.test.js b/src/DatasetteVega.test.js similarity index 58% rename from src/App.test.js rename to src/DatasetteVega.test.js index a754b20..d94e3ee 100644 --- a/src/App.test.js +++ b/src/DatasetteVega.test.js @@ -1,9 +1,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import App from './App'; +import DatasetteVega from './DatasetteVega'; it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(