Customizing Sisense using JavaScript
  • 18 May 2022
  • 11 Minutes to read
  • Contributors
  • Dark
    Light
  • PDF

Customizing Sisense using JavaScript

  • Dark
    Light
  • PDF

Here are some examples for customizing Sisense using JavaScript.

Click each one to view the code.

Customizing Dashboard UI, Filters and Behavior

Add dashboard filters
<!--
   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>
Change a dashboard's data source
<!--
  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>
Retrieve current dashboard filters
<!--
    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>
Add a widget to the dashboard
<!--
  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 look https://sisense.dev/reference/js/widget/
    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>
Call an API when a dashboard is loaded
<!--
  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>
Handle filter changes
<!--
 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>

Change dashboard layout
<!--
   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/reference/js/dashboard/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

Change the look and feel of an existing widget
<!--
    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 look https://sisense.dev/reference/js/widget/indicator.html
    for customizing Pivot widget look https://sisense.dev/reference/js/widget/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>


Was this article helpful?