Skip to content

Commit

Permalink
feat(playground): Chart.js charting library support
Browse files Browse the repository at this point in the history
  • Loading branch information
paveltiunov committed Jun 30, 2019
1 parent 9638a1a commit 40bb5d0
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 99 deletions.
67 changes: 62 additions & 5 deletions packages/cubejs-playground/src/ChartContainer.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import React from 'react';
import { Card, Button } from 'antd';
import {
Card, Button, Menu, Dropdown, Icon
} from 'antd';
import { getParameters } from 'codesandbox-import-utils/lib/api/define';
import { fetch } from 'whatwg-fetch';
import { map } from 'ramda';
import { Redirect } from 'react-router-dom';
import { QueryRenderer } from '@cubejs-client/react';
import sqlFormatter from "sql-formatter";
import PropTypes from 'prop-types';
import PrismCode from './PrismCode';
import { playgroundAction } from './events';

class ChartContainer extends React.Component {
constructor(props) {
super(props);
this.state = { showCode: false };
this.state = {
showCode: false
};
}

async componentDidMount() {
const {
codeSandboxSource,
dependencies,
sandboxId
dependencies
} = this.props;
const codeSandboxRes = await fetch("https://codesandbox.io/api/v1/sandboxes/define?json=1", {
method: "POST",
Expand Down Expand Up @@ -69,7 +73,10 @@ class ChartContainer extends React.Component {
dashboardSource,
hideActions,
query,
cubejsApi
cubejsApi,
chartLibrary,
setChartLibrary,
chartLibraries
} = this.props;

if (redirectToDashboard) {
Expand All @@ -78,6 +85,21 @@ class ChartContainer extends React.Component {

const parameters = getParameters(this.codeSandboxDefinition(codeSandboxSource, dependencies));

console.log(chartLibraries);

const chartLibrariesMenu = (
<Menu onClick={(e) => setChartLibrary(e.key)}>
{
chartLibraries.map(library => (
<Menu.Item key={library.value}>
{library.title}
</Menu.Item>
))
}
</Menu>
);

const currentLibraryItem = chartLibraries.find(m => m.value === chartLibrary);
const extra = (
<form action="https://codesandbox.io/api/v1/sandboxes/define" method="POST" target="_blank">
<input type="hidden" name="parameters" value={parameters} />
Expand All @@ -97,6 +119,12 @@ class ChartContainer extends React.Component {
{addingToDashboard ? 'Creating app and installing modules...' : 'Add to Dashboard'}
</Button>
)}
<Dropdown overlay={chartLibrariesMenu}>
<Button size="small">
{currentLibraryItem && currentLibraryItem.title}
<Icon type="down" />
</Button>
</Dropdown>
<Button
onClick={() => {
playgroundAction('Show Code');
Expand Down Expand Up @@ -159,4 +187,33 @@ class ChartContainer extends React.Component {
}
}

ChartContainer.propTypes = {
resultSet: PropTypes.object,
error: PropTypes.object,
codeExample: PropTypes.string,
render: PropTypes.func.isRequired,
title: PropTypes.string,
codeSandboxSource: PropTypes.string,
dependencies: PropTypes.array.isRequired,
dashboardSource: PropTypes.string,
hideActions: PropTypes.array,
query: PropTypes.object,
cubejsApi: PropTypes.object,
chartLibrary: PropTypes.string.isRequired,
setChartLibrary: PropTypes.func.isRequired,
chartLibraries: PropTypes.array.isRequired
};

ChartContainer.defaultProps = {
query: {},
cubejsApi: null,
hideActions: null,
dashboardSource: null,
codeSandboxSource: null,
title: null,
codeExample: null,
error: null,
resultSet: null
};

export default ChartContainer;
49 changes: 32 additions & 17 deletions packages/cubejs-playground/src/ChartRenderer.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import React from 'react';
import React, { useState } from 'react';
import SourceRender from 'react-source-render';
import presetEnv from '@babel/preset-env';
import presetReact from '@babel/preset-react';
import cubejs from '@cubejs-client/core';
import * as cubejsReact from '@cubejs-client/react';
// eslint-disable-next-line import/no-duplicates
import * as antd from 'antd';
// eslint-disable-next-line import/no-duplicates
import { Alert } from 'antd';

import ChartContainer from './ChartContainer';
import * as bizChartLibrary from './libraries/bizChart';
import * as chartjsLibrary from './libraries/chartjs';
import * as tablesLibrary from './libraries/tables';

export const libraryToTemplate = {
bizcharts: bizChartLibrary,
chartjs: chartjsLibrary
bizcharts: { library: bizChartLibrary, title: 'Bizcharts' },
chartjs: { library: chartjsLibrary, title: 'Chart.js' }
};

export const babelConfig = {
Expand All @@ -32,7 +35,7 @@ const sourceCodeTemplate = (props) => {
import cubejs from '@cubejs-client/core';
import { QueryRenderer } from '@cubejs-client/react';
import { Spin } from 'antd';
${libraryToTemplate[chartLibrary].sourceCodeTemplate({ ...props, renderFnName })}
${chartLibrary.sourceCodeTemplate({ ...props, renderFnName })}
const API_URL = "${apiUrl}"; // change to your actual endpoint
Expand All @@ -48,7 +51,7 @@ const renderChart = (Component) => ({ resultSet, error }) => (
)
const ChartRenderer = () => <QueryRenderer
query={${(typeof query === 'object' ? JSON.stringify(query, null, 2) : query).split('\n').map((l, i) => i > 0 ? ` ${l}` : l).join('\n')}}
query={${(typeof query === 'object' ? JSON.stringify(query, null, 2) : query).split('\n').map((l, i) => (i > 0 ? ` ${l}` : l)).join('\n')}}
cubejsApi={cubejsApi}
render={renderChart(${renderFnName})}
/>;
Expand All @@ -72,24 +75,31 @@ export const ChartRenderer = (props) => {
resultSet,
error,
sqlQuery,
chartLibrary,
dashboardSource,
cubejsApi
cubejsApi,
chartType
} = props;

const [chartLibrary, setChartLibrary] = useState('bizcharts');

sourceCodeFn = sourceCodeFn || sourceCodeTemplate;
const source = sourceCodeFn(props);
const selectedChartLibrary = ['table', 'number'].indexOf(chartType) !== -1
? tablesLibrary : libraryToTemplate[chartLibrary].library;
const source = sourceCodeFn({
...props,
chartLibrary: selectedChartLibrary
});
const dependencies = {
'@cubejs-client/core': cubejs,
'@cubejs-client/react': cubejsReact,
antd,
react: React,
...libraryToTemplate[chartLibrary].imports
...selectedChartLibrary.imports
};
return (
<SourceRender
babelConfig={babelConfig}
onError={error => console.log(error)}
onSuccess={(error, { markup }) => console.log('HTML', markup)}
onError={e => console.log(e)}
resolver={importName => dependencies[importName]}
source={source}
>
Expand All @@ -105,15 +115,20 @@ export const ChartRenderer = (props) => {
codeSandboxSource={withDomRender(source)}
dependencies={dependencies}
dashboardSource={dashboardSource}
chartLibrary={chartLibrary}
setChartLibrary={setChartLibrary}
chartLibraries={Object.keys(libraryToTemplate).map(k => ({ value: k, title: libraryToTemplate[k].title }))}
cubejsApi={cubejsApi}
render={() => jsCompilingError ? (<Alert
message="Error occurred while compiling JS"
description={<pre>{jsCompilingError.toString()}</pre>}
type="error"
/>) : element}
render={() => (jsCompilingError ? (
<Alert
message="Error occurred while compiling JS"
description={<pre>{jsCompilingError.toString()}</pre>}
type="error"
/>
) : element)}
/>
)}
</SourceRender.Consumer>
</SourceRender>
);
};
};
1 change: 0 additions & 1 deletion packages/cubejs-playground/src/PlaygroundQueryBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,6 @@ const PlaygroundQueryBuilder = ({
chartType={chartType}
cubejsApi={cubejsApi}
dashboardSource={dashboardSource}
chartLibrary="bizcharts"
/>
) : <h2 style={{ textAlign: 'center' }}>Choose a measure or dimension to get started</h2>}
</Col>
Expand Down
28 changes: 1 addition & 27 deletions packages/cubejs-playground/src/libraries/bizChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,14 @@ const chartTypeToTemplate = {
<Tooltip crosshairs={{type : 'y'}} />
<Geom type="areaStack" position={\`x*measure\`} size={2} color="color" />
</Chart>`,
barStacked: `
<Chart height={400} data={resultSet.rawData()} forceFit>
<Legend />
<Axis name="Stories.time" label={{ formatter: val => moment(val).format("MMM 'YY") }} />
<Axis name="Stories.count" />
<Tooltip />
<Geom type='intervalStack' position="Stories.time*Stories.count" color="Stories.category" />
</Chart>`,
pie: `
<Chart height={400} data={resultSet.chartPivot()} forceFit>
<Coord type='theta' radius={0.75} />
{resultSet.seriesNames().map(s => (<Axis name={s.key} />))}
<Legend position='right' />
<Tooltip />
{resultSet.seriesNames().map(s => (<Geom type="intervalStack" position={s.key} color="category" />))}
</Chart>`,
number: `
<Row type="flex" justify="center" align="middle" style={{ height: '100%' }}>
<Col>
{resultSet
.seriesNames()
.map(s => (
<Statistic value={resultSet.totalRow()[s.key]} />
))}
</Col>
</Row>
`,
table: `
<Table
pagination={false}
columns={resultSet.tableColumns().map(c => ({ ...c, dataIndex: c.key }))}
dataSource={resultSet.tablePivot()}
/>
`
</Chart>`
};


Expand Down
62 changes: 13 additions & 49 deletions packages/cubejs-playground/src/libraries/chartjs.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,10 @@
import React from 'react';
import * as reactChartjs from 'react-chartjs-2'
import * as reactChartjs from 'react-chartjs-2';
import moment from 'moment';

const chartTypeToTemplate = {
line: `
const data = {
labels: resultSet.categories().map(c => new Date(c.category)),
datasets: resultSet.series().map((s, index) => (
{
label: s.title,
data: s.series.map(r => r.value),
borderColor: COLORS_SERIES[index],
fill: false
}
)),
};
const options = {
scales: { xAxes: [{ type: 'time', time: { unit: 'month' }}] }
};
return <Line data={data} options={options} />;`,
categoryFilter: `
const data = {
labels: resultSet.categories().map(c => new Date(c.category)),
datasets: resultSet.series().map((s, index) => (
{
label: s.title,
data: s.series.map(r => r.value),
borderColor: COLORS_SERIES[index],
fill: false
}
)),
};
const options = {
scales: { xAxes: [{ type: 'time', time: { unit: 'month' }}] }
};
return <Line data={data} options={options} />;`,
lineMulti: `
const data = {
labels: resultSet.categories().map(c => new Date(c.category)),
labels: resultSet.categories().map(c => c.category),
datasets: resultSet.series().map((s, index) => (
{
label: s.title,
Expand All @@ -47,13 +14,11 @@ const chartTypeToTemplate = {
}
)),
};
const options = {
scales: { xAxes: [{ type: 'time', time: { unit: 'month' }}] }
};
const options = {};
return <Line data={data} options={options} />;`,
bar: `
const data = {
labels: resultSet.categories().map(c => new Date(c.category)),
labels: resultSet.categories().map(c => c.category),
datasets: resultSet.series().map((s, index) => (
{
label: s.title,
Expand All @@ -64,25 +29,24 @@ const chartTypeToTemplate = {
)),
};
const options = {
scales: { xAxes: [{ type: 'time', time: { unit: 'month' }}] }
scales: { xAxes: [{ stacked: true }] }
};
return <Bar data={data} options={options} />;`,
barStacked: `
area: `
const data = {
labels: resultSet.categories().map(c => new Date(c.category)),
labels: resultSet.categories().map(c => c.category),
datasets: resultSet.series().map((s, index) => (
{
label: s.title,
data: s.series.map(r => r.value),
backgroundColor: COLORS_SERIES[index],
fill: false
backgroundColor: COLORS_SERIES[index]
}
)),
};
const options = {
scales: { xAxes: [{ type: 'time', time: { unit: 'month' }}] }
scales: { yAxes: [{ stacked: true }] }
};
return <Bar data={data} options={options} />;`,
return <Line data={data} options={options} />;`,
pie: `
const data = {
labels: resultSet.categories().map(c => c.category),
Expand All @@ -100,17 +64,17 @@ const chartTypeToTemplate = {
};


export const sourceCodeTemplate = (chartType, query) => (
export const sourceCodeTemplate = ({ chartType, renderFnName }) => (
`import { Line, Bar, Pie } from 'react-chartjs-2';
import moment from 'moment';
const COLORS_SERIES = ['#FF6492', '#141446', '#7A77FF'];
const renderChart = (resultSet) => {${chartTypeToTemplate[chartType]}
const ${renderFnName} = ({ resultSet }) => {${chartTypeToTemplate[chartType]}
};`
);

export const imports = {
'react-chartjs-2': reactChartjs,
moment
};
};
Loading

0 comments on commit 40bb5d0

Please sign in to comment.