Skip to content

Commit be7e112

Browse files
Timeline charts example (#13)
* Add the timeline code example * Nicer name for this function please * Use caret for sdk package version * Move the slider to a separate component * Use refs for timerId and replay options * Remove favicon link * Improve readme and remove gitgnore
1 parent e0dd9b1 commit be7e112

File tree

11 files changed

+416
-0
lines changed

11 files changed

+416
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# MongoDB Charts Embedding Example - Timeline Chart
2+
3+
## Background
4+
5+
📄 _[See the MongoDB Charts Embedding Docs for more details](https://docs.mongodb.com/charts/saas/embedding-charts/)_
6+
7+
The example code in this directory is building a small react app, implementing a chart timeline using the Charts Embedding SDK.
8+
9+
What the application is doing is showing the distribution of Olympic medals through the years.
10+
11+
We are using a [filter](https://docs.mongodb.com/charts/saas/filter-embedded-charts/#filter-data-on-charts-embedded-with-the-sdk) on the Olympic year, changing the filter every few seconds to turn it into a timelapse.
12+
13+
The idea is this - for every Olympic year we are filtering all data from the beginning of the Olympic games to the current year.
14+
15+
Example:
16+
17+
- First Olympic Year: in this case it's just the data for the first year
18+
- Second Olympic Year: all data from First Olympic Year to Second Olympic Year
19+
- Third Olympic Year: all data from First Olympic Year to Third Olympic Year
20+
- ....
21+
- Last Olympic Year: all data from First Olympic Year to Last Olympic Year, which in this case is all the data
22+
23+
The idea of doing this application was to show you some interactivity you can accomplish with the embedding SDK.
24+
Doing timeline charts is not really a feature of the embedding SDK but it shows you that with a little bit of code you can do different things with your charts.
25+
26+
## Quickstart
27+
28+
_The following steps presume the use of npm, though yarn works as well._
29+
30+
1. Ensure you have Node installed. You can confirm with `node --version`.
31+
32+
2. Clone the Git repository or download the code to your computer.
33+
34+
3. Run `npm install` to install the package dependencies.
35+
36+
4. Run `npm start` to start the application. It will open directly in the browser at this address `http://localhost:3000`.
37+
38+
## Preparing your Chart for Embedding
39+
40+
This sample is preconfigured to render specific charts. You can run the sample as-is, or you can modify it to render your own charts by completing the following steps:
41+
42+
1. Log onto MongoDB Charts
43+
44+
2. If you haven't done so already, create a chart on any dashboard that you would like to embed.
45+
46+
3. Go to the Data Sources tab, find the data source that you are using on the chart, and choose External Sharing Options from the (...) menu. Make sure that embedding is enabled for this data source and select '**Unauthenticated or Verified Signature**'
47+
48+
4. Find the chart you want to embed, click the **(...)** menu and select **Embed Chart**
49+
50+
5. Ensure the Unauthenticated tab is selected and turn on '**Enable unauthenticated access**'
51+
52+
6. Ensure that you whitelist all fields that you want to use filters for.
53+
54+
7. Select the **Javascript SDK** option
55+
56+
8. Note the Chart ID and the Chart Base URL, as you will need them for running the demo.
57+
58+
## Running this Sample with your data
59+
60+
1. If you do not wish to use our sample data and have completed the above steps to prepare your own chart for embedding,
61+
- Open the _Dashboard.jsx_ file (`src/Dashboard.jsx`)
62+
- Replace the `baseUrl` string on with the base URL you copied from the MongoDB Charts Embedded Chart menu (look for "\~REPLACE\~" in the comments)
63+
- Replace the `chartId` string on with the chart ID you copied from the MongoDB Charts Embedded Chart menu. The example code shows 2 charts. (look for "\~REPLACE\~" in the comments)
64+
- Change the filter to whatever makes sense for your project. Currently the filter gets the data between two years and is using the "Year" field for filtering.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "timeline-chart",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"@material-ui/core": "^4.9.12",
7+
"@mongodb-js/charts-embed-dom": "^1.1.2",
8+
"react": "^16.13.1",
9+
"react-dom": "^16.13.1",
10+
"react-scripts": "3.4.1"
11+
},
12+
"scripts": {
13+
"start": "react-scripts start"
14+
},
15+
"eslintConfig": {
16+
"extends": "react-app"
17+
},
18+
"browserslist": {
19+
"production": [
20+
">0.2%",
21+
"not dead",
22+
"not op_mini all"
23+
],
24+
"development": [
25+
"last 1 chrome version",
26+
"last 1 firefox version",
27+
"last 1 safari version"
28+
]
29+
}
30+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<meta name="theme-color" content="#000000" />
7+
<meta
8+
name="description"
9+
content="Web site created using create-react-app"
10+
/>
11+
<title>React App</title>
12+
</head>
13+
<body>
14+
<noscript>You need to enable JavaScript to run this app.</noscript>
15+
<div id="root" style="height: 100%;"></div>
16+
</body>
17+
</html>
36.3 KB
Loading
46.3 KB
Loading
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
.App {
2+
height: 100%;
3+
display: flex;
4+
flex-direction: column;
5+
background-color: #ebf1f5;
6+
}
7+
8+
.slider {
9+
margin: 50px 50px 0;
10+
}
11+
12+
.charts {
13+
display: flex;
14+
justify-content: space-evenly;
15+
margin: 0 20px;
16+
}
17+
18+
.header {
19+
display: flex;
20+
justify-content: center;
21+
align-items: center;
22+
border-bottom: 2px solid lightgray;
23+
}
24+
25+
button {
26+
background-color: transparent;
27+
}
28+
29+
.actions {
30+
margin: 1% 3%;
31+
}
32+
33+
.action-button {
34+
border: 0;
35+
cursor: pointer;
36+
background-size: cover;
37+
margin-left: 20px;
38+
width: 50px;
39+
height: 50px;
40+
outline: none;
41+
}
42+
43+
.play-button {
44+
background-image: url("/play.png");
45+
}
46+
47+
.pause-button {
48+
background-image: url("/pause.png");
49+
}
50+
51+
.charts {
52+
height: 100%;
53+
max-height: 850px;
54+
background-color: white;
55+
margin: 1% 3%;
56+
display: flex;
57+
justify-content: space-around;
58+
border-radius: 4px;
59+
}
60+
61+
#columnChart {
62+
width: 42%;
63+
max-height: 850px;
64+
}
65+
66+
#geoChart {
67+
width: 55%;
68+
max-height: 850px;
69+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from "react";
2+
import "./App.css";
3+
4+
import Dashboard from "./Dashboard";
5+
6+
function App() {
7+
return (
8+
<div className="App">
9+
<Dashboard />
10+
</div>
11+
);
12+
}
13+
14+
export default App;
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import React, { useRef, useCallback, useState } from "react";
2+
3+
import PrettySlider from "./PrettySlider";
4+
import ChartsEmbedSDK from "@mongodb-js/charts-embed-dom";
5+
import { useEffect } from "react";
6+
7+
const firstOlympicsYear = 1896;
8+
const lastOlympicsYear = 2016;
9+
const timelineInterval = 2000; //ms
10+
11+
const sdk = new ChartsEmbedSDK({
12+
baseUrl: "https://charts.mongodb.com/charts-data-science-project-aygif", // Optional: ~REPLACE~ with the Base URL from your Embed Chart dialog
13+
});
14+
15+
const columnChart = sdk.createChart({
16+
chartId: "ff518bbb-923c-4c2c-91f5-4a2b3137f312", // Optional: ~REPLACE~ with the Chart ID from your Embed Chart dialog
17+
});
18+
19+
const geoChart = sdk.createChart({
20+
chartId: "b1983061-ee44-40ad-9c45-4bb1d4e74884", // Optional: ~REPLACE~ with the Chart ID from your Embed Chart dialog
21+
});
22+
23+
export default function Dashboard() {
24+
const refColumnChart = useRef(null);
25+
const refGeoChart = useRef(null);
26+
const [year, setYear] = useState(lastOlympicsYear);
27+
const [playing, setPlaying] = useState(false);
28+
29+
const yearRef = React.useRef(year);
30+
yearRef.current = year;
31+
32+
const timerIdRef = React.useRef();
33+
const replayRef = React.useRef(false);
34+
35+
const renderColumnChart = useCallback(async (ref) => {
36+
try {
37+
await columnChart.render(ref);
38+
} catch (e) {
39+
console.error(e);
40+
}
41+
}, []);
42+
43+
const renderGeoChart = useCallback(async (ref) => {
44+
try {
45+
await geoChart.render(ref);
46+
} catch (e) {
47+
console.error(e);
48+
}
49+
}, []);
50+
51+
const setRefColumnChart = useCallback(
52+
(ref) => {
53+
if (ref) {
54+
renderColumnChart(ref);
55+
}
56+
// Save a reference to the node
57+
refColumnChart.current = ref;
58+
},
59+
[renderColumnChart]
60+
);
61+
62+
const setRefGeoChart = useCallback(
63+
(ref) => {
64+
if (ref) {
65+
renderGeoChart(ref);
66+
}
67+
// Save a reference to the node
68+
refGeoChart.current = ref;
69+
},
70+
[renderGeoChart]
71+
);
72+
73+
const handleChange = (_event, newValue) => {
74+
setYear(newValue);
75+
};
76+
77+
const handleChangeCommitted = (_event, newValue) => {
78+
setYear(newValue);
79+
getDataFromAllPreviousYears(newValue);
80+
};
81+
82+
// This function is creating the filter that will be executed on the data.
83+
// If you wish to run this example on your data, change it to accomodate your idea or make sure you have a "Year" field in your data source
84+
const getDataFromAllPreviousYears = (endYear) => {
85+
let filter = {
86+
$and: [
87+
{ Year: { $gte: firstOlympicsYear } },
88+
{ Year: { $lte: endYear } },
89+
],
90+
};
91+
92+
return Promise.all([
93+
geoChart.setFilter(filter),
94+
columnChart.setFilter(filter),
95+
]);
96+
};
97+
98+
const play = () => {
99+
let currentYear = yearRef.current + 4;
100+
101+
if (currentYear > lastOlympicsYear) {
102+
if (replayRef.current) {
103+
currentYear = firstOlympicsYear;
104+
replayRef.current = false;
105+
} else {
106+
clearInterval(timerIdRef.current);
107+
setPlaying(false);
108+
return;
109+
}
110+
}
111+
getDataFromAllPreviousYears(currentYear);
112+
setYear(currentYear);
113+
};
114+
115+
const setTimelineInterval = () => {
116+
if (playing) {
117+
// we do this because the first play with setInterval is after the time specified
118+
// and the first filter should be instantaneous
119+
play();
120+
timerIdRef.current = setInterval(play, timelineInterval);
121+
} else {
122+
clearInterval(timerIdRef.current);
123+
}
124+
};
125+
126+
const actionTimeline = () => {
127+
if (yearRef.current === lastOlympicsYear) {
128+
replayRef.current = true;
129+
}
130+
131+
setPlaying(!playing);
132+
};
133+
134+
useEffect(() => {
135+
setTimelineInterval();
136+
}, [playing]);
137+
138+
const buttonClass = `action-button ${
139+
playing ? "pause-button" : "play-button"
140+
}`;
141+
142+
return (
143+
<>
144+
<header className="header">
145+
<h1>Building timeline charts with Charts Embedding SDK (MongoDB)</h1>
146+
<button className={buttonClass} onClick={actionTimeline}></button>
147+
</header>
148+
149+
<div className="actions">
150+
<div className="slider">
151+
<PrettySlider
152+
valueLabelDisplay="on"
153+
aria-label="pretto slider"
154+
min={firstOlympicsYear}
155+
max={lastOlympicsYear}
156+
step={4}
157+
onChangeCommitted={handleChangeCommitted}
158+
onChange={handleChange}
159+
value={year}
160+
defaultValue={lastOlympicsYear}
161+
/>
162+
</div>
163+
</div>
164+
165+
<div className="charts">
166+
<div id="columnChart" ref={setRefColumnChart}></div>
167+
<div id="geoChart" ref={setRefGeoChart}></div>
168+
</div>
169+
</>
170+
);
171+
}

0 commit comments

Comments
 (0)