A JavaScript data visualization library for Keen.
npm install keen-dataviz --save
Built-in themes and color palettes
https://github.com/keen/theme-builder - Live Demo
import KeenDataviz from 'keen-dataviz';
import KeenAnalysis from 'keen-analysis'; // API client
import 'keen-dataviz/dist/keen-dataviz.css';
/*
Webpack users: to include CSS files in your project please install
https://github.com/webpack-contrib/css-loader
https://github.com/webpack-contrib/style-loader
Here's an example: https://github.com/keen/keen-dataviz-webpack-boilerplate
*/
const chart = new KeenDataviz({
// Required:
container: '#my-chart-div', // querySelector
// Optional:
title: 'New Customers per Week',
subtitle: 'chart subtitle',
});
// use keen-analysis.js to run a query
const client = new KeenAnalysis({
projectId: 'YOUR_PROJECT_ID',
readKey: 'YOUR_READ_KEY'
});
client
.query({
analysisType: 'count',
eventCollection: 'pageviews',
timeframe: 'this_7_days',
interval: 'daily'
})
.then(results => {
chart
.render(results);
})
.catch(error => {
chart
.message(error.message);
});
https://github.com/keen/keen-react-charts
https://github.com/keen/keen-dataviz-webpack-boilerplate
Include keen-dataviz.js and keen-dataviz.css within your page or project.
<html>
<head>
<meta charset="utf-8">
<script crossorigin src="https://cdn.jsdelivr.net/npm/keen-analysis@3"></script>
<link href="https://cdn.jsdelivr.net/npm/keen-dataviz@3/dist/keen-dataviz.min.css" rel="stylesheet" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/keen-dataviz@3/dist/keen-dataviz.min.js"></script>
</head>
<body>
<!-- DOM Element -->
<div id="some_container"></div>
<style>
#some_container {
width: 400px;
height: 250px;
}
</style>
<!-- Create and Render -->
<script>
const chart = new KeenDataviz({
container: '#some_container',
title: 'New Customers per Week'
});
const client = new KeenAnalysis({
projectId: 'YOUR_PROJECT_ID',
readKey: 'YOUR_READ_KEY'
});
client
.query({
analysisType: 'count',
eventCollection: 'pageviews',
timeframe: 'this_14_days',
interval: 'daily'
})
.then(function(results){
chart
.render(results);
})
.catch(function(error){
chart
.message(error.message);
});
</script>
</body>
</html>
Specify the visualization type. If no type is set, the library will automatically set the best option.
Full list of the supported chart types
const chart = new KeenDataviz({
container: '#some_container', // required
type: 'area'
});
Date formatting is possible by passing either a string or function to dateFormat
. In either case, this value will be set as the axis.x.tick.format
configuration property. As a built-in feature of C3.js, functions are used as an iterator, receiving the date for each interval in milliseconds.
// Use a string template
const chart = new KeenDataviz({
container: '#some_container', // required
dateFormat: '%Y-%m'
});
// .. or a function
const chart = new KeenDataviz({
container: '#some_container', // required
dateFormat: function(ms){
const date = new Date(ms);
return date.getFullYear();
}
});
Date Localization: Dates will be localized to the browser's timezone by default. This is generally desirable, but there are some instances where you may wish to retain the timezones that are returned by the API. You can disable this behavior in any C3.js-based visualization by setting axis.x.localtime
to false
:
const chart = new KeenDataviz({
container: '#some_container', // required
axis: {
x: {
localtime: false
}
}
});
const client = new KeenAnalysis({
projectId: 'YOUR_PROJECT_ID',
readKey: 'YOUR_READ_KEY'
});
const queryPageviews = client
.query({
analysisType: 'count',
eventCollection: 'pageviews',
timeframe: 'this_30_days',
interval: 'daily'
});
const queryFormSubmissions = client
.query({
analysisType: 'count',
eventCollection: 'form_submissions',
timeframe: 'this_30_days',
interval: 'daily'
});
client
.run([queryPageviews, queryFormSubmissions])
.then(results => {
const chart = new KeenDataviz({
container: '#some_container',
results,
// optional
labelMapping: {
'pageviews count': 'Pageviews',
'form_submissions count': 'Forms collected'
}
// or labelMapping RegExp
});
})
.catch(err => {
// Handle errors
console.error(err);
});
const chart = new KeenDataviz({
container: '#some_container',
clearOnRender: true, // clear c3
transition: {
duration: 0 // to avoid animation during re-render
}
});
const fetchResultsAndRender = () => {
client
.query({
analysisType: 'count',
eventCollection: 'pageviews',
timeframe: 'previous_60_minutes',
interval: 'minutely'
})
.then(results => {
chart.render(results);
});
};
const intervalTime = 60 * 1000; // every minute, because query interval is "minutely"
setInterval( () => {
fetchResultsAndRender();
}, intervalTime);
fetchResultsAndRender(); // initial fetch and render
All of the options are passed to C3. See https://c3js.org/reference.html
const chart = new KeenDataviz({
container: '#some_container', // required
// c3 options example, read more https://c3js.org/reference.html
axis: {
x: {
localtime: false
}
},
transition: {
duration: 3000
},
zoom: {
enabled: true
},
grid: {
x: {
show: true
},
y: {
show: true
}
},
size: {
// https://c3js.org/reference.html#size-width
// It's better to control the size using the CSS of the container HTML element
},
onrendered: () => {
// do something when the chart is ready... https://c3js.org/reference.html#onrendered
}
});
const chart = new KeenDataviz({
container: '#some_container', // required
title: false
});
const chart = new KeenDataviz({
container: '#some_container', // required
// default values
legend: {
show: true,
position: 'right', // top, bottom, left, right
alignment: 'center', // vertical alignment: ['top', 'middle', 'bottom'], horizontal alignment: ['left', 'center', 'right'];
label: {
textMaxLength: 12
},
pagination: {
offset: 0, // start from
limit: 5 // items per page
},
tooltip: {
show: true,
pointer: true
},
// sort: (columns) => { return columns; } // custom sorting function
}
});
Default method of sorting is by column name ASC. You can use your own sorting function
const chart = new KeenDataviz({
container: '#some_container', // required
// default values
legend: {
show: true,
position: 'right', // top, bottom, left, right
sort: function (columns) {
const columnsSorted = [];
columns.forEach(column => {
if (column[0] !== 'x') {
let sumOfValues = column.slice(1).reduce((acc = 0, item) => {
return acc + item;
});
columnsSorted.push({ columnName: column[0], columnSum: sumOfValues});
}
});
// let's sort by SUM, DESC
columnsSorted.sort(function(a, b) {
return b.columnSum - a.columnSum;
});
return columnsSorted.map(item => item.columnName);
}
}
});
const chart = new KeenDataviz({
container: '#some_container', // required
colors: ['#1167c5', 'green', '#000000']
});
const chart = new KeenDataviz({
container: '#some_container', // required
palette: 'autocollector' // autocollector | modern | dracula
});
const chart = new KeenDataviz({
container: '#some_container', // required
colorMapping: {
'some_label_1': '#c51111', // column - color
'some_label_2': '#11c53b'
}
});
const chart = new KeenDataviz({
container: '#some_container', // required
labelMapping: {
'long_complex_key_name': 'Human readable label',
}
});
const chart = new KeenDataviz({
container: '#some_container', // required
labelMappingRegExp: [
[/anytext/, 'Purchases'],
[/lorem ipsum/gi, 'Visits']
]
});
const chart = new KeenDataviz({
container: '#some_container', // required
labelMapping: {
'long_complex_key_name': 'Human readable label',
},
labelMappingDimension: 'column' // column, row, both
});
const chart = new KeenDataviz({
container: '#some_container', // required
errorMapping: {
'No data to display': 'my custom message 123'
}
});
const chart = new KeenDataviz({
container: '#some_container', // required
showErrorMessages: false
});
const chart = new KeenDataviz({
container: '#some_container', // required
labels: [
'Step 1',
'Step 2',
'Step 3'
]
});
By default you can pass results with a configuration object
const client = new KeenAnalysis({
projectId: 'YOUR_PROJECT_ID',
readKey: 'YOUR_READ_KEY'
});
// execute some query
client
.query({
analysisType: 'count',
eventCollection: 'pageviews',
timeframe: 'this_160_days'
})
.then(results => {
const chart = new KeenDataviz({
container: '#some_container', // required
results
});
})
.catch(err => {
// Handle errors
});
The same, but with render function:
const chart = new KeenDataviz({
container: '#some_container', // required
showLoadingSpinner: true
});
const client = new KeenAnalysis({
projectId: 'YOUR_PROJECT_ID',
readKey: 'YOUR_READ_KEY'
});
// execute some query
client
.query({
analysisType: 'count',
eventCollection: 'pageviews',
timeframe: 'this_30_days'
})
.then(results => {
// Handle results
chart.render(results);
})
.catch(err => {
// Handle errors
});
const chart = new KeenDataviz({
container: '#some_container', // required
renderAsPromise: true
});
chart
.render(results)
.then({
// do something after rendering is complete
})
.catch(err => {
// handle render error
});
Long query response time? Use a loading spinner to let users know, that data is loading.
const chart = new KeenDataviz({
container: '#some_container', // required
showLoadingSpinner: true
});
const client = new KeenAnalysis({
projectId: 'YOUR_PROJECT_ID',
readKey: 'YOUR_READ_KEY'
});
// execute some query
client
.query({
analysisType: 'count',
eventCollection: 'pageviews',
timeframe: 'this_160_days'
})
.then(results => {
// Handle results
chart.render(results);
})
.catch(err => {
// Handle errors
});
Determine how groupBy results are sorted (asc
for ascending, desc
for descending).
const chart = new KeenDataviz({
container: '#some_container', // required
sortGroups: 'asc'
});
Determine how interval results are sorted (asc
for ascending, desc
for descending).
const chart = new KeenDataviz({
container: '#some_container', // required
sortIntervals: 'desc'
});
Create a stacked chart, used to break down and compare parts of a whole.
const chart = new KeenDataviz({
container: '#some_container', // required
stacking: 'normal' // 'normal' - stacked chart
// 'percent' - 100% stacked chart
});
Create chart without axis, grid and legend
const chart = new KeenDataviz({
container: '#some_container', // required
sparkline: true
})
/*
dummy event model
{
user: {
email: '[email protected]'
},
favourite_fruit: 'Avocado'
}
*/
const chart = new KeenDataviz({
container: '#some_container', // required
table: {
columns: ['favourite_fruit', 'user.email', 'keen.created_at'] // custom order of the columns
}
});
const chart = new KeenDataviz({
container: '#some_container', // required
table: {
pagination: {
limit: 10 // items per page
}
}
});
const chart = new KeenDataviz({
container: '#some_container', // required
});
// dummy result
const result = {
'clicks': [3, 14, 7, 22, 11, 55, 11, 22],
'views': [14, 58, 11, 32, 11, 23, 45, 66]
};
function customParser(data){
const ds = new KeenDataset();
Object.keys(data).forEach(dataKey => {
ds.appendColumn(dataKey);
data[dataKey].forEach((item, itemIndex) => {
ds.set([dataKey, itemIndex+1], item);
});
});
return ds;
}
chart
.render(customParser(result));
const chart = new KeenDataviz({
container: '#some_container', // required
type: 'table',
table: {
mapValues: {
'keen.timestamp': (value) => {
return value.toUpperCase();
}
}
}
})
const chart = new KeenDataviz({
container: '#some_container', // required
renderOnVisible: true
})
By default, it's enabled for all charts with relative time frames starting with this_ eg. this_x_hours. To hide it, use a configuration property:
partialIntervalIndicator: false
You can turn off deprecation warnings with
const chart = new Keen.Dataviz({
container: '#container', // required
showDeprecationWarnings: false
});
You can add button to enable download results from chart.
const chart = new Keen.Dataviz({
container: '#container', // required
ui: {
buttons: {
download: {
label: 'Download as a JPG file', // optional - by default it's just "Download"
type: 'jpg', // optional - by default it's 'json', supported types ['jpg', 'jpeg', 'png', 'csv', 'json']
}
}
}
});
// method by default generates a PNG image
chart.exportImage();
// if quality provided then JPEG is generated
// quality - a number between 0 to 1 indicating image quality (quality = 0 generates PNG image)
// bgcolor - a string value for background color, any valid CSS color value (defaults to '#fff')
chart.exportImage({ quality: 1, bgcolor: 'blue' });
// method by default generates a JSON file
chart.exportData();
// supported formats : 'json', 'csv'
chart.exportData('csv');
You can easily show execution metadata if it's available. By defualt this option is set to true.
const chart = new Keen.Dataviz({
container: '#container', // required
ui: {
executionMetadata: true // default
}
});
By default this feature is switched on. When you click on a point on the chart the result's value is copied to clipboard. When you select a group of points then sum of their values are copied.
const chart = new Keen.Dataviz({
container: '#container', // required
utils: {
clickToCopyToClipboard: false,
}
});
Advanced usage:
- Chart types
- Create custom cohort visualizations
- Create and visualize custom Dataset instances
- Create custom themes
Need a hand with something? Shoot us an email at [email protected]. We're always happy to help, or just hear what you're building! Here are a few other resources worth checking out:
Learn more about the Dataviz API
This is an open source project and we love involvement from the community! Hit us up with pull requests and issues. The more contributions the better!
Learn about contributing to this project.
Run the following commands to install and build this project:
# Clone the repo
$ git clone https://github.com/keen/keen-dataviz.js.git && cd keen-dataviz.js
# Install project dependencies
$ npm install
# Build project with Webpack
$ npm run build
# Build and launch to view demo page
$ npm run start
# Run Jest tests
$ npm run test