Customizing Sisense using JavaScript
Here are some examples for customizing Sisense using JavaScript.
Click each one to view the code.
Customizing Dashboard UI, Filters and Behavior
<!--
This code snippet lets you add a dashboard filter.
-->
<!-- replace with your Sisense server address -->
<script type="text/javascript"
src="https://test.sisense.com/js/sisense.js"></script>
<script type = "text/javascript">
// configure sisense app url and the dashboard that will be loaded
const config = {
sisenseUrl: 'https://test.sisense.com',
dashboardId: '624bf2cae07da90036b0301d',
widgets: [
{ widgetId: '624bf31be07da90036b03020', containerId: 'widget1' },
{ widgetId: '6253cea93b50e60036f411e1', containerId: 'widget2' }
],
filtersContainerId: 'filters'
}
// construct new member filter
const constructFilter = (dim, members) => {
const newFilter = {
"jaql": {
"dim": dim,
"datatype": "text",
"title": "Country",
"filter":
{
"explicit": true,
"multiSelection": true,
"members": members
}
}
};
return newFilter;
}
const addFilter = (dash) => {
// create new filter
const newFilter = constructFilter("[Country.Country]", ["Armenia"])
const filterUpdateOption = {
save: true, // save dashboard with new filters
refresh: true, // refresh widgets after filter change
unionIfSameDimensionAndSameType: false, // merge filter with same dimension, if `false` filter will
be relpaced
shouldKeepDisableState: false, // define keep or not disable state when replace existing filter
isRename: false, // rename existing filter
reason: 'filtersUpdated', // mark for filterChanged event
doNotTriggerChange: false // fire or not `filterschanged` event
};
// add the filter to dashboard
const filters = dash.$$model.filters;
filters.update([newFilter], filterUpdateOption);
return dash;
}
const loadDashboard = (app, dashboardId) => {
return app.dashboards.load(dashboardId);
}
const renderWidgets = (dashboard) => {
config.widgets.forEach((widgetCfg) => {
// render widget, configure your widget id and container id
dashboard.widgets.get(widgetCfg.widgetId).container =
document.getElementById(widgetCfg.containerId);
});
return refreshDashboard(dashboard);
}
const renderFilters = (dashboard) => {
// render Sisense filters panel inside of container with particular id
dashboard.renderFilters(document.getElementById(config.filtersContainerId));
return refreshDashboard(dashboard);
}
const refreshDashboard = (dashboard) => {
dashboard.refresh();
return dashboard;
}
// connect to sisense
// load dashboard
// render widgets
Sisense.connect(config.sisenseUrl)
.then((app) => loadDashboard(app, config.dashboardId))
.then(renderWidgets)
.then(addFilter)
.then(renderFilters);
</script>
<!--
This code snippet lets you change the data source of dashboard
-->
<!-- replace with your Sisense server address -->
<script type="text/javascript"
src="https://test.sisense.com/js/sisense.js"></script>
<script type = "text/javascript">
// configure sisense app url and the dashboard that will be loaded
const config = {
sisenseUrl: 'https://test.sisense.com',
dashboardUrl: '6141feba14d79800357dd138',
widgets: [
{ widgetId: '624bf31be07da90036b03020', containerId: 'widget1' },
{ widgetId: '6253cea93b50e60036f411e1', containerId: 'widget2' }
]
}
// get datasource info
const getDataSourceInfo = ($http, sisenseUrl, dsName ) => {
let dsInfoUrl = `${sisenseUrl}/api/datasources/${dsName}`;
return $http.get(dsInfoUrl);
}
// Change datasource fo model, model has to have 'datasource' property
const changeDatasource = (model, datasource) => {
if(model.datasource && datasource && datasource.fullname !==
model.datasource.fullname){
model.datasource = datasource;
console.log('Succesfully Changed DS');
}
}
// Change datasource for filters
const changeDatasourceForFilters = (filters, datasource) => {
filters.$$items.forEach((filter) => {
changeDatasource(filter.jaql, datasource);
})
}
const loadDashboard = (app, dashboardId) => {
return app.dashboards.load(dashboardId);
}
const renderWidgets = (dashboard) => {
config.widgets.forEach((widgetCfg) => {
// render widget, configure your widget id and container id
dashboard.widgets.get(widgetCfg.widgetId).container =
document.getElementById(widgetCfg.containerId);
});
return refreshDashboard(dashboard);
}
const refreshDashboard = (dashboard) => {
dashboard.refresh();
return dashboard;
}
// connect to sisense
// load dashboard
// render widgets
Sisense.connect(config.sisenseUrl)
.then((app) => loadDashboard(app, config.dashboardUrl))
.then((dash) => {
// name of datasource for change
let dsName = 'Customer2';
getDataSourceInfo(app.$$http, sisenseUrl, dsName)
.then((res) => {
if (res.status === 200) {
// Change datasource for dashboard
changeDatasource(dash.$$model, res.data);
// Change datasource for filters
changeDatasourceForFilters(dash.$$model.filters, res.data);
// render widget
let w1 = dash.widgets.get('5d4744dceb3ab207c0674ce5');
// Change datasource for widget
changeDatasource(w1.$$model, res.data);
changeDatasourceForFilters(w1.filters, res.data);
w1.container = document.getElementById('w1');
}
});
return dash
})
.then(renderWidgets);
</script>
<!--
This code snippet lets you retrieve the current dashboard filters,
iterates on all of them and outputs `dimension: selected value`.
It handles filters from the following types: List type, text - contains, date - calendar.
-->
<!-- replace with your Sisense server address -->
<script type="text/javascript"
src="https://test.sisense.com/js/sisense.js"></script>
<script type = "text/javascript">
// Helper function is define filter type, reference is src/base.module/services/$naming.js
const getFilterType = (jaql) => {
if(defined(jaql) && defined(jaql.filter)){
var exclude = defined(jaql.filter.exclude);
var filter = exclude ? jaql.filter.exclude : jaql.filter;
if(defined(filter.custom) && filter.custom) return 'custom';
else if(defined(filter.members) || filter.all) return 'member';
else if(defined(filter.attributes)) return 'attr';
else if(defined(filter.top) || defined(filter.bottom)) return 'rank';
else if(
defined(filter.startsWith) ||
defined(filter.doesntStartWith) ||
defined(filter.endsWith) ||
defined(filter.doesntEndWith) ||
defined(filter.contains) ||
defined(filter.doesntContain) ||
defined(filter.equals) ||
defined(filter.doesntEqual) ||
defined(filter.toNotEqual) ||
defined(filter.to) ||
defined(filter.fromNotEqual) ||
defined(filter.and) ||
defined(filter.or) ||
defined(filter.from)
) {
return 'criteria';
}
else{
return 'custom';
}
}
else{
return 'member'; // default mode
}
};
// Output filter value depend on filter type
const getFilterValue = (jaql) => {
if (defined(jaql) && defined(jaql.filter)) {
const filterType = getFilterType(jaql)
const filter = jaql.filter;
const dataType = jaql.datatype;
switch (filterType) {
case 'criteria':
if (dataType === 'datetime') {
return `from: ${filter.from} to: ${filter.to}`;
} else {
return Object.keys(filter)[0] + ' : ' +
(
filter.startsWith ||
filter.doesntStartWith ||
filter.endsWith ||
filter.doesntEndWith ||
filter.contains ||
filter.doesntContain ||
filter.equals ||
filter.doesntEqual ||
filter.toNotEqual ||
filter.to ||
filter.fromNotEqual ||
filter.and ||
filter.or ||
filter.from
);
}
break;
case 'member':
if (filter.all) {
return 'include all';
} else {
return filter.members;
}
break;
default:
return filter;
}
} else {
return '';
}
};
// Main function is iterate over dashboard filter and output its Dimension and Values.
const getFiltersInfo = (filters) => {
filters.forEach(item => {
const filterJaql = item.jaql;
console.log(
filterJaql.dim,
':',
getFilterType(filterJaql),
':',
getFilterValue(filterJaql),
':',
filterJaql
);
});
};
// configure sisense app url and the dashboard that will be loaded
const config = {
sisenseUrl: 'https://test.sisense.com',
dashboardUrl: '6141feba14d79800357dd138',
widgets: [
{ widgetId: '624bf31be07da90036b03020', containerId: 'widget1' },
{ widgetId: '6253cea93b50e60036f411e1', containerId: 'widget2' }
]
}
const loadDashboard = (app, dashboardId) => {
return app.dashboards.load(dashboardId);
}
const renderWidgets = (dashboard) => {
config.widgets.forEach((widgetCfg) => {
// render widget, configure your widget id and container id
dashboard.widgets.get(widgetCfg.widgetId).container =
document.getElementById(widgetCfg.containerId);
});
return refreshDashboard(dashboard);
}
const renderFilters = (dashboard) => {
// render Sisense filters panel inside of container with particular id
dashboard.renderFilters(document.getElementById(config.filtersContainerId));
return refreshDashboard(dashboard);
}
const refreshDashboard = (dashboard) => {
dashboard.refresh();
return dashboard;
}
// connect to sisense
// load dashboard
// render widgets
Sisense.connect(config.sisenseUrl)
.then((app) => loadDashboard(app, config.dashboardUrl))
.then((dash) => {
// get filters
const filters = dash.filters.flatten();
// output filters settings
getFiltersInfo(filters);
return dash;
})
.then(renderWidgets);
</script>
<!--
This code snippet lets you add a widget to the dashboard
-->
<!-- replace with your Sisense server address -->
<script type="text/javascript"
src="https://test.sisense.com/js/sisense.js"></script>
<script type = "text/javascript">
let sisenseApp; // reference for Sisense Application
// a widget model to add, should correspond to model on REST API
// can be copy from existing widget.
// for reference please see https://sisense.dev/guides/customJs/jsApiRef/widgetClass/
const widgetObj = {
"title": "",
"type": "indicator",
"datasource": {"title": "child_5", "fullname":
"live:child_5", "id": "live:child_5", "live": true},
"subtype": "indicator/numeric",
"metadata": {
"panels": [
{
"name": "value",
"items": [
{
"jaql": {
"table": "msd_logs_1",
"column": "Timestamp",
"dim": "[msd_logs_1.Timestamp]",
"datatype": "numeric",
"agg": "count",
"title": "# of unique Timestamp"
},
"format": {
"mask": {
"type": "number",
"abbreviations": {"t": true, "b": true, "m": true,
"k": false},
"separated": true,
"decimals": "auto",
"isdefault": true
}, "color": {"color": "#00cee6", "type": "color"}
}
}
]
},
{
"name": "secondary",
"items": []
},
{
"name": "min",
"items": []
},
{
"name": "max",
"items": []
},
{
"name": "filters",
"items": []
}
]
}
};
// configure sisense app url and the dashboard that will be loaded
const config = {
sisenseUrl: 'https://test.sisense.com',
dashboardId: '624bf2cae07da90036b0301d',
widgets: [
{ widgetId: '624bf31be07da90036b03020', containerId: 'widget1' },
{ widgetId: '6253cea93b50e60036f411e1', containerId: 'widget2' }
]
}
const loadDashboard = () => {
return sisenseApp.dashboards.load(config.dashboardId);
}
const refreshDashboard = (dashboard) => {
dashboard.refresh();
return dashboard;
}
// adding widget to dashboard
// to run API call user must have admin or designer role
const addWidgetAPI = () => {
// get http service
const $http = prism.$injector.get('$http');
// url for corresponding API endpoint
const url = `${config.sisenseUrl}/api/dashboards/${config.dashboardId}/widgets`;
return $http.post(url, widgetObj)
.then((response) => {
if (response.status === 200) {
return response.data.oid
}
});
}
const renderNewWidget = (widgetId) => {
return loadDashboard().then((dashboard) => {
dashboard.widgets.get(widgetId).container = document.getElementById('newWidget');
return refreshDashboard(dashboard);
});
}
// connect to sisense
// load dashboard
// render widgets
Sisense.connect(config.sisenseUrl)
.then((app) => {
sisenseApp = app;
return sisenseApp;
})
.then(addWidgetAPI)
.then(renderNewWidget)
</script>
<!--
This code snippet calls an API when a dashboard is loaded.
-->
<!-- replace with your Sisense server address -->
<script type="text/javascript"
src="https://test.sisense.com/js/sisense.js"></script>
<script type = "text/javascript">
// calling API, for example get dashboards
const getDashboardsAPI = ($http) => {
// API url
const url = `${config.sisenseUrl}/api/v1/dashboards?fields=oid,title`;
return $http.get(url)
.then(response => {
if (response.status === 200) {
console.log(response.data);
}
})
.catch((error) => console.log(error.message));
}
// configure sisense app url and the dashboard that will be loaded
const config = {
sisenseUrl: 'https://test.sisense.com',
dashboardUrl: '6141feba14d79800357dd138',
widgets: [
{ widgetId: '624bf31be07da90036b03020', containerId: 'widget1' },
{ widgetId: '6253cea93b50e60036f411e1', containerId: 'widget2' }
]
}
const loadDashboard = (app, dashboardId) => {
return app.dashboards.load(dashboardId);
}
const renderWidgets = (dashboard) => {
config.widgets.forEach((widgetCfg) => {
// render widget, configure your widget id and container id
dashboard.widgets.get(widgetCfg.widgetId).container =
document.getElementById(widgetCfg.containerId);
});
return refreshDashboard(dashboard);
}
const refreshDashboard = (dashboard) => {
dashboard.refresh();
return dashboard;
}
// connect to sisense
// load dashboard
// render widgets
Sisense.connect(config.sisenseUrl)
.then((app) => {
// get http service
const $http = prism.$injector.get('$http');
getDashboardsAPI($http);
return loadDashboard(app, config.dashboardUrl)
})
.then(renderWidgets);
</script>
<!--
This code snippet lets you take action when filters are changed.
-->
<!-- replace with your Sisense server address -->
<script type="text/javascript"
src="https://test.sisense.com/js/sisense.js"></script>
<script type = "text/javascript">
let sisenseApp; // reference for Sisense Application
// Event handler for dashboard 'filterchanged' event
const onFilterChangedHandler = (event, args) => {
// arguments contains dashboard object, change type (add|update|remove), filter items
const { dashboard, type, items } = args;
console.log('filter changed', items, type);
}
// configure sisense app url and the dashboard that will be loaded
const config = {
sisenseUrl: 'https://test.sisense.com',
dashboardUrl: '6141feba14d79800357dd138',
widgets: [
{ widgetId: '624bf31be07da90036b03020', containerId: 'widget1' },
{ widgetId: '6253cea93b50e60036f411e1', containerId: 'widget2' }
],
filtersContainerId: 'filters'
}
const loadDashboard = () => {
return sisenseApp.dashboards.load(config.dashboardId);
}
const renderWidgets = (dashboard) => {
config.widgets.forEach((widgetCfg) => {
// render widget, configure your widget id and container id
dashboard.widgets.get(widgetCfg.widgetId).container =
document.getElementById(widgetCfg.containerId);
});
return refreshDashboard(dashboard);
}
const renderFilters = (dashboard) => {
// render Sisense filters panel inside of container with particular id
dashboard.renderFilters(document.getElementById(config.filtersContainerId));
return refreshDashboard(dashboard);
}
const refreshDashboard = (dashboard) => {
dashboard.refresh();
return dashboard;
}
// connect to sisense
// load dashboard
// render widgets
Sisense.connect(config.sisenseUrl)
.then((app) => {
sisenseApp = app;
return sisenseApp;
})
.then(loadDashboard)
.then((dash) => {
// subscribe on 'filterchanged' dashboard event
dash.on('filterchanged', onFilterChangedHandler);
return dash;
})
.then(renderWidgets)
.then(renderFilters);
</script>
<!--
This code snippet lets you change the dashboard layout based on a setting in the container
application.
-->
// layout object can be copy fron current dashboard layout `prism.activeDashboard.layout`
// for reference see https://sisense.dev/guides/customJs/jsApiRef/dashboardClass/dashboard-layout.html
const newLayout = {
"layout": {
"instanceid": "AB1AE-77A3-B4",
"type": "columnar",
"columns": [{
"width": 50,
"cells": [{
"subcells": [{
"elements": [{
"minHeight": 128,
"maxHeight": 2048,
"minWidth": 128,
"maxWidth": 2048,
"height": "178px",
"defaultWidth": 512,
"widgetid": "624bf31be07da90036b03020",
"autoHeight": "178px"
}], "width": 100, "stretchable": false, "pxlWidth": 715,
"index": 0
}]
}, {
"subcells": [{
"elements": [{
"minHeight": 102,
"maxHeight": 1024,
"minWidth": 128,
"maxWidth": 2048,
"height": 512,
"defaultWidth": 512,
"widgetid": "624eeb0430b5b1003630aa9c"
}], "index": 0, "stretchable": false, "width": 100,
"pxlWidth": 715
}]
}],
"pxlWidth": 715,
"index": 0
}, {"width": 50}],
"container": {}
}
}
//const changeDashboardLayout = (dashboardId, newLayout) => {
const changeDashboardLayout = (newLayout) => {
console.log('changing layout...');
// dashboard id and dashboard service
const {oid, $dashboard} = prism.activeDashboard;
$dashboard.updateDashboard(oid, newLayout)
.then(response => {
if (response.status === 200) {
console.log('layot changed!');
location.reload();
}
})
.catch((error) => console.log(error.message));
}
const main = () => {
// add changeDashboardLayout function to the prism global object
// it can be used from external application
prism.changeDashboardLayout = changeDashboardLayout;
}
main();
// in container application
prism.changeDashboardLayout(prism.activeDashboard.oid, newLayout);
Customizing Widget UI and Behavior
<!--
This code snippet lets you change the look and feel of an existing widget.
This example is relevant for Highcharts widgets (line, column, bar, pie ... etc)
Highcharts view can be changed in a 'beforeviewloaded' widget event.
This event has an 'options' argument that represents options of the Highcharts component.
For reference please look https://api.highcharts.com/highcharts/
for customizing indicator widgets see https://sisense.dev/guides/customJs/jsApiRef/widgetClass/indicator.html
for customizing Pivot widget see https://sisense.dev/guides/customJs/jsApiRef/widgetClass/pivot2.html
-->
<!-- replace with your Sisense server address -->
<script type="text/javascript"
src="https://test.sisense.com/js/sisense.js"></script>
<script type = "text/javascript">
let sisenseApp; // reference for Sisense Application
const onBeforeViewLoadedHandler = (event, args) => {
const {
widget, // Widget instance.
element, // Element container
options // for chart - final Highcharts options, for scattermap - Object that contains the map
instance and the array of markers
} = args;
// change color of legend text on a chart
options.legend.itemStyle.color = '#cc0000';
// adding rounded corners to the columns
options.plotOptions.series.borderRadius = '5px';
// for another options see reference https://api.highcharts.com/highcharts/
};
// configure sisense app url and the dashboard that will be loaded
const config = {
sisenseUrl: 'https://test.sisense.com',
dashboardId: '624bf2cae07da90036b0301d',
widgets: [
{ widgetId: '624bf31be07da90036b03020', containerId: 'widget1' },
{ widgetId: '6253cea93b50e60036f411e1', containerId: 'widget2' }
]
}
const loadDashboard = () => {
return sisenseApp.dashboards.load(config.dashboardId);
}
const customizeWidgets = (dashboard) => {
// when dashboard loaded, iterate widget and attach onBeforeViewLoaded handler
const widgets = dashboard.widgets.$$widgets;
widgets.forEach((widget) => {
// we will apply changes only to column charts
if(widget.type === "chart/column"){
widget.on('beforeviewloaded', onBeforeViewLoadedHandler);
}
})
return dashboard;
}
const renderWidgets = (dashboard) => {
config.widgets.forEach((widgetCfg) => {
// render widget, configure your widget id and container id
dashboard.widgets.get(widgetCfg.widgetId).container =
document.getElementById(widgetCfg.containerId);
});
return refreshDashboard(dashboard);
}
const refreshDashboard = (dashboard) => {
dashboard.refresh();
return dashboard;
}
// connect to sisense
// load dashboard
// render widgets
Sisense.connect(config.sisenseUrl)
.then((app) => {
sisenseApp = app;
return sisenseApp;
})
.then(loadDashboard)
.then(customizeWidgets) // change widgets look and feel
.then(renderWidgets)
</script>