Sisense Community logo
     
    • Community Feedback
    • Chapters
    • Events
    • Forums
      • Help and How To
      • Product Feedback Forum
      • Strategy & Use Cases
    • Blogs
    • KB Docs
      • KB Docs
      • Add-Ons & Plug-Ins
      • APIs
      • Best Practices
      • Blox
      • CDT
      • Cloud Managed Service
      • Data Models
      • Data Sources
      • Embedding Analytics
      • How-Tos & FAQs
      • Onboarding
      • PySisense
      • Security
      • Sisense Administration
      • Sisense Intelligence & AI
      • Troubleshooting
      • Widget & Dashboard Scripts
    • Support
    • Learning
      • Sisense Academy: Free Courses and Certifications
      • Official Developer Documentation
      • Official Product Documentation
      • Official Sisense Youtube Channel
      • Sisense Compose SDK Playground
      • Official Sisense Discord
    • Use Case Gallery
    •      
    Discussions
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    Discussions
    • HomeChevronRightIcon
    • All postsChevronRightIcon
    • Knowledge Base DocsChevronRightIcon
    Widget & Dashboard Scripts

    Created Apr 15, 2026

    6 members

    215 discussions

    Widget & Dashboard Scripts
                       
                                       
      • Widget & Dashboard ScriptsChevronRightIcon

      How to change pivot cells font-size and style [Linux]

      Introduction This article explains how to change the font size and style of cells in a Sisense Pivot Table widget using the official Pivot 2.0 API (widget.transformPivot). A common approach found in community posts relies on jQuery class selectors such as .p-head-content and .p-value to apply CSS changes directly to the DOM. While this may work in older versions, it is not officially supported and is prone to breaking across Sisense updates, since class names and UI structure can change between releases. The recommended and supported approach is to use the widget.transformPivot API, which provides a stable, version-resilient way to style pivot cells programmatically. The solution is tested on Sisense L2026.1.2 and works for both cloud and on-premises deployments. Step-by-Step Guide 1. Open the widget script editor Click the three-dot menu on your Pivot Table widget → Edit Script . 2. Identify the cell types you want to style The transformPivot API allows you to target specific cell types within the pivot: You can target one, several, or all types in a single call. 3. Paste the script widget.transformPivot( { type: ['value', 'member', 'subtotal', 'grandtotal'] }, function(metadata, cell) { cell.style = cell.style || {}; cell.style.fontSize = '8px'; } ); 4. Customize the style At the core of the script, the cell.style object accepts standard CSS properties in camelCase format. You can extend it to apply additional styles beyond font size. For example: widget.transformPivot( { type: ['value', 'member', 'subtotal', 'grandtotal'] }, function(metadata, cell) { cell.style = cell.style || {}; cell.style.fontSize = '8px'; cell.style.fontWeight = 'bold'; cell.style.color = '#333333'; cell.style.fontFamily = 'Arial, sans-serif'; } ); 5. Click Apply and test Save the script and reload the widget. All targeted cell types will now reflect the styles you defined. Important notes Why not use jQuery class selectors? Scripts that rely on class names like .p-head-content or .p-value are tied to the internal DOM structure of the widget, which Sisense does not guarantee to remain stable across versions. These scripts may stop working after an upgrade without any warning. Pivot 2.0 API only: The widget.transformPivot method is part of the Pivot 2.0 API. Ensure your widget is using Pivot 2.0 (not the legacy Pivot widget) for this script to work. Cell types: If you only want to style specific cell types (e.g. only data values), simply remove the unwanted types from the array. For example, { type: ['value'] } will only affect data cells. Conclusion By using the widget.transformPivot API instead of direct DOM manipulation, you get a supported, upgrade-safe way to control the visual appearance of pivot cells. The cell.style object accepts any standard CSS property in camelCase, making it straightforward to extend this pattern to font family, weight, colour, and more. References / Related content Pivot 2.0 API - transformPivot 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      Date Proximity Row Highlighting in Sisense Pivot Table Widgets [Linux]

      Introduction This article demonstrates how to automatically highlight rows in a Sisense Pivot Table widget based on how close a date column is to today's date. The script developed here adds visual urgency to your pivots by colour-coding each row based on the number of days remaining until the date in a specified column: Light green for rows where the deadline has already passed (expired). Light red for rows where the deadline is critically close (urgent). Light yellow for rows where the deadline is approaching within a configurable warning window. No highlight for rows where the deadline is comfortably in the future. This is particularly useful for dashboards tracking contract renewals, project deadlines, task due dates, or any time-sensitive data where users need to spot at-risk rows at a glance. The solution is tested on Sisense L2026.1.2 and works for both cloud and on-premises deployments. Step-by-Step Guide 1. Prepare your environment Open an existing dashboard that contains a Pivot Table widget, or create a new one. Make sure your table includes at least one column that contains date values. The script works best with dates formatted as mm/dd/yyyy or yyyy-mm-dd, which are the standard Sisense table output formats. You can use attached .dash and .sdata for testing. 2. Identify your date column index The script uses a 0-based column index to locate the date column. This means the first column is index 0, the second is 1, the third is 2, and so on. Count the columns in your table from left to right and note the position of your date column. You will use this number in the configuration section of the script. 3. Open the widget script editor Click the three-dot menu on the Pivot Table widget → Edit Script . 4. Paste the full script /** * Date Proximity Row Highlighting * * Compares a specific date column against today's date and paints the row * yellow (if the deadline is approaching) or red (if past due). */ // --- CONFIGURATION --- const DATE_COLUMN_INDEX = 2; // 0-based index of your date column (e.g., 2 is the 3rd column) const DAYS_WARNING = 14; // Highlight yellow if the deadline is within this many days const DAYS_URGENT = 5; // Highlight red if the deadline is urgent (within this many days) const EXPIRED_COLOR = '#d4edda'; // Light Green for past-due/expired deadlines const URGENT_COLOR = '#f8d7da'; // Light Red for urgent deadlines const WARNING_COLOR = '#fff3cd'; // Light Yellow for approaching deadlines widget.on('domready', function () { // Scope our DOM search to this specific widget only const $widget = $(element); // Find all data rows in the table body $widget.find('table tbody tr').each(function () { const $row = $(this); const $cells = $row.find('td'); // Safety check: ensure the row actually has our date column if ($cells.length <= DATE_COLUMN_INDEX) return; // Extract the text of the date column const dateText = $cells.eq(DATE_COLUMN_INDEX).text().trim(); // Skip empty rows or standard 'Grand Total' style texts if (!dateText || dateText.toLowerCase().includes('total')) return; // Parse the date (Works best with mm/dd/yyyy or yyyy-mm-dd Sisense formats) const rowDate = new Date(dateText); // Only proceed if it is a valid date if (!isNaN(rowDate.getTime())) { const today = new Date(); // Strip the time to compare pure calendar dates today.setHours(0, 0, 0, 0); rowDate.setHours(0, 0, 0, 0); // Calculate the difference in milliseconds and convert to days const diffTimeMilli = rowDate.getTime() - today.getTime(); const diffDays = Math.ceil(diffTimeMilli / (1000 * 60 * 60 * 24)); // Apply color to all cells in the row to guarantee the background isn't obscured if (diffDays < 0) { // Deadline is in the past! (Expired) -> Light Green $cells.css('background-color', EXPIRED_COLOR); } else if (diffDays <= DAYS_URGENT) { // Deadline is extremely close! (Urgent) -> Light Red $cells.css('background-color', URGENT_COLOR); } else if (diffDays <= DAYS_WARNING) { // Deadline is approaching within our warning threshold! -> Light Yellow $cells.css('background-color', WARNING_COLOR); } else { // Clear out the color if it's safe $cells.css('background-color', ''); } } }); }); 5. Configure the script constants At the top of the script, update the configuration constants to match your table and your business rules: const DATE_COLUMN_INDEX = 2;     // 0-based index of your date column (e.g., 2 = 3rd column) const DAYS_WARNING = 14;          // Highlight yellow if deadline is within this many days const DAYS_URGENT = 5;            // Highlight red if deadline is within this many days const EXPIRED_COLOR = '#d4edda'; // Light Green for past-due/expired rows const URGENT_COLOR = '#f8d7da';  // Light Red for urgent rows const WARNING_COLOR = '#fff3cd'; // Light Yellow for approaching rows DATE_COLUMN_INDEX – Set this to the 0-based position of your date column. DAYS_WARNING – Any row whose date is within this many days from today will be highlighted yellow. The default is 14 days. DAYS_URGENT – Any row whose date is within this many days from today will be highlighted red. The default is 5 days. This threshold takes priority over the warning threshold. Colour constants – The default colours follow a standard traffic-light convention. You can replace any hex value with your organisation's brand colours. 6. Click Apply and test Save the script and reload the widget. Rows containing dates will now be colour-coded automatically based on their proximity to today. Rows with dates safely in the future will have no background colour change. Important notes Date format compatibility: The script relies on JavaScript's native new Date() parser. This works reliably with mm/dd/yyyy and yyyy-mm-dd formats. If your Sisense instance is configured to display dates in a different format (e.g. dd/mm/yyyy), the parser may misinterpret the values. In that case, you will need to add a custom date parsing step before the new Date(dateText) call. Pagination: The script runs on domready, which fires each time the table re-renders – including after a page change. This means the highlighting is automatically reapplied when the user navigates to a different page of results. Grand Total rows: The script automatically skips any row whose date cell contains the word "total", preventing the Grand Total row from being incorrectly highlighted. Conclusion By hooking into the domready event and reading the rendered cell values directly from the DOM, this script adds a real-time, date-aware traffic-light system to any Sisense Pivot Table widget – with no changes required to the underlying data model. The configuration constants at the top of the script make it straightforward to adapt the column position, day thresholds, and colours to any use case. The same pattern can be extended to highlight rows based on other conditions, such as numeric thresholds, status text values, or combinations of multiple columns and conditions. References / Related content Sisense JavaScript API Reference – Widget Events 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      Custom Tooltip for Bar, Column, Line & Area Charts [Linux]

      Introduction This article demonstrates how to create a fully custom HTML tooltip for Bar, Column, Line, and Area chart widgets in Sisense. By default, Sisense's native tooltip shows only the hovered data point's value and series name. This script goes further by embedding three advanced analytics utilities directly inside the tooltip: Pulling hidden data from the raw query result (e.g. a "Target" column that is not plotted on the chart). Calculating % of Series Total on the fly, so users can instantly see each bar's share of the whole. Calculating the difference from the previous bar (period-over-period growth), with conditional colour formatting. This is ideal for executive dashboards, sales performance charts, or any scenario where users need richer context without leaving the chart. The solution is tested on Sisense 2026.1.2 and works for both cloud and on-premise deployments. Step-by-Step Guide 1. Prepare your environment Open an existing dashboard that contains a Bar, Column, Line, or Area Widget, or create a new one. Make sure your data panel includes: At least one dimension (e.g. Month, Region) on the X-axis. At least one measure (e.g. Revenue, Sales) as the plotted value. Optionally, an unplotted column in your data panel (e.g. "Target") that you want to surface inside the tooltip. This column does not need to be visible on the chart – it just needs to be present in the data panel. 2. Open the widget script editor Click the three-dot menu on the widget → Edit Script . 3. Configure the script flags At the top of the script, two configuration flags control the tooltip behaviour: const HIDE_NATIVE_SISENSE_TOOLTIP = true; const HIDE_ALL_TOOLTIPS = false; Set HIDE_NATIVE_SISENSE_TOOLTIP = true to completely replace Sisense's default tooltip with the custom one. Set it to false if you want both to coexist. Set HIDE_ALL_TOOLTIPS = true only if you want to suppress all tooltips entirely (custom and native) for some reason, and feel free to define a custom condition for this to be true.  4. Paste the full script: /** * Script: Custom Tooltip for Bar & Column Charts * * Demonstrates 3 Utilities: * 1. Pulling hidden data from the raw query (e.g., "Target" column) * 2. Calculating % of Series Total on the fly * 3. Calculating Difference from Previous Bar (Period-over-Period growth) */ // --- CONFIGURATION --- // Set to TRUE to completely overwrite Sisense's native point events // and hide the default Sisense tooltip, ensuring ONLY the custom tooltip below shows. // Set to FALSE to keep Sisense's native events alongside the custom tooltip. const HIDE_NATIVE_SISENSE_TOOLTIP = true; // Set to true if you want to completely hide ALL tooltips (default or custom) when hovering. const HIDE_ALL_TOOLTIPS = false; widget.on('processresult', function (w, args) { if (!args.rawResult || !args.rawResult.values) return; // Optional: Completely disable tooltips if the flag is true if (HIDE_ALL_TOOLTIPS) { if (!args.result.tooltip) args.result.tooltip = {}; args.result.tooltip.enabled = false; args.result.plotOptions = args.result.plotOptions || {}; args.result.plotOptions.series = args.result.plotOptions.series || {}; args.result.plotOptions.series.states = { hover: { enabled: false } }; return; } // UTILITY 1: GET HIDDEN DATA // Get the column indices from the raw query results. // Replace "Target" with an actual unplotted column name in your data panel. const headers = args.rawResult.headers || []; const targetColIndex = headers.indexOf("Target"); // Loop through each data point in the chart's series and attach the raw results metadata let rowIndex = 0; args.result.series.forEach(series => { series.data = series.data.map(point => { return (typeof point === "object" && point !== null) ? Object.assign({}, point) : { y: point }; }); series.data.forEach(point => { const row = args.rawResult.values[rowIndex]; rowIndex++; if (row) { // Attach custom target value to the 'point' object point.customTarget = targetColIndex !== -1 ? parseFloat(row[targetColIndex].data) : null; } }); }); // Step 3: Enable and configure styling for the basic tooltip wrapper if (!args.result.tooltip) { args.result.tooltip = {}; } args.result.tooltip.enabled = true; args.result.tooltip.useHTML = true; args.result.tooltip.shared = false; args.result.tooltip.backgroundColor = '#ffffff'; args.result.tooltip.borderColor = '#cccccc'; args.result.tooltip.borderRadius = 8; args.result.tooltip.borderWidth = 1; args.result.tooltip.style = { color: '#333', fontSize: '13px', fontFamily: 'Arial, sans-serif' }; // Step 4: Define the custom HTML layout returning function args.result.tooltip.formatter = function () { var category = this.x; var value = this.y; var color = this.color; var seriesName = this.series.name; // UTILITY 2: CALCULATE % OF TOTAL // Find the total sum of this specific series var seriesTotal = 0; this.series.data.forEach(function(p) { if (p.y) seriesTotal += p.y; }); var percentOfTotal = seriesTotal > 0 ? ((value / seriesTotal) * 100).toFixed(1) + '%' : 'N/A'; // UTILITY 3: DIFFERENCE FROM PREVIOUS BAR (e.g. Month over Month) var currentIndex = this.point.index; var diffHtml = '<span style="color: #999;">N/A (First Data Point)</span>'; if (currentIndex > 0) { var prevValue = this.series.data[currentIndex - 1].y; var diffValue = value - prevValue; var diffPercent = prevValue !== 0 ? ((diffValue / prevValue) * 100).toFixed(1) : 0; // Conditional Formatting for the difference var isPositive = diffValue >= 0; var diffColor = isPositive ? '#28a745' : '#dc3545'; // Green for growth, Red for decline var diffIcon = isPositive ? '▲' : '▼'; diffHtml = `<span style="color: ${diffColor}; font-weight: bold;"> ${diffIcon} ${Math.abs(diffValue).toLocaleString()} (${diffPercent}%) </span> <span style="color: #666; font-size: 11px;">vs Previous</span>`; } // Build HTML strictly. var html = `<div style="padding: 12px; min-width: 200px;">`; // Header html += `<strong style="font-size: 14px; border-bottom: 2px solid ${color}; padding-bottom: 4px; display: inline-block; margin-bottom: 10px;">${category}</strong><br>`; // Main Value html += `<div style="margin-bottom: 8px;">`; html += `<span style="color: #666;">${seriesName}: </span>`; html += `<b style="font-size: 16px;">${value.toLocaleString()}</b>`; html += `</div>`; // Advanced Analytics Section html += `<div style="background-color: #f8f9fa; border-radius: 4px; padding: 8px; font-size: 12px; border: 1px solid #eee;">`; // Inject Utility 2 (% of Total) html += `<div style="margin-bottom: 4px;"><span style="color: #666;">% of Series Total:</span> <b>${percentOfTotal}</b></div>`; // Inject Utility 3 (Difference from previous) html += `<div style="margin-bottom: 4px;">${diffHtml}</div>`; // Inject Utility 1 (Hidden Target Column) if (this.point.customTarget !== null && !isNaN(this.point.customTarget)) { var targetDiff = value - this.point.customTarget; var targetStatus = targetDiff >= 0 ? '<span style="color:#28a745;">Met Target</span>' : '<span style="color:#dc3545;">Missed Target</span>'; html += `<hr style="margin: 6px 0; border: none; border-top: 1px dashed #ccc;" />`; html += `<div><span style="color: #666;">Target:</span> <b>${this.point.customTarget.toLocaleString()}</b> (${targetStatus})</div>`; } html += `</div></div>`; // End Analytics box & container return html; }; // Step 5: Optionally clear native Sisense tooltips based on the configuration flag args.result.plotOptions = Object.assign({}, args.result.plotOptions || {}); if (HIDE_NATIVE_SISENSE_TOOLTIP) { args.result.plotOptions.series = { turboThreshold: 0, marker: { enabled: true, states: { hover: { enabled: true, radiusPlus: 3, lineWidthPlus: 1 } } }, states: { hover: { enabled: true, halo: { size: 10, opacity: 0.3 } } } }; } else { args.result.plotOptions.series = Object.assign({}, args.result.plotOptions.series || {}); args.result.plotOptions.series.turboThreshold = 0; } }); 5. Adapt the script to your data There are two places you may need to customise: Hidden column name (Utility 1): Replace "Target" in the line below with the exact column title as it appears in your data panel: const targetColIndex = headers.indexOf("Target"); If you don't have a hidden target column, this utility is safely ignored – the tooltip will simply not render the Target section. Tooltip colours: The green (#28a745) and red (#dc3545) colours used for growth/decline indicators can be changed to match your brand palette. 6. Click Apply and test Save the script and hover over any bar or column in the chart. The custom tooltip will appear showing: The category label underlined with the series colour. The raw value in large text. A shaded analytics box containing: % of Series Total – the bar's share of the full series sum. vs Previous – the absolute and percentage change from the prior bar, with a green ▲ or red ▼ indicator. Target comparison (if a Target column is present) – the target value and a Met/Missed status label. Conclusion By hooking into the processresult event, this script transforms a standard Sisense charts into a self-contained analytics panel – surfacing hidden data, share-of-total context, and period-over-period trends all within a single hover interaction. The three utilities demonstrated here are modular: you can use any one of them independently, combine all three, or extend the pattern to add further metrics such as rolling averages or budget variance. This approach is particularly powerful for executive dashboards where users need instant context without drilling into separate views. References/Related Content Sisense JavaScript API Reference Highcharts Tooltip Formatter Documentation 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      How to remove pagination and display all data on a single page in Table and Pivot widgets [Linux]

      Introduction This article demonstrates how to remove pagination from Table and Pivot widgets in Sisense dashboards, forcing all data to be displayed on a single page. By default, Sisense Table and Pivot widgets paginate data into fixed-size pages, which can be limiting when users need to see the full dataset at once. This solution works by increasing the widget's data query limit and presentation page size to a very large number, and optionally hiding the pagination UI controls for a cleaner look. The solution works for both Table and Pivot 2 widgets on Linux deployments. The solution is tested on Sisense 2026.1.2 and works for both cloud and on-premise deployments. Step-by-Step Guide 1. Prepare your environment Open an existing dashboard that contains a Table or Pivot widget you want to display without pagination. Take note of the approximate number of rows your dataset returns, as you may need to adjust the maxDataRows value accordingly. We recommend not using very high numbers at first, as it can be hard to load it. Test it with a reasonable number and increase it little by little, to guarantee the weight of the query and processing will not be an issue for your use case. 2. Apply the code Open the Table widget in Edit mode, go to the three dots menu, select Edit Script , and paste the following code: /**  * Remove Pagination (Show All Data on Single Page)  *   * Works by increasing the query data limit and the widget's page size  * to a very large number, effectively forcing all data onto page 1.  */ <br> // 1. Increase the amount of data the widget requests from the ElastiCube widget.on('beforequery', function(sender, ev) {     // Set to a very high number to fetch all rows (adjust if needed)     var maxDataRows = 2100;          if (ev.query) {         ev.query.count = maxDataRows;     } }); <br> // 2. Increase the widget's presentation page size widget.on('initialized', function(sender, ev) {     var maxPageSize = 2100;          // For standard Tables and older Pivots     if (<a target="_blank" rel="noopener noreferrer nofollow" href="http://sender.style">sender.style</a>) {         <a target="_blank" rel="noopener noreferrer nofollow" href="http://sender.style">sender.style</a>.pageSize = maxPageSize;     } }); <br> // 3. Hide the pagination UI controls (optional, but requested for a "single page" look) widget.on('domready', function(sender, ev) {     var $el = $(element);          // Hide standard table pagination     $el.find('.table-grid__pagination').hide();          // Hide Pivot 2 pagination (if applicable)     $el.find('.pivot-pagination').hide(); }); In our example we had 2094 rows, and it worked as expected with no issues or delays: 3. Customize the script for your use case Before saving, consider adjusting the following: maxDataRows – Set this to a number higher than the maximum number of rows your dataset is expected to return. The default is 50000, but you can increase or decrease it based on your data volume. Keep in mind that fetching very large datasets may impact dashboard performance. maxPageSize – Should match maxDataRows to ensure the presentation layer also renders all rows on a single page. Hiding pagination controls (step 3) – This part is optional. If you want to keep the pagination UI visible (even though it will only show one page), you can remove or comment out the domready block. Conclusion By combining an increased query row limit, an expanded presentation page size, and hidden pagination controls, you can effectively display all data in a Table or Pivot widget on a single page, or just build large pages under custom circumstances. This is particularly useful for dashboards where users need full visibility of the dataset without navigating through pages. The script is a flexible starting point – adjust the row limits to match your data volume, and toggle the pagination UI hiding on or off depending on your use case. Keep performance in mind when working with very large datasets, as loading all rows at once may increase widget render time. References/Related Content Sisense JavaScript API Reference 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      How to add a new calculated row to a Table widget [Linux]

      Introduction This article demonstrates how to add a new calculated row, Grand Total in our example, to a Table widget in Sisense dashboards using a custom JavaScript script. With this article you should learn the base to understand how to add a new row with calculation based on other rows data, ensuring the calculated row values persists across pagination updates. The solution is tested on Sisense 2026.1.2 and works for both cloud and on-premise deployments. Step-by-Step Guide 1. Prepare your environment Open an existing dashboard that contains a Table widget you want to add a calculated row to, in our example, Grand Total row. Make sure the table has numeric columns you want to sum. Take note of the column positions (0-based index) that should be included in the total calculation. 2. Apply the code Open the Table widget in Edit mode, go to the three dots menu, select Edit Script , and paste the following code: var addGrandTotal = function (sender, event) { // 1. Correctly scope to the widget's own element var $el = $(element); var $table = $el.find("table.table-grid__table").length ? $el.find("table.table-grid__table") : $el.find("table").first(); if (!$table.length) return; // 2. Cleanup existing grand-total rows to ensure fresh calculation var existingTotal = $table.find("tr.grand-total").length; $table.find("tr.grand-total").remove(); var $rows = $table.find("tbody tr"); if (!$rows.length) return; // 3. Determine real column cells, excluding phantom/hidden columns var $firstRow = $rows.first(); // Filter for cells that have a column index class to avoid technical/phantom columns var $referenceCells = $firstRow.find("td").filter(function () { return ( $(this).attr("class").indexOf("table-grid__cell--col-") !== -1 && !$(this).hasClass("table-grid__cell--phantom") ); }); var colCount = $referenceCells.length; // Indices for columns to sum (0-based), change it according to your needs and columns-to-sum position var sumColumns = [1, 2, 3]; var totals = {}; sumColumns.forEach(function (colIdx) { totals[colIdx] = 0; }); // 4. Calculate totals for current view $rows.each(function () { var $cells = $(this).find("td"); sumColumns.forEach(function (colIdx) { if ($cells.length > colIdx) { var cellValue = $cells .eq(colIdx) .text() .replace(/[^0-9.\-]/g, ""); var num = parseFloat(cellValue); if (!isNaN(num)) { totals[colIdx] += num; } } }); }); // 5. Build Grand Total row var $grandTotalRow = $("<tr/>", { class: "grand-total table-grid__row", style: "height: 26px; background-color: #f9f9f9;", }); for (var i = 0; i < colCount; i++) { var isSumCol = sumColumns.indexOf(i) !== -1; var content = ""; if (i === 0) { content = "Grand Total"; } else if (isSumCol) { content = totals[i].toLocaleString(); } // Get original style but force bold and center/middle alignment var originalStyle = $referenceCells.eq(i).attr("style") || ""; var $td = $("<td/>", { class: "table-grid__cell table-grid__cell--rows table-grid__cell--col-" + i, style: originalStyle + "; border-width: 1px; vertical-align: middle; font-weight: bold;", }); var $contentWrapper = $("<div/>", { class: "table-grid__content" }).append( $("<div/>", { class: "table-grid__content__wrapper" }).append( $("<div/>", { class: "table-grid__content__inner" }).text(content), ), ); $td .empty() .append('<div class="table-grid__cell-corner"></div>') .append($contentWrapper); $grandTotalRow.append($td); } // 6. Append to tbody $table.find("tbody").append($grandTotalRow); // 7. Prevent layout jumps (only trigger resize if we actually added a new row that wasn't there before page change) if (!existingTotal) { setTimeout(function () { if (window.dispatchEvent) { window.dispatchEvent(new Event("resize")); } }, 100); } }; // Bind to 'ready' for the initial load, and 'domready' for pagination updates widget.on("ready", addGrandTotal); widget.on("domready", addGrandTotal); 3. Customize the script for your use case Before saving, update the following to match your table: sumColumns – Update the array [1, 2, 3] with the 0-based indices of the columns you want to sum. For example, if you want to sum the second and fourth columns only, use [1, 3]. The first column (index 0) is always used as the "Grand Total"  or your preferable name label cell. background-color – Optionally adjust the Grand Total row's background color in the style string to match your dashboard's theme. Conclusion By binding the Grand Total logic to both the ready and domready widget events and correctly scoping the script to the widget's own element, you can ensure a stable and persistent calculated row in your Table widgets – one that recalculates correctly after pagination, and other DOM changes. The script is a flexible starting point: you can adapt the column indices, styling, and label text to fit your specific table structure, or extend it to calculate averages, minimums, or other aggregate values as needed. References/Related Content Sisense JavaScript API Reference 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      How to block widget queries until a required dashboard filter is selected [Linux]

      Introduction This article explains how to prevent specific widgets from executing their data queries until the user has selected a value in a required dashboard filter. Without this control, widgets may load with incomplete or misleading results when a filter is left at "All" or has no selection. The solution uses the dashboard.on('widgetbeforequery') event to intercept each widget's query before it runs. If the required filter has no specific value selected, the query is emptied and the widget title is updated to prompt the user to make a selection. This is particularly useful for dashboards where certain widgets are only meaningful in the context of a specific filter value, for example, a report that should only display data for a selected brand, region, or department. The solution is tested on Sisense L2026.1.1 and works for both cloud and on-premises deployments. Step-by-Step Guide 1. Open the dashboard script editor From your dashboard, click the Edit button → open the Script tab (dashboard-level script, not widget-level). 2. Identify the widgets to protect Each Sisense widget has a unique ID (OID). To find a widget's OID: Open the dashboard in edit mode. Click on the widget → open its script editor or inspect the URL/network request. Copy the OID — it is a 24-character hexadecimal string (e.g. 69cd7ac30a2c1ca15a29c174). You can also get this value from the URL when inside the widget editing. Add all target widget OIDs to the targetWidgetIds array in the script. 3. Identify the required filter name The script matches the filter by its jaql.title value — the internal field name Sisense uses, which may differ from the display label shown on the dashboard. To find the exact value: Temporarily leave the console.log line active in the script (it is included by default): console.log('filters', d.filters.$$items) Open your browser's developer console, reload the dashboard, and inspect the logged output. Find the filter you want to require and note the value of its jaql.title property. Use that exact string as the value of requiredFilterName. 4. Paste the full script dashboard.on('widgetbeforequery', (d, args) => { // Specify the title of the filter you want to require const requiredFilterName = "Brand"; const targetWidgetIds = ["69cd7ac30a2c1ca15a29c174", "69cd7a670a2c1ca15a29c16e"]; if (!targetWidgetIds.includes(args.widget.oid)) { return; } // Find the filter in the dashboard's filter collection // Uncomment the lines below to identify the exact filter title value and structure, to adapt the code accordingly: console.log('filters', d.filters.$$items) let targetFilter = d.filters.$$items.find( (filter) => filter?.jaql?.title == requiredFilterName ); // If the filter is not set or "All" is selected, stop the query if (targetFilter.jaql.filter?.members?.length == 0 || targetFilter.jaql.filter?.all) { args.query.metadata = []; // Empty the query // Optional: update the widget title to prompt the user args.widget.title = "Please select a filter value for " + requiredFilterName + " to view data"; } }); 5. Configure the script constants At the top of the script, update the two configuration values to match your dashboard: 6. How the script works The script hooks into the widgetbeforequery event, which fires for every widget on the dashboard just before its data query is sent. For each firing: It checks whether the widget is in the targetWidgetIds list — if not, it exits immediately and the widget loads normally. It searches the dashboard's filter collection (d.filters.$$items) for a filter whose jaql.title matches requiredFilterName. It checks whether that filter has any specific members selected (members.length == 0) or whether "All" is selected (filter.all). If no specific value is selected, it empties the widget's query metadata — preventing any data from loading — and optionally updates the widget title to guide the user. 7. Adapting the script to your filter structure The console.log lines included in the script are intentional debugging helpers. Before going live, use them to inspect your filter's structure in the browser console and confirm: That jaql.title matches your requiredFilterName exactly. That the filter state is accessible via jaql.filter.members and jaql.filter.all (it can have a different structure depending on filter type). If your filter uses a different structure (e.g. a hierarchical/cascading filter with a levels array), the lookup logic will need to be adapted accordingly. The logged output will make this visible. 8. Save and test Once configured, comment out or remove the console.log lines, save the dashboard script, and reload the dashboard. The target widgets should display no data and show the prompt message when the required filter is set to "All" or has no selection. Once a specific filter value is chosen, the widgets will load their data normally. Important notes Important notes Dashboard-level script only: This script must be placed in the dashboard script editor, not in an individual widget's script. The dashboard.on('widgetbeforequery') event is only available at dashboard level. Flat filters only: This script is designed for standard, single-level filters where the field title is accessible via filter.jaql.title. If your filter is hierarchical (cascading), the filter structure will differ — use the console.log output to inspect it and adapt the lookup logic accordingly. Finding the filter title: The requiredFilterName must match the internal jaql.title value exactly, including capitalisation and spacing. The console.log helpers are the most reliable way to verify this. Widget OIDs: Each widget OID must be a 24-character hexadecimal string. Double-check these values — an incorrect OID means the widget will not be protected by the script. Widget title change is optional: The line that updates args.widget.title is a helpful UX touch but is not required for the blocking logic to work. You can remove or customize the message to suit your dashboard's language and audience. Multiple widgets: You can protect as many widgets as needed by adding their OIDs to the targetWidgetIds array. Widgets not in the list are completely unaffected. Remove console logs before publishing: The console.log lines are useful during setup but should be commented out or removed before the dashboard goes live to avoid unnecessary browser console output. Conclusion By intercepting the widgetbeforequery event at dashboard level and checking the state of a required filter before each query runs, you can enforce a "filter-first" experience for any widget on your dashboard, ensuring users always see meaningful, contextually correct data rather than unfiltered results. The built-in console.log helpers make it straightforward to inspect your filter structure and adapt the script to your specific setup. References / Related content Dashboard Class Documentation Dynamically Updating Widget Titles Based on Filter Selections in Sisense (Linux) 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      How to apply background filters in the dashboard programmatically [Linux]

      Introduction This article explains how to programmatically apply background filters to Sisense dashboards using JavaScript. Background filters allow you to set filter criteria that are not visible or directly editable by end users, but still affect the data displayed in widgets. This approach is useful for enforcing business logic or default data scoping behind the scenes. The solution described here is tested on Sisense L2026.1 and is compatible with both cloud and on-premises deployments. Step-by-Step Guide Prepare Your Environment Create a new dashboard or go to an existing dashboard with at least 1 widget. Apply a filter with “Include all” option for the value you want to apply the background filter to. Apply the code  Go to three dots menu in the dashboard, Edit Script, and paste the following code: var filterValue = 'ABC' function setFilter(value) {   // Getting specifically the existing Brand filter to apply a background filter on it. Change it to get the filter you want.   // Note in this context the Brand filter must already exist in your dashboard, even if with "Include all" applied.   var filter = prism.activeDashboard.filters.$$items.find(function(f){     return f.jaql && f.jaql.title === 'Brand';   });      if (filter) {      filter.jaql.filter = { // This is the main filter explicit: false,         multiSelection: false, all: true, filter: { // This is the background filter contains: value, isBetween: false } // You can also change the main filter value here if you want, defining a contains or members array for example.      }; <br> prism.activeDashboard.filters.update(filter, {save: true, refresh: true});   } } <br> dashboard.on('widgetready', function(scope, args) {     setFilter(filterValue); }); Code Explanation filterValue: The value you want to apply as a background filter. setFilter(value): This function searches for the specified dashboard filter (by title, e.g., "Brand"). It then updates the filter’s JAQL object, nesting the background filter criteria inside the main filter object. filter.jaql.filter: The main filter object. The nested filter property sets the background filter criteria (e.g., contains: value). prism.activeDashboard.filters.update: Applies the updated filter to the dashboard, saving and refreshing widgets. dashboard.on('widgetready', ...): Ensures the filter is applied after all widgets are loaded, preventing timing issues. This is just a basic example, feel free to use it as a base to create a custom code that will apply the background filters under some specific conditions or action trigger. As a result, you should see something similar as the following screenshot, the black funnel icon suggesting there is a background filter applied, then if you go to the three dots, Edit background filter, you will see the exact filter and values applied programmatically in the UI. Note: The filter must already exist in the dashboard for this script to work. If you need to dynamically add filters, use the Sisense JavaScript API methods for creating filters. Conclusion Applying background filters programmatically in Sisense dashboards empowers developers and administrators to tailor data visibility and enforce business logic without exposing filter controls to end users. This approach enables advanced scenarios such as dynamic data segmentation, automated contextual filtering, and compliance-driven data access—all managed seamlessly behind the scenes. By leveraging JavaScript, you can respond to user actions, external events, or custom logic to adjust dashboard filters in real time, unlocking new levels of flexibility and automation in your analytics environment. This capability is especially valuable for creating personalized dashboards, implementing security rules, or integrating Sisense with external systems, making your dashboards smarter and more responsive to business needs. References/Related Content Sisense JavaScript API Reference 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      How to change dashboard filters on Tabber Widget tab switch [Linux]

      Introduction This article demonstrates how to programmatically change dashboard filters in Sisense dashboards when a user switches tabs in a Tabber Widget. The solution is tested on Sisense L2026.1 and works for both cloud and on-premises deployments. By following this guide, you can create dashboards that automatically filter data based on the selected tab, improving usability and data relevance. Step-by-Step Guide Prepare Your Environment Create a new dashboard or go to an existing dashboard, and create a Tabber Widget (or use existing one). Apply the code  Edit the Tabber Widget, go to three dots menu and Edit Script, and paste the following code: // Map each tab to the Brand value you want to filter by var tabFilterMapping = {   'TAB 1': 'ABC',   'TAB 2': 'Addimar  ' }; <br> var lastAppliedTab = null; // Track the last tab for which the filter was applied <br> function setBrandFilter(value, tabName) {   var filter = prism.activeDashboard.filters.$$items.find(function(f){     return f.jaql && f.jaql.title === 'Brand';   });   if (filter) {     // Only apply if not already applied for this tab     if (lastAppliedTab !== tabName) {       filter.jaql.filter = {         explicit: true,         multiSelection: false,         members: [value]       };       lastAppliedTab = tabName;       prism.activeDashboard.filters.update(filter, {save: true, refresh: true});     }   } } <br> widget.on('ready', function(scope, args) {   // On initial load, apply filter for the default tab   var defaultTab = <a target="_blank" rel="noopener noreferrer nofollow" href="http://scope.style">scope.style</a>.tabs[<a target="_blank" rel="noopener noreferrer nofollow" href="http://scope.style">scope.style</a>.activeTab].title;   var defaultValue = tabFilterMapping[defaultTab];   if (defaultValue) {     setBrandFilter(defaultValue, defaultTab);   } <br>   // On tab click, apply filter only if a new tab is selected   $('.listItemContainer', element).on('click', function(e){     var selectedTab = $(e.currentTarget).text().trim();     var filterValue = tabFilterMapping[selectedTab];     if (filterValue) {       setBrandFilter(filterValue, selectedTab);     }   }); }); Code Explanation In this example we are mapping the tabs (TAB 1 and TAB 2) with the brand names we want to be applied in the filter, and then, when the tabs elements are clicked, we apply the filter change logic to filter the specific brand name mapped for TAB 1 or TAB 2 in the dashboard.  Conclusion By leveraging the Tabber Widget’s scripting capabilities, you can create dynamic dashboards that respond to user interactions, such as tab changes, by automatically updating filters. This approach enhances user experience and ensures that relevant data is always displayed based on the selected tab. The provided script is a flexible starting point - feel free to expand it to support additional filters as needed for your use case, or even trigger other actions or manipulate something other than filters if required. References/Related Content Sisense JavaScript API Reference 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.

      Thalita Santos
      Thalita SantosPosted 6 days ago
      0
               
    • Blog banner
      • Widget & Dashboard ScriptsChevronRightIcon

      Programmatically Formatting Bar Chart Widget Value Labels in Sisense

                                                                                               

      Programmatically Formatting Bar Chart Widget Value Labels in Sisense This article outlines ways to programmatically format Sisense Bar Chart Widget Value labels via widget scripts , covering methods to prevent label overlap and apply consistent styling across all labels. Custom Styling for Data Labels The script below enables the formatting of Chart Widget Value labels by setting a custom background color, padding, and border-radius. Ensure the default data label UI option is disabled. Other CSS and Highcharts settings can be added as needed.         widget.on('render', function (se, ev) { ev.widget.queryResult.plotOptions.bar.dataLabels = { backgroundColor: '#f5d142', color: 'white', padding: 5, borderRadius: 5, enabled: true } })       Preventing Label Overlap The script below manually adjusts value label positioning to prevent overlap in densely populated bar chart widgets. The exact formulas for label positioning can be changed as needed.         widget.on('domready', function (se, ev) { var barWidth = $('.highcharts-series-group .highcharts-series rect', element).width(); $('.highcharts-data-labels .highcharts-label', element).each(function () { var labelWidth = $(this).find('rect').width(); var labelHeight = $(this).find('rect').height(); $(this).find('rect').attr('x', ($(this).find('rect').attr('x') + 2)); $(this).find('rect').attr('height', barWidth); $(this).find('rect').attr('y', ((labelHeight - barWidth) / 2)); }) })         Dynamically Increase Space for Labels If bar value labels overlap with the chart bars, you can dynamically adjust the maximum value on the y-axis to create additional space. A different formula, or a hard-coded value, can also be used as the y-axis maximum value.         widget.on('processresult', function (se, ev) { var maxValue = 0; var increasePercent = 0.2; ev.result.series.forEach(function (series) { series.data.forEach(function (dataItem) { if (dataItem.y > maxValue) maxValue = dataItem.y; }) ev.result.yAxis[0].max = maxValue + (increasePercent * maxValue); }) })       Conclusion These scripts enable customizing dynamically formatted and well-positioned data labels in your Sisense charts, enhancing readability and aesthetics beyond the default Sisense data bar data labels in bar chart widgets. For further discussion of these types of scripts, see the  Dynamically Formatted Data Labels article Example Of Custom Labels Added via Scripting   Y-Axis Maximum Set To a Very Large Value Check out this related content:  Academy Documentation

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago • Last reply 1 week ago
      1
               
      • Widget & Dashboard ScriptsChevronRightIcon

      Customize Onclick Of Column Chart

               

      In this post, we will show how to override the default click behavior of the column chart widget.  This same widget script should work for bar, line, and area charts as well. Create a Sisense widget that contains the data points you want to pass on.   From the widget editor, click the settings menu at the top right and open up the  Script Editor.   Copy and paste the following script, swapping out the  baseUrl  variable to meet your criteria, then click save. /* Welcome to your Widget's Script. To learn how you can access the Widget and Dashboard objects, see the online documentation at http://developer.sisense.com/pages/viewpage.action?pageId=557127 */ // widget.on('render', function(widget,args){ // Define the base url var baseUrl = 'http://www.google.com?q='; // Get the labels for the axis and/or break by var axisLabel = $$get(widget.metadata.panel(0).items[0], 'jaql.title', null), breakByLabel = $$get(widget.metadata.panel(2).items[0], 'jaql.title', null); // Define a click handler function wasClicked(event){ // Build the link var url = baseUrl + axisLabel + '=' + this.category; // Is there a break by field too? if (breakByLabel){ url += ', ' + breakByLabel + '=' + this.series.name; } // Open the url in a new tab window.open(url,'_blank'); } // Get a reference to the highcharts object var hc = widget.queryResult; // Loop through each data series hc.series.forEach(function(series){ // Loop through each data point series.data.forEach(function(point){ // Assign the event handler point.events = { 'click': wasClicked } }) }) }) Now, when you click on a specific bar/column it will open up the url you specified and pass along the selection  from the category/axis/break by panels.  The labels used to pass through come from the panel titles, similar to how the legend and tooltips are displayed.

      intapiuser
      intapiuserPosted 3 years ago • Last reply 3 months ago
      1
               
    …