mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
add graphql2chartjs to community tools (#1669)
This commit is contained in:
parent
75ec309e8a
commit
a9cd694eff
3
community/tools/graphql2chartjs/.gitignore
vendored
Normal file
3
community/tools/graphql2chartjs/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
lib
|
||||
bundle
|
3
community/tools/graphql2chartjs/.npmignore
Normal file
3
community/tools/graphql2chartjs/.npmignore
Normal file
@ -0,0 +1,3 @@
|
||||
src
|
||||
example
|
||||
bundle
|
229
community/tools/graphql2chartjs/README.md
Normal file
229
community/tools/graphql2chartjs/README.md
Normal file
@ -0,0 +1,229 @@
|
||||
# graphql2chartjs - Instant realtime charts using GraphQL
|
||||
|
||||
`graphql2chartjs` reshapes your GraphQL data as per the [ChartJS](https://chartjs.org) API. This makes it easy to query a GraphQL API and render the output as a ChartJS chart.
|
||||
|
||||
For example, if you're using Postgres and [Hasura](https://hasura.io), this is what using `graphql2chartjs` looks like:
|
||||
|
||||
![graphql2chartjs](https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/graphql2chartjs-explained.png)
|
||||
|
||||
|
||||
|
||||
## Demos & sandbox
|
||||
We've set up a GraphQL server with continuously changing data, so that you can try graphql2chartjs out easily.
|
||||
|
||||
|
||||
|[View live charts](https://graphql2chartjs-examples.herokuapp.com) | [Edit in sandbox](https://codesandbox.io/s/p2wpj1o8pj) | [Open GraphiQL](https://g2c-examples-graphiql.herokuapp.com/) |
|
||||
|---|---|---|
|
||||
|
||||
![realtime chart with live data](https://storage.googleapis.com/graphql-engine-cdn.hasura.io/assets/graphql2chartjs/live-chart.gif)
|
||||
|
||||
The demo above cover the following types of charts: [basic](https://graphql2chartjs-examples.herokuapp.com/#bar), [multiple datasets](https://graphql2chartjs-examples.herokuapp.com/#multi-bar), [mixed chart-types](https://graphql2chartjs-examples.herokuapp.com/#mixed), [realtime chart with live data](https://graphql2chartjs-examples.herokuapp.com/#live-chart), [realtime time-series](https://graphql2chartjs-examples.herokuapp.com/#timeseries-chart)
|
||||
|
||||
## Usage with Hasura
|
||||
Hasura gives you an instant realtime GraphQL API on an existing Postgres database. You can create views to capture analytics and aggregations on your database and instantly turn them into charts.
|
||||
|
||||
Watch this video below to see a demo/tutorial of using Hasura with an existing Postgres database, creating views and building charts.
|
||||
|
||||
<div style="text-align:center">
|
||||
<a href="https://www.youtube.com/watch?v=153iv1-qFuc&feature=youtu.be" target="_blank">
|
||||
<img src="https://storage.googleapis.com/graphql-engine-cdn.hasura.io/assets/graphql2chartjs/g2c-youtube-embed.png" width="1000px" alt="youtube video demo">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
## Example usage with react, apollo and react-chartjs-2
|
||||
|
||||
```javascript
|
||||
import {Query} from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
import graphql2chartjs from 'graphql2chartjs';
|
||||
import {Bar} from 'react-chartjs-2';
|
||||
|
||||
const Chart = () => (
|
||||
<Query
|
||||
query={gql`
|
||||
query {
|
||||
Articles: articleStats {
|
||||
label: title
|
||||
data: num_likes
|
||||
}
|
||||
}`}
|
||||
}>
|
||||
{({data} => {
|
||||
if (data) {
|
||||
const g2c = new graphql2chartjs(data, 'bar');
|
||||
return (<Bar data={g2c.data} />);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</Query>
|
||||
);
|
||||
```
|
||||
|
||||
## Mapping GraphQL queries to ChartJS charts
|
||||
|
||||
Different types of charts need different structures in their datasets.
|
||||
|
||||
For example a bar chart dataset needs labels and data associated for each label; the ChartJS API refers to this as `label` and `data`. Once you alias fields in your graphql query to `label` and `data`, and pass the response through `graphql2chartjs`, your dataset is ready to be used by bar chart in chartjs.
|
||||
|
||||
### Bar / Line / Doughnut / Pie / Radar / Polar Area / Area
|
||||
|
||||
Charts of this type need 2 data inputs, `label` and `data`.
|
||||
```graphql
|
||||
query {
|
||||
ArticleLikes : articles {
|
||||
label: title
|
||||
data: likes
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Scatter / Bubble
|
||||
|
||||
Charts of this type need 2 data inputs: `data_x`, `data_y` (and `data_r` for bubble).
|
||||
```graphql
|
||||
query {
|
||||
ArticleLikesVsComments : articles {
|
||||
data_x: num_likes
|
||||
data_y: num_comments
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Time series (line / bar)
|
||||
|
||||
Charts of this type need 2 data inputs, `data_x` or `data_t` and `data_y`. Note that there is no `label`.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
StockPrices : stockprice {
|
||||
data_t: created
|
||||
data_y: price
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## graphql2chartjs usage
|
||||
|
||||
graphql2chartjs works in 3 steps:
|
||||
|
||||
1. Initialise graphql2chartjs: `const g2c = new graphql2chartjs()`
|
||||
2. Add data from your graphql response: `g2c.add(graphqlResponse.data, 'line')`
|
||||
3. Set your chart data to the data properly of the graphql2chartjs instance: `g2c.data`
|
||||
|
||||
### Step 1: Initialiase with data: `new graphql2chartjs()`
|
||||
|
||||
#### Option 1: Initialise with data and chart type
|
||||
|
||||
**`graphql2chartjs(data, chartType)`**
|
||||
|
||||
```javascript
|
||||
const g2c = new graphql2chartjs(data, 'bar');
|
||||
```
|
||||
|
||||
- `data`: This is your GraphQL response. This data should have fields `label`, `data` etc. as per the GraphQL querying described above.
|
||||
- `chartType`: This is a string that represents valid values of what your chart type is. Valid values include `'line'`, `'bar'`, `'radar'`, `'doughnut'`, `'pie'`, `'polarArea'`, `'bubble'`, `'scatter'`.
|
||||
|
||||
**Notes:**
|
||||
- This is the simplest way of using `graphql2chartjs`
|
||||
- If you have multiple datasets, all of the datasets will be rendered automatically as the same type of chart
|
||||
- To customise the UI options of the rendered chart like colors or to create a mixed type chart (one dataset is rendered as a line chart, another as a bar chart) use the next initialisation method instead of this one.
|
||||
|
||||
|
||||
#### Option 2: Initialise with data and a transform function
|
||||
|
||||
**`graphql2chartjs(data, transform)`**
|
||||
|
||||
The transformation function can add chartjs dataset props or even modify the record data:
|
||||
|
||||
```javascript
|
||||
const g2c = new graphql2chartjs(data, (datasetName, dataPoint) => {
|
||||
return {
|
||||
chartType: 'bar',
|
||||
backgroundColor: 'yellow'
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
- `transform(datasetName, dataPoint)`: This function defined by you can take the name of the dataset and the data record that comes from the GraphQL response and returns an object that can should have the `chartType` key and optionally other keys that specify other dataset properties.
|
||||
- The object returned by this function should look like the following:
|
||||
```javascript
|
||||
{
|
||||
chartType: 'line', // Or 'line', 'bar', 'radar', 'doughnut', 'pie', 'polarArea', 'bubble', 'scatter'
|
||||
<other keys as per the dataset properties per chart. Refer to the link below>
|
||||
}
|
||||
```
|
||||
- `chartType`: This should be a string value, one of: `'line'`, `'bar'`, `'radar'`, `'doughnut'`, `'pie'`, `'polarArea'`, `'bubble'`, `'scatter'`
|
||||
- Other keys in this object should be dataset properties. These properties are slightly different for different chart types.
|
||||
- Line chart: https://www.chartjs.org/docs/latest/charts/line.html#dataset-properties
|
||||
- Bar chart: https://www.chartjs.org/docs/latest/charts/bar.html#dataset-properties
|
||||
- Radar chart: https://www.chartjs.org/docs/latest/charts/radar.html#dataset-properties
|
||||
- Doughnut & Pie: https://www.chartjs.org/docs/latest/charts/doughnut.html#dataset-properties
|
||||
- Polar: https://www.chartjs.org/docs/latest/charts/polar.html#dataset-properties
|
||||
- Bubble: https://www.chartjs.org/docs/latest/charts/bubble.html#dataset-properties
|
||||
- Scatter: https://www.chartjs.org/docs/latest/charts/scatter.html#dataset-properties
|
||||
|
||||
|
||||
### Step 2: Now create your cchart with data - `g2c.data`
|
||||
|
||||
`g2c.data` gives you access to the latest ChartJS data that can be passed to your chart.
|
||||
|
||||
1. Javascript
|
||||
```javascript
|
||||
var myChart = new Chart(ctx, { data: g2c.data });
|
||||
```
|
||||
|
||||
2. react-chartjs-2
|
||||
```javascript
|
||||
<Bar data={g2c.data} />
|
||||
```
|
||||
|
||||
### Step 3: (optional) Incrementally add data for your chart
|
||||
|
||||
**`g2c.add()`**
|
||||
|
||||
Once you've initialised a `graphql2chartjs` object, you can use the `add` function to add data for the first time or incrementally:
|
||||
|
||||
```javascript
|
||||
await data = runQuery(..);
|
||||
|
||||
// Add for a chart type
|
||||
g2c.add(data, 'line');
|
||||
|
||||
// Add with a transformation function to change UI props for the new data added or udpated
|
||||
g2c.add(data, (datasetName, dataPoint) => {
|
||||
chartType: 'line',
|
||||
pointBackgroundColor: 'yellow'
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### Via npm
|
||||
|
||||
```
|
||||
npm install --save graphql2chartjs
|
||||
```
|
||||
|
||||
### Use in a script tag
|
||||
|
||||
```html
|
||||
<script src="https://storage.googleapis.com/graphql-engine-cdn.hasura.io/tools/graphql2chartjs/index.js" type="application/javascript"></script>
|
||||
```
|
||||
|
||||
## Reforming the data
|
||||
|
||||
### `reform()`
|
||||
|
||||
You can reform the existing data in your `graphql2chartjs` instance using the reform function that takes a reformer function as an argument. This reformer function is run over every datapoint in every dataset. For instance, to scale the x and y coordinates, you would do something like:
|
||||
|
||||
```
|
||||
g2c.reform((datasetName, dataPoint) => {
|
||||
// scale the x, y coordinates
|
||||
return {
|
||||
data_x: scalingFactor(dataPoint.data_x),
|
||||
data_y: scalingFactor(dataPoint.data_y)
|
||||
}
|
||||
})
|
||||
```
|
16
community/tools/graphql2chartjs/babel.config.js
Normal file
16
community/tools/graphql2chartjs/babel.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
const presets = [
|
||||
[
|
||||
"@babel/env",
|
||||
{
|
||||
targets: {
|
||||
edge: "17",
|
||||
firefox: "60",
|
||||
chrome: "67",
|
||||
safari: "11.1",
|
||||
},
|
||||
useBuiltIns: "usage",
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
module.exports = { presets };
|
15
community/tools/graphql2chartjs/example/README.md
Normal file
15
community/tools/graphql2chartjs/example/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Example
|
||||
|
||||
This example app is live at https://graphql2chartjs-examples.herokuapp.com
|
||||
|
||||
## Client (React app)
|
||||
|
||||
The app directory has the React app that is already set up with a backend.
|
||||
|
||||
## Migrations (Hasura migrations)
|
||||
|
||||
The hasura directory contains the migrations for the Hasura GraphQL Engine backend that is setup at https://graphql2chartjs.hasura.app and has a GraphQL endpoint https://graphqlchartjs.hasura.app/v1alpha1/graphql
|
||||
|
||||
## Scripts
|
||||
|
||||
The scripts directory contains the scripts that are run to populate the backend with sample data.
|
2
community/tools/graphql2chartjs/example/app/.gitignore
vendored
Normal file
2
community/tools/graphql2chartjs/example/app/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
build
|
6
community/tools/graphql2chartjs/example/app/README.md
Executable file
6
community/tools/graphql2chartjs/example/app/README.md
Executable file
@ -0,0 +1,6 @@
|
||||
To run this example:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
39
community/tools/graphql2chartjs/example/app/package.json
Normal file
39
community/tools/graphql2chartjs/example/app/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "graphql2chartjs-examples",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"apollo-boost": "^0.1.28",
|
||||
"apollo-link-ws": "^1.0.14",
|
||||
"bootstrap": "^4.3.1",
|
||||
"chart.js": "^2.7.3",
|
||||
"graphql": "^14.1.1",
|
||||
"graphql2chartjs": "^0.2.1",
|
||||
"react": "^16.8.3",
|
||||
"react-apollo": "^2.4.1",
|
||||
"react-bootstrap": "^1.0.0-beta.5",
|
||||
"react-chartjs-2": "^2.7.4",
|
||||
"react-dom": "^16.8.3",
|
||||
"react-scripts": "2.1.5",
|
||||
"react-syntax-highlighter": "^10.1.3",
|
||||
"subscriptions-transport-ws": "^0.9.15"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@playlyfe/gql": "^2.6.1"
|
||||
}
|
||||
}
|
BIN
community/tools/graphql2chartjs/example/app/public/favicon.ico
Executable file
BIN
community/tools/graphql2chartjs/example/app/public/favicon.ico
Executable file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
48
community/tools/graphql2chartjs/example/app/public/index.html
Executable file
48
community/tools/graphql2chartjs/example/app/public/index.html
Executable file
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
|
||||
integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>graphql2chartjs examples</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
15
community/tools/graphql2chartjs/example/app/public/manifest.json
Executable file
15
community/tools/graphql2chartjs/example/app/public/manifest.json
Executable file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
65
community/tools/graphql2chartjs/example/app/src/App.css
Executable file
65
community/tools/graphql2chartjs/example/app/src/App.css
Executable file
@ -0,0 +1,65 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.half_screen {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chartWrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.loadingIndicator {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@media(max-width: 767px) {
|
||||
.half_screen {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
.chartWrapper {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.fiftypercent {
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
}
|
28
community/tools/graphql2chartjs/example/app/src/App.js
Executable file
28
community/tools/graphql2chartjs/example/app/src/App.js
Executable file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import NavBar from './Navbar';
|
||||
|
||||
import {
|
||||
BasicBarChart,
|
||||
StyledBarChart,
|
||||
MultiDatasetBarChart,
|
||||
MixedLineBarChart,
|
||||
LiveChart,
|
||||
RealtimeTimeseriesChart
|
||||
} from './charts';
|
||||
|
||||
const App = () => (
|
||||
<div>
|
||||
<NavBar />
|
||||
<div style={{margin: '10px', paddingTop: '65px'}}>
|
||||
<BasicBarChart/>
|
||||
<StyledBarChart/>
|
||||
<MultiDatasetBarChart />
|
||||
<MixedLineBarChart />
|
||||
<LiveChart />
|
||||
<RealtimeTimeseriesChart />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default App;
|
9
community/tools/graphql2chartjs/example/app/src/App.test.js
Executable file
9
community/tools/graphql2chartjs/example/app/src/App.test.js
Executable file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
33
community/tools/graphql2chartjs/example/app/src/Navbar.js
Normal file
33
community/tools/graphql2chartjs/example/app/src/Navbar.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { Navbar, Nav, NavDropdown, Button } from 'react-bootstrap';
|
||||
|
||||
const Bar = () => {
|
||||
return (
|
||||
<Navbar bg="light" fixed="top">
|
||||
<Navbar.Brand href="/">graphql2chartjs examples</Navbar.Brand>
|
||||
<Navbar.Collapse id="basic-navbar-nav">
|
||||
<Nav>
|
||||
<NavDropdown title="Jump" id="basic-nav-dropdown">
|
||||
<NavDropdown.Item href="#bar">Basic bar chart</NavDropdown.Item>
|
||||
<NavDropdown.Item href="#styled-bar">Styled bar chart</NavDropdown.Item>
|
||||
<NavDropdown.Item href="#multi-bar">Bar (multiple datasets)</NavDropdown.Item>
|
||||
<NavDropdown.Item href="#mixed">Mixed chart (bar and line)</NavDropdown.Item>
|
||||
<NavDropdown.Item href="#live-chart">Live chart</NavDropdown.Item>
|
||||
<NavDropdown.Item href="#timeseries-chart">Time series</NavDropdown.Item>
|
||||
</NavDropdown>
|
||||
</Nav>
|
||||
<Nav>
|
||||
<Nav.Link href="https://github.com/hasura/graphql-engine/tree/master/community/tools/graphql2chartjs">
|
||||
<Button variant="dark" size="sm">GitHub</Button>
|
||||
</Nav.Link>
|
||||
<Nav.Link href="https://codesandbox.io/s/p2wpj1o8pj">
|
||||
<Button variant="link" size="sm">Edit in sandbox</Button>
|
||||
</Nav.Link>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
)
|
||||
}
|
||||
|
||||
export default Bar;
|
||||
|
@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import graphql2chartjs from 'graphql2chartjs';
|
||||
import { Query } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const query = `
|
||||
query {
|
||||
ArticleLikes: article_stats {
|
||||
id
|
||||
label: title
|
||||
data: num_likes
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Chart component
|
||||
const Chart = ({ query }) => (
|
||||
<Query
|
||||
query={gql`${query}`}
|
||||
>
|
||||
{
|
||||
({data, error, loading}) => {
|
||||
if (loading || error) {
|
||||
return <div className="loadingIndicator">Please wait </div>;
|
||||
}
|
||||
// create graphql2chartjs instance
|
||||
const g2c = new graphql2chartjs();
|
||||
// add graphql data to graphql2chartjs instance
|
||||
g2c.add(data, 'bar');
|
||||
// render chart with g2c data :)
|
||||
return (
|
||||
<Bar data={g2c.data} />
|
||||
)
|
||||
}
|
||||
}
|
||||
</Query>
|
||||
)
|
||||
|
||||
/****************************************UTILS*****************************************/
|
||||
|
||||
const HighlightedQuery = ({ query }) => (
|
||||
<SyntaxHighlighter
|
||||
language="graphql"
|
||||
style={docco}
|
||||
>
|
||||
{query}
|
||||
</SyntaxHighlighter>
|
||||
)
|
||||
|
||||
const BasicBarChart = ({ path }) => {
|
||||
return (
|
||||
<div style={{margin: '10px', paddingTop: '65px'}}>
|
||||
<div key="bar">
|
||||
<div style={{marginBottom: '20px'}} id="bar">
|
||||
<h2 style={{margin: '10px', textAlign: 'center'}}>Basic bar chart</h2>
|
||||
<div className="chartWrapper">
|
||||
<div className="half_screen">
|
||||
<HighlightedQuery query={query} />
|
||||
</div>
|
||||
<div className="half_screen">
|
||||
<Chart query={query}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://github.com/hasura/graphql-engine/tree/master/community/tools/graphql2chartjs/example/app/src/charts/BasicBarChart.js">View source </a>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { BasicBarChart };
|
@ -0,0 +1,108 @@
|
||||
import React from 'react';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import graphql2chartjs from 'graphql2chartjs';
|
||||
import { Subscription } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const subscription = `
|
||||
subscription {
|
||||
stock_price_for_Amazon: stocks (
|
||||
order_by: {
|
||||
created: desc
|
||||
}
|
||||
where: {
|
||||
ticker: {
|
||||
_eq: "AMZN"
|
||||
}
|
||||
}
|
||||
limit: 100
|
||||
) {
|
||||
data_t: created
|
||||
data_y: price
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Chart component
|
||||
const Chart = () => (
|
||||
<Subscription
|
||||
subscription={gql`${subscription}`}
|
||||
>
|
||||
{
|
||||
({data, error, loading}) => {
|
||||
if (loading || error) {
|
||||
console.error(error);
|
||||
return <div className="loadingIndicator">Please wait </div>;
|
||||
}
|
||||
// create graphql2chartjs instance
|
||||
const g2c = new graphql2chartjs();
|
||||
// add graphql data to graphql2chartjs instance while adding different chart types and properties
|
||||
g2c.add(data, (dataSetName, dataPoint) => {
|
||||
return {
|
||||
...dataPoint,
|
||||
chartType: 'line',
|
||||
borderColor: '#333538',
|
||||
pointBackgroundColor: '#333538',
|
||||
backgroundColor: '#333538',
|
||||
fill: false
|
||||
}
|
||||
});
|
||||
// render chart with g2c data :)
|
||||
return (
|
||||
<Line
|
||||
data={g2c.data}
|
||||
options={{
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time'
|
||||
}]
|
||||
},
|
||||
animation: {
|
||||
duration: 0, // general animation time
|
||||
},
|
||||
bezierCurve : false
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
</Subscription>
|
||||
)
|
||||
|
||||
/****************************************UTILS*****************************************/
|
||||
|
||||
const HighlightedSubscription = () => (
|
||||
<SyntaxHighlighter
|
||||
language="graphql"
|
||||
style={docco}
|
||||
>
|
||||
{subscription}
|
||||
</SyntaxHighlighter>
|
||||
)
|
||||
|
||||
const LiveChart = ({ path }) => {
|
||||
return (
|
||||
<div style={{margin: '10px', paddingTop: '65px'}}>
|
||||
<div key="live-chart">
|
||||
<div style={{marginBottom: '20px'}} id="live-chart">
|
||||
<h2 style={{margin: '10px', textAlign: 'center'}}>Live chart (with mock data)</h2>
|
||||
<div className="chartWrapper">
|
||||
<div className="half_screen">
|
||||
<HighlightedSubscription subscription={subscription} />
|
||||
</div>
|
||||
<div className="half_screen">
|
||||
<Chart />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://github.com/hasura/graphql-engine/tree/master/community/tools/graphql2chartjs/example/app/src/charts/LiveChart.js">View source </a>
|
||||
<br/>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { LiveChart };
|
@ -0,0 +1,99 @@
|
||||
import React from 'react';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import graphql2chartjs from 'graphql2chartjs';
|
||||
import { Query } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const query = `
|
||||
query {
|
||||
ArticleComments: article_stats {
|
||||
id
|
||||
label: title
|
||||
data: num_comments
|
||||
}
|
||||
ArticleLikes: article_stats {
|
||||
id
|
||||
label: title
|
||||
data: num_likes
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Chart component
|
||||
const Chart = () => (
|
||||
<Query
|
||||
query={gql`${query}`}
|
||||
>
|
||||
{
|
||||
({data, error, loading}) => {
|
||||
if (loading || error) {
|
||||
console.error(error);
|
||||
return <div className="loadingIndicator">Please wait </div>;
|
||||
}
|
||||
// create graphql2chartjs instance
|
||||
const g2c = new graphql2chartjs();
|
||||
// add graphql data to graphql2chartjs instance while adding different chart types and properties
|
||||
g2c.add(data, (dataSetName, dataPoint) => {
|
||||
if (dataSetName === 'ArticleLikes') {
|
||||
// return bar chart properties for article likes
|
||||
return {
|
||||
...dataPoint,
|
||||
chartType: 'bar',
|
||||
backgroundColor: '#44c0c1',
|
||||
}
|
||||
}
|
||||
// return line chart properties for article comments
|
||||
return {
|
||||
...dataPoint,
|
||||
chartType: 'line',
|
||||
borderColor: '#ffce49',
|
||||
pointBackgroundColor: '#ffce49',
|
||||
backgroundColor: '#ffce49',
|
||||
fill: false
|
||||
}
|
||||
});
|
||||
// render chart with g2c data :)
|
||||
return (
|
||||
<Bar data={g2c.data} />
|
||||
)
|
||||
}
|
||||
}
|
||||
</Query>
|
||||
)
|
||||
|
||||
/****************************************UTILS*****************************************/
|
||||
|
||||
const HighlightedQuery = ({ query }) => (
|
||||
<SyntaxHighlighter
|
||||
language="graphql"
|
||||
style={docco}
|
||||
>
|
||||
{query}
|
||||
</SyntaxHighlighter>
|
||||
)
|
||||
|
||||
const MixedLineBarChart = ({ path }) => {
|
||||
return (
|
||||
<div style={{margin: '10px', paddingTop: '65px'}}>
|
||||
<div key="mixed">
|
||||
<div style={{marginBottom: '20px'}} id="mixed">
|
||||
<h2 style={{margin: '10px', textAlign: 'center'}}>Mixed chart (line and bar)</h2>
|
||||
<div className="chartWrapper">
|
||||
<div className="half_screen">
|
||||
<HighlightedQuery query={query} />
|
||||
</div>
|
||||
<div className="half_screen">
|
||||
<Chart />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://github.com/hasura/graphql-engine/tree/master/community/tools/graphql2chartjs/example/app/src/charts/MixedLineBarChart.js">View source </a>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { MixedLineBarChart };
|
@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import graphql2chartjs from 'graphql2chartjs';
|
||||
import { Query } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const query = `
|
||||
query {
|
||||
ArticleComments: article_stats {
|
||||
id
|
||||
label: title
|
||||
data: num_comments
|
||||
}
|
||||
ArticleLikes: article_stats {
|
||||
id
|
||||
label: title
|
||||
data: num_likes
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Chart component
|
||||
const Chart = () => (
|
||||
<Query
|
||||
query={gql`${query}`}
|
||||
>
|
||||
{
|
||||
({data, error, loading}) => {
|
||||
if (loading || error) {
|
||||
console.error(error);
|
||||
return <div className="loadingIndicator">Please wait </div>;
|
||||
}
|
||||
// create graphql2chartjs instance
|
||||
const g2c = new graphql2chartjs();
|
||||
// add graphql data to graphql2chartjs instance while adding the backgroundcolor property
|
||||
g2c.add(data, (dataSetName, dataPoint) => {
|
||||
if (dataSetName === 'ArticleLikes') {
|
||||
return {
|
||||
...dataPoint,
|
||||
chartType: 'bar',
|
||||
backgroundColor: '#44c0c1',
|
||||
}
|
||||
}
|
||||
return {
|
||||
...dataPoint,
|
||||
chartType: 'bar',
|
||||
backgroundColor: '#ffce49',
|
||||
}
|
||||
});
|
||||
// render chart with g2c data :)
|
||||
return (
|
||||
<Bar data={g2c.data} />
|
||||
)
|
||||
}
|
||||
}
|
||||
</Query>
|
||||
)
|
||||
|
||||
/****************************************UTILS*****************************************/
|
||||
|
||||
const HighlightedQuery = ({ query }) => (
|
||||
<SyntaxHighlighter
|
||||
language="graphql"
|
||||
style={docco}
|
||||
>
|
||||
{query}
|
||||
</SyntaxHighlighter>
|
||||
)
|
||||
|
||||
const MultiDatasetBarChart = ({ path }) => {
|
||||
return (
|
||||
<div style={{margin: '10px', paddingTop: '65px'}}>
|
||||
<div key="multi-bar">
|
||||
<div style={{marginBottom: '20px'}} id="multi-bar">
|
||||
<h2 style={{margin: '10px', textAlign: 'center'}}>Bar chart (multiple datasets)</h2>
|
||||
<div className="chartWrapper">
|
||||
<div className="half_screen">
|
||||
<HighlightedQuery query={query} />
|
||||
</div>
|
||||
<div className="half_screen">
|
||||
<Chart />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://github.com/hasura/graphql-engine/tree/master/community/tools/graphql2chartjs/example/app/src/charts/MultiDatasetBarChart.js">View source </a>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { MultiDatasetBarChart };
|
@ -0,0 +1,204 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import graphql2chartjs from 'graphql2chartjs';
|
||||
import { ApolloConsumer } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
// Chart component
|
||||
const Chart = ({client}) => {
|
||||
const [chartJsData, setChartjsData] = useState({});
|
||||
useEffect(
|
||||
() => {
|
||||
// initialize g2c
|
||||
const g2c = new graphql2chartjs();
|
||||
let latestTime = null;
|
||||
// load initial data
|
||||
client.query({
|
||||
query: gql`${firstQuery}`
|
||||
}).then((resp) => {
|
||||
// add the received data to g2c and update state
|
||||
g2c.add(resp.data, () => lineChartOptions);
|
||||
setChartjsData(g2c.data)
|
||||
|
||||
// update the timestamp of the last received entry
|
||||
if (resp.data.StockPriceForAmazon.length) {
|
||||
latestTime = resp.data.StockPriceForAmazon[0].data_t;
|
||||
}
|
||||
|
||||
// subscribe to a notification with newest data in the database
|
||||
client.subscribe({
|
||||
query: gql`${lastEventSubscription}`
|
||||
}).subscribe({
|
||||
next(event) {
|
||||
// if the data is not stale, fetch new data and add to g2c
|
||||
if (event.data.StockPriceForAmazon.length) {
|
||||
if (!latestTime || event.data.StockPriceForAmazon[0].data_t > latestTime) {
|
||||
fetchMore()
|
||||
}
|
||||
}
|
||||
},
|
||||
error(err) {
|
||||
console.error(err);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const fetchMore = () => {
|
||||
client.query({
|
||||
query: gql`${fetchMoreQuery}`,
|
||||
variables: {
|
||||
time: latestTime || "2019-03-12T19:16:45.640128+00:00"
|
||||
}
|
||||
}).then((resp) => {
|
||||
if (resp.data.StockPriceForAmazon.length) {
|
||||
g2c.add(resp.data, () => lineChartOptions);
|
||||
latestTime = resp.data.StockPriceForAmazon[0].data_t;
|
||||
setChartjsData(g2c.data);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<Line
|
||||
data={chartJsData}
|
||||
options={{
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time'
|
||||
}]
|
||||
},
|
||||
animation: {
|
||||
duration: 0, // general animation time
|
||||
},
|
||||
bezierCurve : false
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/****************************************UTILS*****************************************/
|
||||
|
||||
const firstQuery = `
|
||||
query {
|
||||
StockPriceForAmazon: stocks (
|
||||
order_by: {
|
||||
created: desc
|
||||
}
|
||||
where: {
|
||||
ticker: {
|
||||
_eq: "AMZN"
|
||||
}
|
||||
}
|
||||
limit: 1000
|
||||
) {
|
||||
data_t: created
|
||||
data_y: price
|
||||
ticker
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const lastEventSubscription = `
|
||||
subscription {
|
||||
StockPriceForAmazon: stocks (
|
||||
order_by: {
|
||||
created: desc
|
||||
}
|
||||
where: {
|
||||
ticker: {
|
||||
_eq: "AMZN"
|
||||
}
|
||||
}
|
||||
limit: 1
|
||||
) {
|
||||
data_t: created
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const fetchMoreQuery = `
|
||||
query ($time: timestamptz) {
|
||||
StockPriceForAmazon: stocks (
|
||||
order_by: {
|
||||
created: desc
|
||||
}
|
||||
where: {
|
||||
_and: [
|
||||
{
|
||||
ticker: {
|
||||
_eq: "AMZN"
|
||||
}
|
||||
},
|
||||
{
|
||||
created: {
|
||||
_gt: $time
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
) {
|
||||
data_t: created
|
||||
data_y: price
|
||||
ticker
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const HighlightedSubscription = () => (
|
||||
<SyntaxHighlighter
|
||||
language="graphql"
|
||||
style={docco}
|
||||
>
|
||||
{
|
||||
`
|
||||
# first query to load last 1000 data points${firstQuery}
|
||||
|
||||
# subscription to detect any change in the database${lastEventSubscription}
|
||||
|
||||
# fetch data newer than the locally existing data${fetchMoreQuery}
|
||||
`
|
||||
}
|
||||
</SyntaxHighlighter>
|
||||
)
|
||||
|
||||
const RealtimeTimeseriesChart = ({ path }) => {
|
||||
return (
|
||||
<div style={{margin: '10px', paddingTop: '65px'}}>
|
||||
<div key="timeseries-chart">
|
||||
<div style={{marginBottom: '20px'}} id="timeseries-chart">
|
||||
<h2 style={{margin: '10px', textAlign: 'center'}}>Timeseries chart (with mock data)</h2>
|
||||
<div className="chartWrapper">
|
||||
<div className="half_screen">
|
||||
<HighlightedSubscription/>
|
||||
</div>
|
||||
<div className="half_screen">
|
||||
<ApolloConsumer>
|
||||
{
|
||||
client => <Chart client={client} />
|
||||
}
|
||||
</ApolloConsumer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://github.com/hasura/graphql-engine/tree/master/community/tools/graphql2chartjs/example/app/src/charts/RealtimeTimeseriesChart.js">View source </a>
|
||||
<br/>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const lineChartOptions = {
|
||||
chartType: 'line',
|
||||
fill: false,
|
||||
borderColor: 'brown',
|
||||
pointBackgroundColor: 'brown',
|
||||
showLine: false
|
||||
}
|
||||
|
||||
export { RealtimeTimeseriesChart };
|
@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import graphql2chartjs from 'graphql2chartjs';
|
||||
import { Query } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const query = `
|
||||
query {
|
||||
ArticleLikes: article_stats {
|
||||
id
|
||||
label: title
|
||||
data: num_likes
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Chart component
|
||||
const Chart = ({ query }) => (
|
||||
<Query
|
||||
query={gql`${query}`}
|
||||
>
|
||||
{
|
||||
({data, error, loading}) => {
|
||||
if (loading || error) {
|
||||
return <div className="loadingIndicator">Please wait </div>;
|
||||
}
|
||||
// create graphql2chartjs instance
|
||||
const g2c = new graphql2chartjs();
|
||||
// add graphql data to graphql2chartjs instance while adding the backgroundcolor property
|
||||
g2c.add(data, (datasetName, dataPoint) => ({
|
||||
...dataPoint,
|
||||
chartType: 'bar',
|
||||
backgroundColor: '#44c0c1',
|
||||
}));
|
||||
// render chart with g2c data :)
|
||||
return (
|
||||
<Bar data={g2c.data} />
|
||||
)
|
||||
}
|
||||
}
|
||||
</Query>
|
||||
)
|
||||
|
||||
/****************************************UTILS*****************************************/
|
||||
|
||||
const HighlightedQuery = ({ query }) => (
|
||||
<SyntaxHighlighter
|
||||
language="graphql"
|
||||
style={docco}
|
||||
>
|
||||
{query}
|
||||
</SyntaxHighlighter>
|
||||
)
|
||||
|
||||
const StyledBarChart = ({ path }) => {
|
||||
return (
|
||||
<div style={{margin: '10px', paddingTop: '65px'}}>
|
||||
<div key="styled-bar">
|
||||
<div style={{marginBottom: '20px'}} id="styled-bar">
|
||||
<h2 style={{margin: '10px', textAlign: 'center'}}>Styled bar chart</h2>
|
||||
<div className="chartWrapper">
|
||||
<div className="half_screen">
|
||||
<HighlightedQuery query={query} />
|
||||
</div>
|
||||
<div className="half_screen">
|
||||
<Chart query={query}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://github.com/hasura/graphql-engine/tree/master/community/tools/graphql2chartjs/example/app/src/charts/StyledBarChart.js">View source </a>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { StyledBarChart };
|
@ -0,0 +1,6 @@
|
||||
export { BasicBarChart } from './BasicBarChart';
|
||||
export { StyledBarChart } from './StyledBarChart';
|
||||
export { MultiDatasetBarChart } from './MultiDatasetBarChart';
|
||||
export { MixedLineBarChart } from './MixedLineBarChart';
|
||||
export { LiveChart } from './LiveChart';
|
||||
export { RealtimeTimeseriesChart } from './RealtimeTimeseriesChart';
|
14
community/tools/graphql2chartjs/example/app/src/index.css
Executable file
14
community/tools/graphql2chartjs/example/app/src/index.css
Executable file
@ -0,0 +1,14 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
31
community/tools/graphql2chartjs/example/app/src/index.js
Executable file
31
community/tools/graphql2chartjs/example/app/src/index.js
Executable file
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
import { WebSocketLink } from 'apollo-link-ws';
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
|
||||
const link = new WebSocketLink({
|
||||
uri: 'wss://graphql2chartjs.hasura.app/v1alpha1/graphql',
|
||||
options: {
|
||||
reconnect: true
|
||||
}
|
||||
})
|
||||
|
||||
const cache = new InMemoryCache();
|
||||
|
||||
const client = new ApolloClient({
|
||||
link,
|
||||
cache
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
(
|
||||
<ApolloProvider client={client}>
|
||||
<App />
|
||||
</ApolloProvider>
|
||||
),
|
||||
document.getElementById('root')
|
||||
);
|
7
community/tools/graphql2chartjs/example/app/src/logo.svg
Executable file
7
community/tools/graphql2chartjs/example/app/src/logo.svg
Executable file
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
|
||||
<g fill="#61DAFB">
|
||||
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
|
||||
<circle cx="420.9" cy="296.5" r="45.7"/>
|
||||
<path d="M520.5 78.1z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
9828
community/tools/graphql2chartjs/example/app/yarn.lock
Normal file
9828
community/tools/graphql2chartjs/example/app/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
||||
endpoint: http://localhost:8080
|
@ -0,0 +1,77 @@
|
||||
CREATE TABLE public.articles (
|
||||
id integer NOT NULL,
|
||||
title text NOT NULL,
|
||||
created timestamp with time zone DEFAULT now() NOT NULL,
|
||||
length integer NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE SEQUENCE public.article_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
CREATE TABLE public.comments (
|
||||
uuid uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||
article_id integer NOT NULL,
|
||||
comment text NOT NULL,
|
||||
created timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.likes (
|
||||
uuid uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||
article_id integer NOT NULL,
|
||||
created timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE VIEW public.article_stats AS
|
||||
SELECT a.id,
|
||||
a.title,
|
||||
a.length,
|
||||
l.count AS num_likes,
|
||||
c.count AS num_comments
|
||||
FROM public.articles a,
|
||||
( SELECT likes.article_id,
|
||||
count(likes.uuid) AS count
|
||||
FROM public.likes
|
||||
GROUP BY likes.article_id) l,
|
||||
( SELECT comments.article_id,
|
||||
count(comments.uuid) AS count
|
||||
FROM public.comments
|
||||
GROUP BY comments.article_id) c
|
||||
WHERE ((a.id = l.article_id) AND (a.id = c.article_id));
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.stocks (
|
||||
ticker text NOT NULL,
|
||||
price numeric NOT NULL,
|
||||
created timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.articles ALTER COLUMN id SET DEFAULT nextval('public.article_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.articles
|
||||
ADD CONSTRAINT article_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.comments
|
||||
ADD CONSTRAINT comments_pkey PRIMARY KEY (uuid);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.likes
|
||||
ADD CONSTRAINT likes_pkey PRIMARY KEY (uuid);
|
||||
|
||||
ALTER TABLE ONLY public.stocks
|
||||
ADD CONSTRAINT stocks_pkey PRIMARY KEY (ticker);
|
||||
|
@ -0,0 +1,2 @@
|
||||
- type: replace_metadata
|
||||
args: {"functions":[],"remote_schemas":[],"tables":[{"table":"article_stats","object_relationships":[],"array_relationships":[],"insert_permissions":[],"select_permissions":[{"role":"anonymous","comment":null,"permission":{"allow_aggregations":false,"columns":["id","title","length","num_likes","num_comments"],"filter":{}}}],"update_permissions":[],"delete_permissions":[],"event_triggers":[]},{"table":"comments","object_relationships":[],"array_relationships":[],"insert_permissions":[],"select_permissions":[],"update_permissions":[],"delete_permissions":[],"event_triggers":[]},{"table":"stocks","object_relationships":[],"array_relationships":[],"insert_permissions":[],"select_permissions":[{"role":"anonymous","comment":null,"permission":{"allow_aggregations":false,"columns":["ticker","price","created"],"filter":{}}}],"update_permissions":[],"delete_permissions":[],"event_triggers":[]},{"table":"articles","object_relationships":[],"array_relationships":[],"insert_permissions":[],"select_permissions":[],"update_permissions":[],"delete_permissions":[],"event_triggers":[]},{"table":"likes","object_relationships":[],"array_relationships":[],"insert_permissions":[],"select_permissions":[],"update_permissions":[],"delete_permissions":[],"event_triggers":[]}],"query_templates":[]}
|
@ -0,0 +1,6 @@
|
||||
- args:
|
||||
relationship: comments
|
||||
table:
|
||||
name: articles
|
||||
schema: public
|
||||
type: drop_relationship
|
@ -0,0 +1,13 @@
|
||||
- args:
|
||||
name: comments
|
||||
table:
|
||||
name: articles
|
||||
schema: public
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
id: article_id
|
||||
remote_table:
|
||||
name: comments
|
||||
schema: public
|
||||
type: create_array_relationship
|
@ -0,0 +1,3 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."stocks" DROP COLUMN "uuid"
|
||||
type: run_sql
|
@ -0,0 +1,6 @@
|
||||
- args:
|
||||
sql: CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
type: run_sql
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."stocks" ADD COLUMN "uuid" uuid NOT NULL DEFAULT gen_random_uuid()
|
||||
type: run_sql
|
@ -0,0 +1 @@
|
||||
[]
|
@ -0,0 +1,6 @@
|
||||
- args:
|
||||
cascade: false
|
||||
sql: |-
|
||||
alter table stocks drop constraint stocks_pkey;
|
||||
alter table stocks add constraint stocks_pkey primary key (uuid)
|
||||
type: run_sql
|
@ -0,0 +1 @@
|
||||
node_modules
|
1
community/tools/graphql2chartjs/example/scripts/.gitignore
vendored
Normal file
1
community/tools/graphql2chartjs/example/scripts/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.env
|
@ -0,0 +1,6 @@
|
||||
FROM node:8
|
||||
WORKDIR /usr/src/app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
CMD [ "node", "index.js" ]
|
10
community/tools/graphql2chartjs/example/scripts/README.md
Normal file
10
community/tools/graphql2chartjs/example/scripts/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
Scripts that run to keep changing the data in the database.
|
||||
|
||||
```
|
||||
ADMIN_SECRET=<secret> node index.js
|
||||
```
|
||||
|
||||
```
|
||||
docker build -t g2c-stocks-script:v1 .
|
||||
docker run -e ADMIN_SECRET=<secret> g2c-stocks-script:v1
|
||||
```
|
67
community/tools/graphql2chartjs/example/scripts/index.js
Normal file
67
community/tools/graphql2chartjs/example/scripts/index.js
Normal file
@ -0,0 +1,67 @@
|
||||
require('dotenv').config();
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const trendVars = {
|
||||
minor: 1,
|
||||
major: 10
|
||||
};
|
||||
|
||||
let companies = [
|
||||
{ticker: 'AMZN', price: 821.66},
|
||||
{ticker: 'FB', price: 490.89},
|
||||
{ticker: 'AAPL', price: 853.23},
|
||||
{ticker: 'GOOG', price: 829.36},
|
||||
{ticker: 'MSFT', price: 871.95}
|
||||
];
|
||||
|
||||
// Set up minor trend (updates every half second)
|
||||
const changeMinor = () => {
|
||||
companies = companies.map((c) => {
|
||||
return {
|
||||
ticker: c.ticker,
|
||||
price: c.price + (Math.random() * 2) - 1
|
||||
};
|
||||
});
|
||||
};
|
||||
setInterval(changeMinor, 500);
|
||||
|
||||
// Set up major trend (updates every 5seconds)
|
||||
const changeMajor = () => {
|
||||
companies = companies.map((c) => {
|
||||
return {
|
||||
ticker: c.ticker,
|
||||
price: c.price + (Math.random() * 20) - 10
|
||||
};
|
||||
});
|
||||
};
|
||||
setInterval(changeMajor, 5000);
|
||||
|
||||
// Update stock prices
|
||||
const updatePrices = () => {
|
||||
fetch(
|
||||
'https://graphql2chartjs.hasura.app/v1alpha1/graphql',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
query: `
|
||||
mutation($data: [stocks_insert_input!]!) {
|
||||
insert_stocks(objects: $data) {
|
||||
affected_rows
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
data: companies
|
||||
}
|
||||
}),
|
||||
headers: {
|
||||
'x-hasura-admin-secret': process.env.ADMIN_SECRET
|
||||
}
|
||||
}
|
||||
).then((resp) => resp.json()).then((r) => {
|
||||
console.log(JSON.stringify(r, null, 2));
|
||||
setTimeout(updatePrices, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
updatePrices();
|
18
community/tools/graphql2chartjs/example/scripts/package-lock.json
generated
Normal file
18
community/tools/graphql2chartjs/example/scripts/package-lock.json
generated
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "scripts",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"dotenv": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz",
|
||||
"integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
|
||||
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
|
||||
}
|
||||
}
|
||||
}
|
14
community/tools/graphql2chartjs/example/scripts/package.json
Normal file
14
community/tools/graphql2chartjs/example/scripts/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "scripts",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"dotenv": "^6.2.0",
|
||||
"node-fetch": "^2.3.0"
|
||||
}
|
||||
}
|
11
community/tools/graphql2chartjs/example/scripts/yarn.lock
Normal file
11
community/tools/graphql2chartjs/example/scripts/yarn.lock
Normal file
@ -0,0 +1,11 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
dotenv@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
|
||||
|
||||
node-fetch@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
|
224
community/tools/graphql2chartjs/how-it-works.md
Normal file
224
community/tools/graphql2chartjs/how-it-works.md
Normal file
@ -0,0 +1,224 @@
|
||||
## How it works
|
||||
|
||||
### Motivation
|
||||
|
||||
We started using ChartJS with GraphQL so that we could leverage GraphQL's realtime subscriptions to build realtime charts. Soon enough we realised that we can automate this tedious procedure of restructuring the GraphQL data to a form that ChartJS expects.
|
||||
|
||||
The idea behind this tool is to generate ChartJS compliant `data` object from your GraphQL response by simply adding a few aliases in your GraphQL query.
|
||||
|
||||
### GraphQL Aliasing
|
||||
|
||||
GraphQL gives you the power of aliasing the response fields with custom names. Lets look at a simple GraphQL query.
|
||||
|
||||
```gql
|
||||
query {
|
||||
rootField {
|
||||
field1
|
||||
field2
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The response to this query would be of the form:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"rootField": [
|
||||
{
|
||||
"field1": "value 1",
|
||||
"field2": "value 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, when we alias the above GraphQL query like so:
|
||||
|
||||
```gql
|
||||
query {
|
||||
aliasedRootField: rootField {
|
||||
aliasedField1: field1
|
||||
aliasedField2: field2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The response would be:
|
||||
|
||||
```
|
||||
{
|
||||
"data": {
|
||||
"aliasedRootField": {
|
||||
"aliasedField1": 'value 1',
|
||||
"aliasedField2": 'value 2'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ChartJS API
|
||||
|
||||
Most of the ChartJS charts expect a data object of the form:
|
||||
|
||||
```js
|
||||
{
|
||||
"labels": ["label1", "label2", ..., "label10"], // list of strings
|
||||
"datasets": [ // list of custom datasets with their properties
|
||||
{
|
||||
"data": [1334, 4314, ..., 2356],
|
||||
"backgroundColor": ['red', "blue", ..., "brown"],
|
||||
"borderColor": ['red', "blue", ..., "brown"],
|
||||
"fill": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### The graphql2chartjs function
|
||||
|
||||
The `graphql2chartjs` function i.e. the default export of this library accepts two arguments:
|
||||
1. **type**: (String) Type of the chart; Eg. `bar`, `line`, `pie`
|
||||
2. **graphqlData**: [Object] This should be an object with each field having its value as a list of data points.
|
||||
|
||||
You can directly feed the output of the `graphql2chartjs` function to your ChartJS instance.
|
||||
|
||||
```js
|
||||
|
||||
const graphQLResponse = makeGraphQLQuery();
|
||||
var chartType = 'bar';
|
||||
|
||||
var myChart = new Chart(ctx, {
|
||||
type: chartType,
|
||||
data: graphql2chartjs(chartType, graphQLResponse),
|
||||
options: {...} //custom options
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### How the restructuring works
|
||||
|
||||
The `graphql2chartjs` function understands the API for each kind of chart that it supports. It constructs appropriate arrays mapping the indices of labels with other dataset properties.
|
||||
|
||||
Lets consider this GraphQL response:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"VideoGameFollowers": [
|
||||
{
|
||||
"id": 1,
|
||||
"label": "Dota",
|
||||
"data": 427014,
|
||||
"pointBackgroundColor": "red",
|
||||
"fill": false
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"label": "CS:GO",
|
||||
"data": 220006,
|
||||
"pointBackgroundColor": "yellow",
|
||||
"fill": false
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"label": "NFS",
|
||||
"data": 71004,
|
||||
"pointBackgroundColor": "#3366ff",
|
||||
"fill": false
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"label": "PUBG",
|
||||
"data": 129769,
|
||||
"pointBackgroundColor": "#330000",
|
||||
"fill": false
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"label": "Quake 3",
|
||||
"data": 90808,
|
||||
"pointBackgroundColor": "green",
|
||||
"fill": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above GraphQL response is restructured to the ChartJS `data` as follows:
|
||||
|
||||
1. It starts with initializing the `data` object as:
|
||||
|
||||
```json
|
||||
{
|
||||
"labels": [],
|
||||
"datasets": []
|
||||
}
|
||||
```
|
||||
|
||||
2. It pushes a dataset with label as `humanized(rootFieldName)`. In this case, the root field is `VideoGameFollowers`. After inserting this step, the `data` object looks like
|
||||
|
||||
```json
|
||||
{
|
||||
"labels": [],
|
||||
"dataset": [
|
||||
{
|
||||
"label": "Video game followers"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3. It then iterates over the contents of this dataset. For each datapoint in the dataset, it pushes the label to the top level `labels` array and every other property to the dataset. So, after inserting the first data point, that is:
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Dota",
|
||||
"data": 427014,
|
||||
"pointBackgroundColor": "red",
|
||||
"fill": false
|
||||
}
|
||||
```
|
||||
|
||||
the `data` object looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"labels": ["Dota"],
|
||||
"datasets": [
|
||||
{
|
||||
"data": [427014],
|
||||
"pointBackgroundColor": ["red"],
|
||||
"fill": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
As you see, `pointBackgroundColor` and `data` get pushed in an array while `fill` gets set as a top level field. This is because `graphql2chartjs` function understands that the ChartJS API expects `pointBackgroundColor` to be an array and `fill` to be a simple flag.
|
||||
|
||||
4. It repeats the step above for every data point. The final `data` object would be:
|
||||
|
||||
```json
|
||||
{
|
||||
"labels": [ "Dota", "Cs:go", "Nfs", "Pubg", "Quake 3"],
|
||||
"datasets": [
|
||||
{
|
||||
"label": "Video game followers",
|
||||
"id": 5,
|
||||
"data": [ 427014, 220006, 71004, 129769, 90808 ],
|
||||
"pointBackgroundColor": ["red", "yellow", "#3366ff", "#330000", "green"],
|
||||
"fill": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Now you can pass this data object to your ChartJS instance and you will have a chart like this:
|
||||
|
||||
![line-chart-example](assets/readme-line-chart-example.png)
|
||||
|
||||
#
|
32
community/tools/graphql2chartjs/package.json
Normal file
32
community/tools/graphql2chartjs/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "graphql2chartjs",
|
||||
"version": "0.2.1",
|
||||
"description": "",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"test": "node test/index.test.js",
|
||||
"compile": "rm -rf lib && ./node_modules/.bin/babel src --out-dir lib",
|
||||
"bundle": "rm -rf bundle/js && ./node_modules/.bin/rollup -c",
|
||||
"launch": "yarn compile && yarn bundle && yarn publish"
|
||||
},
|
||||
"author": "Hasura",
|
||||
"repository": "hasura/graphql-engine",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"inflection": "^1.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/preset-env": "^7.3.4",
|
||||
"browserify": "^16.2.3",
|
||||
"rollup": "^1.3.1",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-commonjs": "^9.2.1",
|
||||
"rollup-plugin-filesize": "^6.0.1",
|
||||
"rollup-plugin-multi-entry": "^2.1.0",
|
||||
"rollup-plugin-node-resolve": "^4.0.1",
|
||||
"rollup-plugin-progress": "^1.0.0"
|
||||
}
|
||||
}
|
34
community/tools/graphql2chartjs/rollup.config.js
Normal file
34
community/tools/graphql2chartjs/rollup.config.js
Normal file
@ -0,0 +1,34 @@
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import multiEntry from 'rollup-plugin-multi-entry';
|
||||
import filesize from 'rollup-plugin-filesize';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import progress from 'rollup-plugin-progress';
|
||||
|
||||
let pluginOptions = [
|
||||
multiEntry(),
|
||||
resolve({
|
||||
module: true,
|
||||
jsnext: true,
|
||||
browser: true
|
||||
}),
|
||||
commonjs(),
|
||||
progress(),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
filesize({
|
||||
showGzippedSize: false,
|
||||
})
|
||||
];
|
||||
|
||||
export default [{
|
||||
input: './src/index.js',
|
||||
output: {
|
||||
name: 'main', // for external calls (need exports)
|
||||
file: 'bundle/js/index.js',
|
||||
format: 'umd',
|
||||
},
|
||||
moduleName: 'graphql2chartjs',
|
||||
plugins: pluginOptions,
|
||||
}];
|
101
community/tools/graphql2chartjs/src/array-fields.js
Normal file
101
community/tools/graphql2chartjs/src/array-fields.js
Normal file
@ -0,0 +1,101 @@
|
||||
const line = {
|
||||
data: true,
|
||||
borderDash: true,
|
||||
pointBackgroundColor: true,
|
||||
pointBorderColor: true,
|
||||
pointBorderWidth: true,
|
||||
pointRadius: true,
|
||||
pointStyle: true,
|
||||
pointRotation: true,
|
||||
pointHitRadius: true,
|
||||
pointHoverBackgroundColor: true,
|
||||
pointHoverBorderColor: true,
|
||||
pointHoverBorderWidth: true,
|
||||
pointHoverRadius: true,
|
||||
};
|
||||
|
||||
const bar = {
|
||||
data: true,
|
||||
backgroundColor: true,
|
||||
borderColor: true,
|
||||
borderWidth: true,
|
||||
hoverBackgroundColor: true,
|
||||
hoverBorderColor: true,
|
||||
hoverBorderWidth: true
|
||||
};
|
||||
|
||||
const pie = {
|
||||
data: true,
|
||||
backgroundColor: true,
|
||||
borderColor: true,
|
||||
borderWidth: true,
|
||||
hoverBackgroundColor: true,
|
||||
hoverBorderColor: true,
|
||||
hoverBorderWidth: true
|
||||
}
|
||||
|
||||
const polar = {
|
||||
data: true,
|
||||
backgroundColor: true,
|
||||
borderColor: true,
|
||||
borderWidth: true,
|
||||
hoverBackgroundColor: true,
|
||||
hoverBorderColor: true,
|
||||
hoverBorderWidth: true
|
||||
}
|
||||
|
||||
const scatter = {
|
||||
borderDash: true,
|
||||
pointBackgroundColor: true,
|
||||
pointBorderColor: true,
|
||||
pointBorderWidth: true,
|
||||
pointRadius: true,
|
||||
pointStyle: true,
|
||||
pointRotation: true,
|
||||
pointHitRadius: true,
|
||||
pointHoverBackgroundColor: true,
|
||||
pointHoverBorderColor: true,
|
||||
pointHoverBorderWidth: true,
|
||||
pointHoverRadius: true,
|
||||
};
|
||||
|
||||
const bubble = {
|
||||
borderDash: true,
|
||||
pointBackgroundColor: true,
|
||||
pointBorderColor: true,
|
||||
pointBorderWidth: true,
|
||||
pointRadius: true,
|
||||
pointStyle: true,
|
||||
pointRotation: true,
|
||||
pointHitRadius: true,
|
||||
pointHoverBackgroundColor: true,
|
||||
pointHoverBorderColor: true,
|
||||
pointHoverBorderWidth: true,
|
||||
pointHoverRadius: true,
|
||||
};
|
||||
|
||||
const radar = {
|
||||
data: true,
|
||||
borderDash: true,
|
||||
pointBackgroundColor: true,
|
||||
pointBorderColor: true,
|
||||
pointBorderWidth: true,
|
||||
pointRadius: true,
|
||||
pointStyle: true,
|
||||
pointRotation: true,
|
||||
pointHitRadius: true,
|
||||
pointHoverBackgroundColor: true,
|
||||
pointHoverBorderColor: true,
|
||||
pointHoverBorderWidth: true,
|
||||
pointHoverRadius: true,
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
line,
|
||||
bar,
|
||||
scatter,
|
||||
bubble,
|
||||
pie,
|
||||
polar,
|
||||
radar
|
||||
};
|
78
community/tools/graphql2chartjs/src/converter.js
Normal file
78
community/tools/graphql2chartjs/src/converter.js
Normal file
@ -0,0 +1,78 @@
|
||||
const inflection = require('inflection');
|
||||
const arrayFields = require('./array-fields');
|
||||
|
||||
const chartTypeMap = {
|
||||
'line': arrayFields.line,
|
||||
'bar': arrayFields.bar,
|
||||
'radar': arrayFields.radar,
|
||||
'polarArea': arrayFields.polar,
|
||||
'doughnut': arrayFields.pie,
|
||||
'pie': arrayFields.pie,
|
||||
'bubble': arrayFields.bubble,
|
||||
'scatter': arrayFields.scatter
|
||||
};
|
||||
|
||||
function convert(graphqlData, chartType) {
|
||||
const data = {
|
||||
labels: [],
|
||||
datasets: [],
|
||||
};
|
||||
const dataSets = Object.keys(graphqlData);
|
||||
const numDataSets = dataSets.length;
|
||||
for (let i = 0; i < numDataSets; i++) {
|
||||
const dataSetName = dataSets[i];
|
||||
const dataSet = graphqlData[dataSetName];
|
||||
data.datasets.push({
|
||||
label: inflection.transform(dataSetName, ['underscore', 'humanize']),
|
||||
data: []
|
||||
});
|
||||
const dataSetSize = dataSet.length;
|
||||
for (let j = 0; j < dataSetSize; j++) {
|
||||
const element = dataSet[j];
|
||||
let isRadiusDefined = element.data_r !== undefined;
|
||||
if (element.data_x !== undefined || element.data_t !== undefined) {
|
||||
if (element.data_x) {
|
||||
const dataPoint = {
|
||||
x: element.data_x,
|
||||
y: element.data_y,
|
||||
}
|
||||
if (isRadiusDefined) {
|
||||
dataPoint.r = element.data_r;
|
||||
}
|
||||
data.datasets[i].data.push(dataPoint);
|
||||
} else if (element.data_t !== undefined) {
|
||||
data.datasets[i].data.push();
|
||||
let dataPoint = {
|
||||
t: element.data_t,
|
||||
y: element.data_y
|
||||
};
|
||||
if (isRadiusDefined) {
|
||||
dataPoint[r] = element.data_r;
|
||||
}
|
||||
data.datasets[i].data.push(dataPoint);
|
||||
}
|
||||
}
|
||||
const arrayFieldsByType = element.chartType ? chartTypeMap[element.chartType] : chartTypeMap[chartType];
|
||||
Object.keys(element).forEach(property => {
|
||||
if (property === 'data_x' || property === 'data_t' || property === 'data_y' || property === 'data_r') {
|
||||
return;
|
||||
}
|
||||
if (property === 'label') {
|
||||
if (i === 0) {
|
||||
data.labels.push(element[property]);
|
||||
}
|
||||
} else if (arrayFieldsByType[property]) {
|
||||
if (!data.datasets[i][property]) {
|
||||
data.datasets[i][property] = [];
|
||||
}
|
||||
data.datasets[i][property].push(element[property]);
|
||||
} else {
|
||||
data.datasets[i][property === 'chartType' ? 'type' : property] = element[property];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
module.exports = convert;
|
118
community/tools/graphql2chartjs/src/index.js
Normal file
118
community/tools/graphql2chartjs/src/index.js
Normal file
@ -0,0 +1,118 @@
|
||||
const converter = require('./converter');
|
||||
|
||||
function convert (type, graphqlData) {
|
||||
try {
|
||||
return converter(graphqlData, type);
|
||||
} catch (e) {
|
||||
console.error('unexpected error in graphql2chartjs; please check your graphql response');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
class Graphql2Chartjs {
|
||||
constructor() {
|
||||
this.data = {}
|
||||
}
|
||||
|
||||
handleInit (graphqlData, arg) {
|
||||
this.data = {};
|
||||
if (typeof arg === 'string') {
|
||||
this.gqlData = graphqlData;
|
||||
this.chartType = arg;
|
||||
this.data = convert(arg, graphqlData);
|
||||
} else if (typeof arg === 'function') {
|
||||
this.transformer = arg;
|
||||
this.gqlData = this.transformGqlData(graphqlData, arg);
|
||||
this.data = convert(this.chartType, this.gqlData);
|
||||
} else {
|
||||
console.error('invalid second argument to graphql2chartjs');
|
||||
}
|
||||
}
|
||||
|
||||
transformGqlData(graphqlData, transformer) {
|
||||
const transformedGqlData = {};
|
||||
Object.keys(graphqlData).forEach(datasetName => {
|
||||
transformedGqlData[datasetName] = graphqlData[datasetName].map((dataPoint) => {
|
||||
return { ...dataPoint, ...transformer(datasetName, dataPoint) }
|
||||
});
|
||||
});
|
||||
return transformedGqlData;
|
||||
}
|
||||
|
||||
reset (graphqlData, arg) {
|
||||
this.handleInit(graphqlData, arg);
|
||||
}
|
||||
|
||||
add (graphqlData, arg) {
|
||||
if (!graphqlData) {
|
||||
console.warn('invalid graphql data provided to Graphql2Chartjs');
|
||||
return;
|
||||
}
|
||||
if (!this.gqlData || (this.gqlData && Object.keys(this.gqlData).length === 0)) {
|
||||
this.handleInit(graphqlData, arg);
|
||||
return;
|
||||
}
|
||||
this.mergeData(
|
||||
(typeof arg === 'function') ? this.chartType : arg,
|
||||
(typeof arg === 'function') ? this.transformGqlData(graphqlData, arg) : graphqlData
|
||||
);
|
||||
}
|
||||
|
||||
reform (transformer) {
|
||||
this.gqlData = this.transformGqlData(this.gqlData, transformer);
|
||||
this.data = convert(this.chartType, this.gqlData);
|
||||
}
|
||||
|
||||
mergeData(type, graphqlData) {
|
||||
const oldGqlData = { ...this.gqlData };
|
||||
Object.keys(graphqlData).forEach(dsName => {
|
||||
if (oldGqlData[dsName]) {
|
||||
graphqlData[dsName].forEach((dp) => {
|
||||
const oldDs = oldGqlData[dsName];
|
||||
let oldDsLength = oldGqlData[dsName].length;
|
||||
let refIndex;
|
||||
for (var _i = oldDs.length - 1; _i >= 0; _i--) {
|
||||
let refDp = oldDs[_i];
|
||||
if (refDp.label && refDp.label === dp.label) {
|
||||
refIndex = _i;
|
||||
break;
|
||||
} else if (refDp.data_r !== undefined) {
|
||||
if (refDp.data_x === dp.data_x && refDp.data_y === dp.data_y && refDp.data_r === dp.data_r) {
|
||||
refIndex = _i;
|
||||
break;
|
||||
}
|
||||
} else if (refDp.data_x !== undefined) {
|
||||
if (refDp.data_x === dp.data_x && refDp.data_y === dp.data_y) {
|
||||
refIndex = _i;
|
||||
break;
|
||||
}
|
||||
} else if (refDp.data_t !== undefined) {
|
||||
if (refDp.data_t === dp.data_t && refDp.data_y === dp.data_y) {
|
||||
refIndex = _i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!refIndex) {
|
||||
refIndex = oldDsLength;
|
||||
oldDsLength++;
|
||||
oldGqlData[dsName] = [...oldGqlData[dsName], { ...dp }]
|
||||
} else {
|
||||
oldGqlData[dsName][refIndex] = {...oldGqlData[dsName][refIndex], ...dp};
|
||||
}
|
||||
})
|
||||
} else {
|
||||
oldGqlData[dsName] = graphqlData[dsName];
|
||||
}
|
||||
})
|
||||
this.gqlData = oldGqlData;
|
||||
this.data = convert(type, oldGqlData);
|
||||
}
|
||||
}
|
||||
if ((typeof window) != 'undefined') {
|
||||
window.Graphql2Chartjs = Graphql2Chartjs;
|
||||
window.graphql2chartjs = Graphql2Chartjs;
|
||||
window.GraphQL2ChartJS = Graphql2Chartjs;
|
||||
window.Graphql2chartjs = Graphql2Chartjs;
|
||||
}
|
||||
module.exports = Graphql2Chartjs;
|
131
community/tools/graphql2chartjs/test/data.test.js
Normal file
131
community/tools/graphql2chartjs/test/data.test.js
Normal file
File diff suppressed because one or more lines are too long
178
community/tools/graphql2chartjs/test/index.test.js
Normal file
178
community/tools/graphql2chartjs/test/index.test.js
Normal file
@ -0,0 +1,178 @@
|
||||
const Graphql2Chartjs = require('../src/index');
|
||||
const { vg1, vg2, vg3, scatter1, scatter2 } = require('./data.test')
|
||||
|
||||
const logTestResult = (condition, message) => {
|
||||
if (condition) {
|
||||
console.log(`Passed: ${message}`);
|
||||
} else {
|
||||
console.log(`Failed: ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const runTests = () => {
|
||||
console.log('Running tests \n\n');
|
||||
let g2c = new Graphql2Chartjs();
|
||||
logTestResult((Object.keys(g2c.data).length === 0), 'Empty initialization');
|
||||
g2c = new Graphql2Chartjs()
|
||||
g2c.add(vg1.data, 'line')
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 5 && g2c.data.datasets.length === 1,
|
||||
g2c.data.datasets[0].fill === false &&
|
||||
g2c.data.datasets[0].data[0] === 427014 && g2c.data.datasets[0].pointBackgroundColor[0] === "red" &&
|
||||
g2c.data.datasets[0].data[1] === 220006 && g2c.data.datasets[0].pointBackgroundColor[1] === "yellow" &&
|
||||
g2c.data.datasets[0].data[2] === 71004 && g2c.data.datasets[0].pointBackgroundColor[2] === "#3366ff" &&
|
||||
g2c.data.datasets[0].data[3] === 129769 && g2c.data.datasets[0].pointBackgroundColor[3] === "#330000" &&
|
||||
g2c.data.datasets[0].data[4] === 90808 && g2c.data.datasets[0].pointBackgroundColor[4] === "green" &&
|
||||
true
|
||||
),
|
||||
'Initialization with data without transformer'
|
||||
)
|
||||
g2c = new Graphql2Chartjs();
|
||||
g2c.add(vg2.data, (dsName, dp) => {
|
||||
return {
|
||||
...dp, fill: true, chartType: 'line'
|
||||
}
|
||||
})
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 5 && g2c.data.datasets.length === 1,
|
||||
g2c.data.datasets[0].fill === true &&
|
||||
g2c.data.datasets[0].data[0] === 427014 && g2c.data.datasets[0].pointBackgroundColor[0] === "red" &&
|
||||
g2c.data.datasets[0].data[1] === 220006 && g2c.data.datasets[0].pointBackgroundColor[1] === "yellow" &&
|
||||
g2c.data.datasets[0].data[2] === 71004 && g2c.data.datasets[0].pointBackgroundColor[2] === "#3366ff" &&
|
||||
g2c.data.datasets[0].data[3] === 129222 && g2c.data.datasets[0].pointBackgroundColor[3] === "#330000" &&
|
||||
g2c.data.datasets[0].data[4] === 90808 && g2c.data.datasets[0].pointBackgroundColor[4] === "green"
|
||||
),
|
||||
'Initialization with data with transformer'
|
||||
)
|
||||
g2c.add({ "VideoGameFollowers": [{
|
||||
"id": 4,
|
||||
"label": "PUBG",
|
||||
"data": 129769,
|
||||
"pointBackgroundColor": "#333333",
|
||||
}]}, 'line')
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 5 && g2c.data.datasets.length === 1,
|
||||
g2c.data.datasets[0].fill === true &&
|
||||
g2c.data.datasets[0].data[0] === 427014 && g2c.data.datasets[0].pointBackgroundColor[0] === "red" &&
|
||||
g2c.data.datasets[0].data[1] === 220006 && g2c.data.datasets[0].pointBackgroundColor[1] === "yellow" &&
|
||||
g2c.data.datasets[0].data[2] === 71004 && g2c.data.datasets[0].pointBackgroundColor[2] === "#3366ff" &&
|
||||
g2c.data.datasets[0].data[3] === 129769 && g2c.data.datasets[0].pointBackgroundColor[3] === "#333333" &&
|
||||
g2c.data.datasets[0].data[4] === 90808 && g2c.data.datasets[0].pointBackgroundColor[4] === "green"
|
||||
),
|
||||
'Update without transformer'
|
||||
)
|
||||
g2c.add({ "VideoGameFollowers": [{
|
||||
"id": 4,
|
||||
"label": "PUBG",
|
||||
"data": 129769,
|
||||
"pointBackgroundColor": "#333333",
|
||||
}]}, (ds, dp) => {
|
||||
return {
|
||||
pointBackgroundColor: "#111111",
|
||||
data: 120000,
|
||||
chartType: 'line'
|
||||
}
|
||||
})
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 5 && g2c.data.datasets.length === 1 &&
|
||||
g2c.data.datasets[0].fill === true &&
|
||||
g2c.data.datasets[0].data[0] === 427014 && g2c.data.datasets[0].pointBackgroundColor[0] === "red" &&
|
||||
g2c.data.datasets[0].data[1] === 220006 && g2c.data.datasets[0].pointBackgroundColor[1] === "yellow" &&
|
||||
g2c.data.datasets[0].data[2] === 71004 && g2c.data.datasets[0].pointBackgroundColor[2] === "#3366ff" &&
|
||||
g2c.data.datasets[0].data[3] === 120000 && g2c.data.datasets[0].pointBackgroundColor[3] === "#111111" &&
|
||||
g2c.data.datasets[0].data[4] === 90808 && g2c.data.datasets[0].pointBackgroundColor[4] === "green"
|
||||
),
|
||||
'Update with transformer'
|
||||
)
|
||||
g2c.add(scatter1.data, 'line');
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 5 && g2c.data.datasets.length === 3 &&
|
||||
g2c.data.datasets[1].backgroundColor === "purple" &&
|
||||
g2c.data.datasets[2].backgroundColor === "orange"
|
||||
),
|
||||
'Update by adding a new dataset'
|
||||
)
|
||||
|
||||
g2c.reform((dp, ds) => {
|
||||
return {
|
||||
chartType: 'line'
|
||||
}
|
||||
})
|
||||
|
||||
g2c.add(scatter1.data, (ds, dp) => {
|
||||
if (ds === 'DataSet2') {
|
||||
return {
|
||||
...dp,
|
||||
backgroundColor: 'red',
|
||||
chartType: 'line'
|
||||
};
|
||||
} else if (ds === 'DataSet1') {
|
||||
return {
|
||||
...dp,
|
||||
backgroundColor: 'green',
|
||||
chartType: 'line'
|
||||
};
|
||||
|
||||
}
|
||||
return dp;
|
||||
});
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 5 && g2c.data.datasets.length === 3 &&
|
||||
g2c.data.datasets[1].backgroundColor === "green" &&
|
||||
g2c.data.datasets[2].backgroundColor === "red"
|
||||
),
|
||||
'Update by adding a new dataset with transformer'
|
||||
)
|
||||
|
||||
g2c.reset(scatter1.data, (ds, dp) => {
|
||||
if (ds === 'DataSet2') {
|
||||
return {
|
||||
...dp,
|
||||
backgroundColor: 'brown',
|
||||
chartType: 'scatter'
|
||||
};
|
||||
} else if (ds === 'DataSet1') {
|
||||
return {
|
||||
...dp,
|
||||
backgroundColor: 'blue',
|
||||
chartType: 'bubble'
|
||||
};
|
||||
}
|
||||
return dp;
|
||||
});
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 0 && g2c.data.datasets.length === 2 &&
|
||||
g2c.data.datasets[1].backgroundColor === "brown" &&
|
||||
g2c.data.datasets[0].backgroundColor === "blue"
|
||||
),
|
||||
'Reset scatter new data'
|
||||
)
|
||||
|
||||
g2c.reset(vg3.data, (ds, db) => {
|
||||
return {
|
||||
chartType: 'bar'
|
||||
}
|
||||
})
|
||||
logTestResult(
|
||||
(
|
||||
g2c.data.labels.length === 5 && g2c.data.datasets.length === 1 &&
|
||||
g2c.data.datasets[0].backgroundColor[0] === "red" && g2c.data.datasets[0].data[0] === 427014 &&
|
||||
g2c.data.datasets[0].backgroundColor[1] === "yellow" && g2c.data.datasets[0].data[1] === 220006 &&
|
||||
g2c.data.datasets[0].backgroundColor[2] === "#3366ff" && g2c.data.datasets[0].data[2] === 71004 &&
|
||||
g2c.data.datasets[0].backgroundColor[3] === "#330000" && g2c.data.datasets[0].data[3] === 129769 &&
|
||||
g2c.data.datasets[0].backgroundColor[4] === "green" && g2c.data.datasets[0].data[4] === 90808
|
||||
),
|
||||
'Reset with bar data'
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
runTests();
|
3175
community/tools/graphql2chartjs/yarn.lock
Normal file
3175
community/tools/graphql2chartjs/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user