Auto zoom Area Map widget to a specific country on load (Linux)
Auto zoom Area Map widget to a specific country on load To test this solution, you must create a dashboard based on Sample ECommerce and an Area Map widget based on a Country column. Once it is done - please go to the widget editor, click three dots select Edit Script, then paste the following script there: As a result, we have our Area Map widget zoomed to Germany on the initial load: const countryName = 'Germany' widget.on('processresult', (scope, args)=>{ saveMapObj() }) widget.on('domready', (scope, args)=>{ zoomToCountry(L.geoJson(widget.queryResult.geoJson), window.currentMap, countryName); }) function zoomToCountry(geoData, map, countryName) { // Create a new empty GeoJSON object to store only the selected country's feature(s) geoData.eachLayer(function (layer) { if (layer.feature.properties.name === countryName) { map.fitBounds(layer.getBounds()); // Zoom to country bounds } }) } function saveMapObj() { let leafletHookStatus = $$get(prism, 'autoZoomAreaMap.leaflet.initHookAdded') if(!leafletHookStatus) { L.Map.addInitHook(function () { window.currentMap = this }) $$set(prism, 'autoZoomAreaMap.leaflet.initHookAdded', true); } } [ALT Text: A map of Europe with colored regions indicating specific areas. Central Europe is highlighted in teal, while other areas appear in gray. The United Kingdom is visible to the northwest. The date displayed at the top indicates June 10, 2025.] DO NOT CHANGE!!! Disclaimer: This post outlines a potential custom workaround for a specific use case or provides instructions regarding a specific task. The solution may not work in all scenarios or Sisense versions, so we strongly recommend testing it in your environment before deployment. If you need further assistance with this please let us know.613Views1like0CommentsHide vertical lines on the Pivot widget (Linux)
Hide vertical lines on the Pivot widget To test this solution, you must create a dashboard based on any cube and create any Pivot widget. Once it is done - please go to the widget editor, click three dots select Edit Script, then paste the following script there: const howManyRowsToIgnore = 2; widget.on('ready', () => { const cells = element[0].querySelectorAll('.table-grid__cell'); let maxColIndex = 0; cells.forEach(cell => { const match = cell.className.match(/table-grid__cell--col-(\d+)/); if (match) { const colIndex = parseInt(match[1], 10); if (colIndex > maxColIndex) { maxColIndex = colIndex; } } }); cells.forEach(cell => { const colMatch = cell.className.match(/table-grid__cell--col-(\d+)/); const rowMatch = cell.className.match(/table-grid__cell--row-(\d+)/); if (colMatch && rowMatch) { const colIndex = parseInt(colMatch[1], 10); const rowIndex = parseInt(rowMatch[1], 10); if (rowIndex >= howManyRowsToIgnore) { if (colIndex !== 0) { cell.style.borderLeft = 'none'; } if (colIndex !== maxColIndex) { cell.style.borderRight = 'none'; } } } }); }); Please note there is a howManyRowsToIgnore parameter that specifies how many first rows to ignore. It is done to have the possibility to leave vertical lines for header values if needed. If you need to hide all vertical lines including header-related lines as well - just set this value to 0. As a result, we have a Pivot widget with hidden vertical lines: [ALT Text: A table titled "Budget" displaying a list of brands along with their corresponding total costs. The first entry, "ABC," shows a total cost of $3,564,045.86. The subsequent entries include brands such as "Addimax," "Adderax," and "Addulator Inc," along with their respective total costs, ranging from around $100 to several million. The table spans multiple rows and includes a total of 2094 entries. The header indicates columns for "Brand" and "Total Cost."] Disclaimer: This post outlines a potential custom workaround for a specific use case or provides instructions regarding a specific task. The solution may not work in all scenarios or Sisense versions, so we strongly recommend testing it in your environment before deployment. If you need further assistance with this please let us know.307Views1like0CommentsConverting an Existing Sisense Widget to a Dynamic ComposeSDK Widget Component
Using either the widget Sisense API's, or the Sisense widget JS API object, it is possible to determine all components and parameters of a widget required to create an equivalent dynamic ComposeSDK widget component. This approach allows the extraction of all components and parameters of a widget, enabling the creation of an equivalent dynamic ComposeSDK widget component without directly referencing or relying on a specific widget or dashboard ID. The metadata of an existing widget contains all the information needed to create a dynamic version of an existing Sisense widget. It is also possible to use an existing widget ID and dashboard ID to render an existing widget in ComposeSDK, but this does not take full advantage of the capabilities of ComposeSDK to generate new widgets directly.2.3KViews2likes1CommentViewing Widget JAQL Queries
Viewing Widget JAQL Queries There are a large number of reasons you may want to view the query(ies) being sent from a dashboard widget, most of which are performance or data quality related. This article explains two methods that can be used independently of each other or in concert to assure all perspectives of the issue are understood. A query executed from a dashboard widget against an Elasticube can be captured using any HTML5 browser console (or headless browser), or by calling the Sisense REST API. Queries against an Elasticube are in JAQL for JSON query processing on big data. Sisense provides a complete JAQL Syntax Reference of the properties and options in the query which define the result set. Using any HTML5 browser console The JAQL query can be captured by using the developer tools available in HTML5 browser consoles. This is easy to set up and gives a better context (you see all HTTP requests on the page) than the REST API approach. If the page is not available or you are focusing on a specific widget query, obtaining the JAQL query via API may be more appropriate. To capture the query, create a HAR file following this process. You will need this file if you contact Sisense Support. The developer tools will capture everything that happens in your browser session rather than just the JAQL query for one specific widget. This will provide context if you are analyzing an overall dashboard issue. It is a best practice to capture the HAR file even if you are focusing on a specific widget due to the context it provides. Using a REST API call The JAQL query for a specific widget can be obtained by using the Sisense REST API.This provides focus and allows you to obtain the code even when the dashboard may not be available (e.g. Page fails before executing the request.) If the dashboard is available it is best practice to capture the HAR file (see above) to provide context while using the API call to provide focus. To obtain the query, prepare a response to retrieve the correct JAQL. Below you can find the JavaScript code to prepare the payload. Use the value of parameter [payload] as body to send API requests at /elasticubes/{elasticube}/jaql. var dashboardId = '5b2bc0c85c523d24c48c3e36'; var widgetId = '5b2bc0ee5c523d24c48c3e4f'; var elastiCubeName = 'Sample Lead Generation'; //Get widget's structure var widgetStructure = sendAPIRequest('GET', '/api/v1/dashboards/'+dashboardId+'/widgets/'+widgetId, ''); //Prepare response to use it in next API request var currectStructure = prepareResponse(widgetStructure) //Send request to server //var jaqlResponse = sendAPIRequest('POST', '/api/datasources/'+elastiCubeName+'/jaql', currectStructure); /*optional*/ //process request processRequest(jaqlResponse); function prepareResponse(a) { var result = []; for (i=0; i<a.metadata.panels.length; i++) { for (j=0; j<a.metadata.panels[i].items.length; j++) { if (a.metadata.panels[i].items[j]) { result.push(a.metadata.panels[i].items[j].jaql); }; } }; var payload = {metadata: result}; payload = JSON.stringify(payload); return payload; }; function sendAPIRequest(method, url, data) { var response = $.ajax({ method: method, url: url, async: false, data: data, contentType: 'application/json'}).responseJSON; return response; } function processRequest(jaqlResponse) { //console.log(jaqlResponse) /*optional if using the Send request to server*/ console.log(currectStructure) Conclusion If you need additional help, please contact Sisense Support.2.5KViews0likes0CommentsSetting up Docker Registry for Sisense Offline Installation
An offline, or air-gapped, Sisense environment provides higher security than online, connected environments. As the offline environment has no outside communication, the only method to install Sisense in this environment is by using removable media, such as USB drives. The system must have the following in place to complete an offline installation: A Bastion host with Docker installed (Recommended) A secured Docker registry that is accessible to the offline environment The Registry is a stateless, highly scalable server side application that stores and lets you distribute Docker images. In case of Sisense offline installation Docker Registry is used to distribute the Sisense images within an isolated network. Next article provides steps on how to install and configure the Docker registry.4.2KViews1like3CommentsRetrieve All Widget IDs, Titles And Types In A Dashboard
1) Open the Developers Console (press F12 in Chrome.) 2) Navigate to Console tab: 3) In the console prompt line, enter the following script and press Enter: 4) prism.activeDashboard.widgets.toArray() 5) This results in a printout of an object with the widget ID, title and data type:3.3KViews2likes0CommentsHow To Order A Stacked Column Chart Using A BreakBy
Introduction When adding a break by field to an ordered column chart, the order does not exist anymore. We have created this post in order to workaround this problem. Steps In order to use the script, you need to follow the next steps: 1. Edit the widget 2. In the widget Editor, click on the widget menu button and choose Edit Script (this will open a new page for the script editing) 3. In the widget Editor window paste the following script: var categories= ["value desc"]; //Manual order\"value asc"\"Value desc" var breakBy = [ ]; // ---------- DO NOT EDIT BELOW THIS LINE ---------- var findIndex = function(val, list) { if (list=='breakby') { return breakBy.indexOf(val) } else if (list=='categories') { return categories.indexOf(val) } }; widget.on('processresult',function(se,ev){ //If categories array contains "value asc" or "value desc", sort categories by their total values. //The sorted categories will be placed in the category array used by the manual sorting script if (categories.length>0&&categories[0] === "value desc"){ categories = sortColumnsByValue(ev,"desc"); } else if (categories.length>0&&categories[0] === "value asc"){ categories = sortColumnsByValue(ev,"asc"); }; //determine if we should sort categories or breakbys categoryStatus=false; breakStatus=false; for (p=0; p<ev.widget.metadata.panels.length; p++){ if (ev.widget.metadata.panels[p].name=="categories" && ev.widget.metadata.panels[p].items.length>0 && categories.length>0){ categoryStatus=true; } else if (ev.widget.metadata.panels[p].name=="break by" && ev.widget.metadata.panels[p].items.length>0 && breakBy.length>0){ breakStatus=true; } }; if (categoryStatus){ ev.result.series.forEach( function(bb) { newData= new Array(categories.length); var blankcat; bb.data.forEach( function(cat) { if (typeof cat.selectionData !== 'undefined'){ newData[findIndex(cat.selectionData[0].trim(), 'categories')]=cat; } else{ blankcat=cat; } }) for (i=0; i<newData.length; i++) { if (typeof newData[i] === 'undefined') { newData[i]=blankcat; } } bb.data=newData; }); ev.result.xAxis.categories=categories; }; if (breakStatus) { ev.result.series.sort(function(a,b){ if (findIndex(a.name, 'breakby') < findIndex(b.name,'breakby')) { return -1 } else if (findIndex(a.name,'breakby') > findIndex(b.name,'breakby')) { return 1 } return 0; }); }; }); //Sort categories by total value: functions //Sort ascending \ descending function sortCatsByValueAsc(a,b){ var aValue = a.value; var bValue = b.value; return ((aValue < bValue) ? -1 : ((aValue > bValue) ? 1 : 0)); } function sortCatsByValueDesc(a,b){ var aValue = a.value; var bValue = b.value; return ((aValue < bValue) ? 1 : ((aValue > bValue) ? -1 : 0)); } //replace "category" object with the category name function sortedCatsNames(arr){ for (var i=0 ; i<arr.length ; i++){ arr[i] = arr[i].name; } } //Main function function sortColumnsByValue (arg, sorting){ //Create an array to contain all categories' original index, name and value var cats = []; for (var c=0 ; c<arg.result.xAxis.categories.length ; c++){ cats.push({index:c ,name: arg.result.xAxis.categories[c],value:0}); } for (var i=0; i<arg.result.series.length; i++){ for (var j=0 ; j<arg.result.series[i].data.length;j++){ cats[j].value+=arg.result.series[i].data[j].y; } } //Sort Array by category value var sortCats; if (sorting==="desc"){ sortCats = cats.sort(sortCatsByValueDesc); } else { sortCats = cats.sort(sortCatsByValueAsc); } //Replace objects with the category name sortedCatsNames(sortCats); return sortCats; } ***All script rights reserved to Adi Lewenstein Script Modifications You can change the order type between Ascending (value asc) and Descending (value desc) in the first row of the script var categories= ["value desc"]; //Manual order\"value asc"\"Value desc" 4. click on Save and close the window 5. Go back to the widget Editor page (Should still be open) and refresh the browser ( The script should already affect the widget ) 6. Click apply ***Sisense orders the values in an alphabetical order, If you wish to order the values in a different order, create a new column and add a number at the beginning of each value according to the order you want them to appear.2.1KViews1like0Comments