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
    • TagsChevronRightIcon
    Code First Analytics
    • Blog banner
      • Widget & Dashboard ScriptsChevronRightIcon

      Hiding Widgets if a Widget Has No Results [Linux]

                                                                                                               

      Hiding Widgets if a Widget Has No Results [Linux] Introduction A common dashboard design requirement is hiding widgets that have no data. This article includes a widget script that conditionally hides an indicator widget when its primary value is empty or represents an N/A like value. This behavior is commonly requested when dashboard filters or formulas result in no meaningful value and the widget should be visually hidden rather than showing an empty or zero indicator. The article also includes an alternative dashboard-level approach that hides widgets based on filter selections, without inspecting widget query results. This alternative is derived from the linked external blog post . This is applicable to both on-cloud and on-prem Sisense in all recent Sisense versions. Use Case Customers often want indicator widgets to disappear when their calculated value is not meaningful. Common examples include: Filters resulting in no matching data Calculations returning N/A, null, or empty values Conditional metrics that only apply to certain filter selections Rather than showing an empty indicator, this approach hides the widget entirely and restores it automatically when the value becomes valid again. Layout Considerations For best visual results, it is ideal to use one of these two layouts for the indicator widgets that may be hidden: Place the indicator widget in its own dashboard row Place it at the end of a row Hiding a widget via a script does not automatically resize or reflow other widgets on the same row. Step by Step Guide Widget Script to Hide Widget Based on Indicator Value This widget script is applied directly to the indicator widget. It evaluates the widget’s primary value after each render and hides or shows the widget accordingly. Behavior If the primary value is null, empty, or an N/A like string, the widget container is set to display: none. When the value becomes valid and not null again, the widget is restored to visibility and redrawn to ensure correct indicator rendering. A guard variable prevents infinite redraw loops, since a redraw triggers the widget ready event. A debug flag allows optional console logging when needed. Widget Script /** * Hide widget when its primary indicator value is empty or N/A-like. * * Behavior: * - If the value is empty, set the widget container to display "none". * - If the returned value becomes a number or valid value, restore display style to original and redraw * - A redraw triggers "ready" again, so a guard variable prevents a redraw loop. * * Debug: * - Set debug variable = true to enable console logging to track script status. * * For best results use on indicator in own row or at end of row, if other widgets exist on row, empty space will appear in dashboard */ widget.on("ready", function () { function run() { var debug; var suppressNextReady; var hideValues; debug = false; suppressNextReady = false; hideValues = [ "n/a", "#n/a", "na", "none", "null", "undefined" ]; // Function to turn console logging on or off function log(message, data) { if (!debug) { return; } if (data === undefined) { console.log("[hide-empty-indicator] " + message); return; } console.log("[hide-empty-indicator] " + message, data); } // Widget CSS selector function getWidgetElement() { return document.querySelector('widget[widgetid="' + widget.oid + '"]'); } // Get value of primary indicator value function getPrimaryValue() { if ( widget.queryResult && widget.queryResult.value && widget.queryResult.value.data !== undefined ) { return widget.queryResult.value.data; } try { if ( widget.queryResult && widget.queryResult.data && widget.queryResult.data.length ) { return widget.queryResult.data[0][0]; } } catch (e) { log("Value read failed for data[0][0].", e); } if (widget.queryResult && Array.isArray(widget.queryResult)) { if (widget.queryResult.length && widget.queryResult[0].length) { if (widget.queryResult[0][0]) { return widget.queryResult[0][0].Value; } } } return null; } function shouldHide(value) { var text; var normalized; if (value === null || value === undefined) { return true; } text = String(value).trim(); if (!text) { return true; } normalized = text.replace(/\\/g, "/").toLowerCase(); return hideValues.indexOf(normalized) !== -1; } function isElementHidden(element) { if (!element) { return false; } return element.style.display === "none"; } function hideWidget(element) { if (!element) { return; } if (element.style.display === "none") { return; } element.style.display = "none"; log("Hid widget due to empty/N/A-like value."); } function showWidgetAndRedrawIfNeeded(element) { var wasHidden; if (!element) { return; } wasHidden = isElementHidden(element); element.style.display = ""; if (!wasHidden) { log("Widget already visible."); return; } if (typeof widget.redraw !== "function") { log("Widget restored, redraw not available."); return; } suppressNextReady = true; log("Widget restored, triggering redraw."); widget.redraw(); } function applyRule() { var element; var primaryValue; element = getWidgetElement(); if (!element) { log("Widget container element not found."); return; } if (widget.queryResult === undefined) { log("queryResult is not available yet, keeping widget visible."); element.style.display = ""; return; } primaryValue = getPrimaryValue(); log("Primary value evaluated.", primaryValue); if (shouldHide(primaryValue)) { hideWidget(element); return; } showWidgetAndRedrawIfNeeded(element); } function onReady() { if (suppressNextReady) { suppressNextReady = false; log("Ready fired after redraw, applying rule without redraw."); applyRule(); return; } applyRule(); } onReady(); } run(); }); Notes The script uses display: none instead of jQuery hide or show to avoid layout and rendering issues with indicator widgets. Redraw is triggered only when restoring visibility, not when hiding. The script relies only on the widget ready event, which fires again after redraw and filter changes. Dashboard Script to Hide Widgets Based on Filter Selections As an alternative, widgets can be hidden purely based on filter selections, without checking whether the widget returns data. This approach is useful when visibility rules are deterministic based on filters. This method uses a dashboard script and CSS classes to hide widget containers. Example Dashboard Script dashboard.on('filterschanged', function (se, ev) { let filterName = 'Region' //mapping of filter items and widgets to be hidden. //if selected filter item is not available in the list, widgets in 'default' key will be hidden let itemWidgetMapping = { 'Midwest':['6390b5a285a029002e9e2ad6'], 'South': ['6238887ba77683002ea4425b'], 'West':['6390b5a285a029002e9e2ad6', '6238887ba77683002ea4425b'], 'default':[] } selectedFilter = ev.items.find(el=>el.jaql.title == filterName) let selectedItem = 'default' if(selectedFilter && selectedFilter.jaql.filter.members) selectedItem = selectedFilter.jaql.filter.members[0] //unhide all widgets first and then hide widgets based on selected filter $(`widget`).closest('.dashboard-layout-subcell-host').removeClass('dontshowme-parent') if(selectedItem in itemWidgetMapping){ for (const [key, value] of Object.entries(itemWidgetMapping)) { if(key == selectedItem){ itemWidgetMapping[key].forEach(function (item, index) { $(`widget[widgetid="${item}"]`).closest('.dashboard-layout-subcell-host').addClass('dontshowme-parent') }); } } } else{ itemWidgetMapping['default'].forEach(function (item, index) { $(`widget[widgetid="${item}"]`).closest('.dashboard-layout-subcell-host').addClass('dontshowme-parent') }); } }); Choosing the Right Approach Generally the widget script is best suited when: Visibility depends on whether data is returned The indicator value can be empty due to calculations or filters Generally the dashboard script is best suited when: Visibility depends only on filter selections Centralized control over multiple widgets is required These approaches are alternatives and should not be used simultaneously for the same widgets. Conclusion Hiding indicator widgets based on their returned value can potentially improve dashboard clarity and user experience. The widget script approach provides result aware behavior, while the dashboard script approach offers deterministic, filter based control. Both methods are powerful tools for customizing dashboard widget visibility. Two Full Row Indicator Widgets, both visible First Row Indicator Widget is now hidden, by script, due to no data Two Indicators in one row, both visible Second Indicator is now hidden by script, due to no data 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.

      Jeremy Friedel
      Jeremy FriedelPosted 1 week ago
      0
               
    • Blog banner
      • Add-ons & Plug-InsChevronRightIcon

      Plugin - Custom Coloring of Bar and Column Chart Type Widgets (barChartCustomColor)

                                                       

      Plugin - Custom Coloring of Bar and Column chart-type widgets This plugin  modifies the color scheme of bar and column chart-type widgets to match the current color palette of the dashboard. Installation To install this plugin, download and unzip the attachment. Then drop the barChartCustomColor folder into your plugins folder (/opt/sisense/storage/plugins). Enable the plugin on the Add-ons tab in the Admin section, wait for the plugin to build, and the plugin will be enabled. The plugin API can also be used, as well as the file management UI . Notes When a bar or column chart type Sisense widget is very straightforward and focused and does not have multiple values, breaks by's, categories, or conditional coloring, it can sometimes appear relatively mono-color but still be the most effective way to quickly visually communicate important data.   While Sisense includes numerous powerful and varied methods and options to change widget styling  and includes functionality to set bar color to vary based on the bar value , it sometimes may be desired to use the dashboard palette to vary the bar colors independently of bar value and guarantee a colorful widget regardless of the data and current filter state of the current dashboard and widget. This plugin allows the current dashboard palette to be used in this manner in a simple and quick matter and allows this to be applied quickly to an entire dashboard at once. Linking the widget coloring to the dashboard palette allows this color scheme to be quickly changed in the native UI without modifying individual widgets. This plugin also allows using a color dictionary config option to modify the coloring of a value in all widgets this plugin is enabled in.       This plugin  uses the processresult widget event to modify the color parameter of each bar or column in a series.   The dashboard palette is retrieved using the getPalette() function within the dashboard style parameter, which returns the current dashboard palette. This can be used in other plugins and scripts.     dashboard.style.getPalette()       This plugin is enabled via dashboard or widget scripts that set the widget parameter changeColor to true for a widget.   For a widget script, this is as simple as adding this one line to the script:     widget.changeColor = true;       Adding this three-line dashboard script will set this parameter to true for all widgets in the dashboard:     dashboard.on('widgetinitialized', function (_, dashObj) { dashObj.widget.changeColor = true; });     This can be modified with custom logic as needed, for example, based on widget title or ordering in the dashboard. If this option is enabled for a type of widget this plugin does not apply to, it will simply be ignored by the plugin. This plugin ignores widgets with  Break By's or Date Categories, or widgets that are not bar or column chart-type widgets. The color scheme is based on the current dashboard palette.    Changing the dashboard palette using the standard Sisense palette UI will result in a matching change of all widgets in the dashboard where this plugin is enabled. The config file of this plugin includes an option called colorDictionary, this can be used to override the standard palette color for all instances this value appears as the bar category, for all widgets this plugin is enabled for, regardless of the dashboard palette. Any standard HTML color naming format may be used in the dictionary config value. For example, if the colorDictionary is set to:     colorDictionary : { 'Bikes': '#AA6C39', 'ABC' : 'yellow' }      Then the ABC bar or column will be yellow in the widget, regardless of the current widget palette or positioning. This will apply to all widgets where this plugin is active. This plugin can be used as a basic template for your own Sisense plugins that run on a specific widget or dashboard event, that can be enabled or disabled via widget script, and that includes a configuration file to modify settings without modifying code files.        How did the plugin work for you? What other type of plugin are you looking to learn more about? Let me know in the comments!

      Jeremy Friedel
      Jeremy FriedelPosted 2 years ago • Last reply 1 month ago
      1
               
    • Blog banner
      • Widget & Dashboard ScriptsChevronRightIcon

      Advanced Scripting for Sisense’s Simply Ask NLQ: Adding Currency Symbols to Pivot Tables

                                                                                       

      Advanced Scripting for Sisense’s Simply Ask NLQ: Adding Currency Symbols to Pivot Tables Sisense’s Simply Ask feature empowers users to create visualizations instantly by asking natural language questions, simplifying data exploration without needing technical expertise or manual widget configuration. This Natural Language Query (NLQ) capability accelerates insights generation without the need to manually create new Sisense widgets through the standard UI for each question or data relationship of interest.   User interface of the 'Simply Ask - Sample ECommerce' dashboard in the Sisense data analytics tool. A central search bar at the top allows users to 'Ask a question about your data.' Below the search bar is a large icon with a plus symbol, inviting users to ask questions about dashboard data. On the right, there is a list of 'Available fields' categorized under Commerce, Category, Country, and Formulas, with individual fields like Brand ID, Amount, Age Range, and Revenue. A 'Done' button is highlighted at the bottom. A previously published article discussed how to customize Simply Ask NLQ-generated widgets using scripting, providing multiple examples of basic modifications and more detail on the basic mechanics of scripting Simply Ask widgets. This article will share a more complex example to show how NLQ widgets can be customized in more complex conditional ways, in this example: programmatically adding currency symbols to specific data columns in pivot table widgets generated by Simply Ask. Enhancing NLQ Widgets with Currency Symbols When dealing with financial data, it’s can be important to display currency symbols alongside numeric values for clarity, especially if multiple currency may be used in a datasource. However, Simply Ask NLQ-generated widgets may not automatically include these symbols, especially if they are dynamically generated based on natural language user queries. The following script demonstrates how to: Detect when a new NLQ widget is rendered. Identify specific data columns based on their titles using purely HTML based Javascript observation. Append a currency symbol to the numeric text values in those columns. Full Script:   const addCurrencySignToValues = () => { // Column keywords to match (case-insensitive) const currencySignColumns = ['Revenue', 'Cost']; // Replace with actual column keywords as needed // Shared constants for selectors and class patterns const NLQ_MAIN_CONTENT_SELECTOR = 'div.nlq-widget__main-content'; const DATA_CELL_SELECTOR = 'td.table-grid__cell--data'; const HEADER_CELL_SELECTOR = 'td.table-grid__cell--row-0'; const INNER_CONTENT_SELECTOR = 'div.table-grid__content__inner'; const ROW_CLASS_PREFIX = 'table-grid__cell--row-'; const COLUMN_CLASS_PREFIX = 'table-grid__cell--col-'; const NUMBER_PATTERN = /^[0-9,.]*$/; // Determine the default symbol to use let symbol = '$'; // Function to get the cell's row and column indices const getCellPosition = (cell) => { let classList = cell.classList; // Find row and column classes let rowClass = Array.from(classList).find(cls => cls.startsWith(ROW_CLASS_PREFIX)); let columnClass = Array.from(classList).find(cls => cls.startsWith(COLUMN_CLASS_PREFIX)); // If both classes are found, extract indices if (rowClass && columnClass) { let rowIndex = rowClass.split(ROW_CLASS_PREFIX)[1]; let columnIndex = columnClass.split(COLUMN_CLASS_PREFIX)[1]; return { row: parseInt(rowIndex, 10), column: parseInt(columnIndex, 10) }; } // Return null if not found return null; }; // Build a mapping of column indices to titles let columnHeaders = {}; // Collect header cells (row 0) $(HEADER_CELL_SELECTOR).each(function () { let cellPosition = getCellPosition(this); if (cellPosition) { let headerText = $(this).text().trim(); columnHeaders[cellPosition.column] = headerText; } }); // Process data cells $(`${NLQ_MAIN_CONTENT_SELECTOR} ${DATA_CELL_SELECTOR}`).each(function () { let cellText = $(this).text(); let cellPosition = getCellPosition(this); if (cellPosition) { let columnIndex = cellPosition.column; let columnTitle = columnHeaders[columnIndex]; // Check if column title includes any of the keywords (case-insensitive) if ( currencySignColumns.some(keyword => columnTitle.toLowerCase().includes(keyword.toLowerCase()) ) && NUMBER_PATTERN.test(cellText) ) { let changedText = `${cellText}${symbol}`; // Modify the text within the inner div $(this).find(INNER_CONTENT_SELECTOR).text(changedText); } } }); }; // Set up the MutationObserver to detect when new NLQ widgets are rendered dashboard.on('domready', function () { $('button.nlq-main-button').click(function () { setTimeout(function () { // Observe changes in the widget container const elementToObserve = $('div.nlq-widget__main-content')[0]; const observer = new MutationObserver(function () { addCurrencySignToValues(); }); observer.observe(elementToObserve, { subtree: true, childList: true }); }, 1000); }); }); ​   How It Works Define Column Keywords : The currencySignColumns array variable contains the keywords for column titles to target (in this example, ‘Revenue’ and ‘Cost’). The script will search for these keywords in the pivot column headers to determine which columns should have the currency symbol appended. Set Up Constants for CSS Class Names : Constants for selectors and class prefixes are defined for easy maintenance and readability. Determine the Currency Symbol : By default, in this example the symbol is set to '$'. It can be modified to any symbol required. If needed this can be be read from a Javascript variable set by a plugin or other script, to determine the correct currency symbol to use for a dashboard and datasource. Get Cell Position Function : The getCellPosition function extracts the row and column indices from a cell’s class list, which follows a naming convention contained in class names. Build a Column Headers Map : The script builds a mapping (columnHeaders) of column indices to their respective header titles by iterating over header cells. This same concept and code can be used in other scripts that customize pivots based on header titles. Process Data Cells : Iterates over all data cells, checks if cell's column title matches any of the keywords, and if the cell content is numeric. Append Currency Symbol : If both conditions are met, the script appends the currency symbol to the cell’s text within the inner content div. Set Up Mutation Observer : The script uses a MutationObserver to detect when a new NLQ widget is rendered in the Simply Ask modal. It observes the main widget content element for changes and triggers addCurrencySignToValues when mutations occur. Customization Tips Updating Keywords : Modify the currencySignColumns array to include any keywords relevant to the data columns in question. Changing the Currency Symbol : Change the symbol variable to use a different currency symbol or to dynamically set it based on user preferences or locale. Adjusting Number Pattern : The NUMBER_PATTERN regex can be adjusted to match different numeric data includes different formats (e.g., decimal points, thousand separators). Use as Template:  This example can be used as a template for completely different types of custom scripting of Simply Ask NLQ widgets. Integrating the Script into a Dashboard To use this script in a Sisense environment: Add the Script to the Dashboard : Place the script within the dashboard’s JavaScript editor. This will ensure it runs when the dashboard is loaded.   Considerations CSS Class Updates : CSS class names can change between Sisense versions; keeping CSS selector strings as variables allows for easy updating if needed. Conclusion By leveraging standard JavaScript, and dashboard scripting NLQ-generated widgets can be enhanced in Sisense’s Simply Ask feature with advanced customizations like appending currency symbols to specific data columns. This approach allows for maintaining consistency and clarity in data presentations, providing a better experience for users interacting with dynamically generated visualizations. This example shows that complex widget scripting customizations are possible with Simply Ask NLQ generated widgets. Related Content: For more details on scripting with Simply Ask NLQ and additional examples, refer to the previous article Modifying Simply Ask Natural Language Query Generated Widgets with Scripting .

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago
      0
               
    • Blog banner
      • Widget & Dashboard ScriptsChevronRightIcon

      Modifying Simply Ask Natural Language Query Generated Widgets with Scripting

                                                                                       

      Modifying Simply Ask Natural Language Query Generated Widgets with Scripting Sisense's Simply Ask  feature empowers users to create visualizations instantly by asking natural language questions, simplifying data exploration without needing technical expertise or manual widget configuration. This Natural Language Query (NLQ) capability accelerates data exploration and insights generation without needing to manually create new Sisense widgets through the standard UI for each question or data relationship of interest.   User interface of the 'Simply Ask - Sample ECommerce' dashboard in the Sisense data analytics tool. A central search bar at the top allows users to 'Ask a question about your data.' Below the search bar is a large icon with a plus symbol, inviting users to ask questions about dashboard data. On the right, there is a list of 'Available fields' categorized under Commerce, Category, Country, and Formulas, with individual fields like Brand ID, Amount, Age Range, and Revenue. A 'Done' button is highlighted at the bottom. However, these automatically generated widgets differ from standard widgets in that they do not trigger typical widget scripting events , which poses a challenge for applying custom modifications. This limitation can hinder the application of standard dashboard and widget scripting customizations directly to these widgets. Fortunately, it is possible to modify NLQ-generated widgets programmatically by using JavaScript to manipulate the HTML of the fully rendered widgets without relying on Sisense-specific widget scripting events. This approach involves observing changes in the Simply Ask modal DOM and applying custom code when new widgets are rendered. This article explores how to implement such customizations, providing code examples and notes to help customize NLQ-generated Simply Ask widgets.   Understanding the Context Standard Sisense widgets support a variety of events (such as `beforequery` , `processquery` , `ready` , etc.) that run custom code at specific points in the widget’s query and rendering lifecycle, allowing developers to inject functionality and modifications seamlessly. However, NLQ-generated widgets within the Simply Ask modal do not trigger these events. This means that any scripts using these events will not directly apply to NLQ widgets until they are pinned (added) to the dashboard as standard Sisense widgets. When an NLQ widget is pinned using the dashboard pin UI button in the Simply Ask modal, it becomes a native Sisense dashboard widget, fully compatible with standard scripting. However, for scenarios where it is desired to apply customizations immediately within the Simply Ask modal for new generated NLQ widgets, without pinning the widget, code-based customization is still possible.   Solution Overview To modify NLQ-generated widgets within the Simply Ask modal, JavaScript provides an effective solution through the use of `MutationObserver` and direct manipulation of HTML elements. Specifically, you can: Use JavaScript to observe changes in the DOM Element : By setting up a standard JavaScript `MutationObserver` , you can detect when new widgets are rendered in the Simply Ask modal DOM. Apply custom modifications to the widget's HTML : Once the observer detects a new widget, you can manipulate the HTML elements using standard JavaScript or jQuery, including adjusting the styling. Implementing the Solution Setting Up a Mutation Observer First, the code must detect when a new widget is rendered in the Simply Ask modal. You can do this by observing changes in the modal's main content area using a Javascript  MutationObserver . The code for this MutationObserver  can run within the dashboard script, like all dashboard scripts, this will be specific to the current dashboard. This MutationObserver  could also exist in plugin code if this customization is designed to run on all dashboards with Simply Ask enabled, without modifying the dashboard script for each dashboard. This flexibility allows custom modifications to be reusable across different dashboards by placing the `MutationObserver` in plugin code or specific to one dashboard by placing it in that dashboard's script.   dashboard.on('domready', function () { // Listen for clicks on the Simply Ask button $('button.nlq-main-button').click(function () { setTimeout(function () { // Select the widget container area of the Simply Ask modal const elementToObserve = $('div.nlq-widget__main-content')[0]; // Create a MutationObserver instance const observer = new MutationObserver(function () { // Call your custom function when a change in DOM is detected customizeNLQWidget(); }); // Start observing the DOM for mutations observer.observe(elementToObserve, { subtree: true, childList: true }); }, 1000); // Delay to ensure the modal is fully rendered }); }); ​     Explanation : Event Listener : The script runs on the click event using an event listener on the Simply Ask UI button ( `nlq-main-button` ) which opens the Simply Ask NLQ modal. Delay with setTimeout : A short delay ensures that the Simply Ask modal is fully rendered before setting up the observer. MutationObserver:  Observes the NLQ-generated widget HTML element using the selector `nlq-widget__main-content` for any changes, specifically when new child nodes are added (i.e., when a new widget is generated on an NLQ query). subtree : Ensures that mutations to child elements of the main content area are also detected. childList : Watches for changes in the direct children of the observed element (e.g., when new widgets are added). Callback Function : When a mutation is observed, the `customizeNLQWidget` function is called to apply custom modifications. This can be any function or series of functions.   Example Customization Function The function that applies the desired modifications to the NLQ widget can be defined with any valid JavaScript. In this example, the function will modify the pivot header cells' styling.   const customizeNLQWidget = () => { const NLQ_MAIN_CONTENT_SELECTOR = 'div.nlq-widget__main-content'; const HEADER_CELL_SELECTOR = 'td.table-grid__cell--row-0'; // Apply CSS styles to the pivot table header cells $(`${NLQ_MAIN_CONTENT_SELECTOR} ${HEADER_CELL_SELECTOR}`).css({ 'background-color': 'blue', 'color': 'white', }); }; ​   Explanation : Selectors : The function targets the pivot table header cells within the NLQ modal. These can be any valid CSS selector, in this example they are: `NLQ_MAIN_CONTENT_SELECTOR` : Selects the main content area of the NLQ widget. `HEADER_CELL_SELECTOR` : Selects the header cells of a pivot table type widget. CSS Styling : Applies custom CSS styling to change the background color to blue and the text color to white.   Combining the Code   For clarity, here's the combined code that brings together the observer setup and the example custom code customization function:   dashboard.on('domready', function () { $('button.nlq-main-button').click(function () { setTimeout(function () { const elementToObserve = $('div.nlq-widget__main-content')[0]; const observer = new MutationObserver(function () { customizeNLQWidget(); }); observer.observe(elementToObserve, { subtree: true, childList: true }); }, 1000); }); }); const customizeNLQWidget = () => { const NLQ_MAIN_CONTENT_SELECTOR = 'div.nlq-widget__main-content'; const HEADER_CELL_SELECTOR = 'td.table-grid__cell--row-0'; $(`${NLQ_MAIN_CONTENT_SELECTOR} ${HEADER_CELL_SELECTOR}`).css({ 'background-color': 'blue', 'color': 'white', }); };   Customization Examples The example above modifies the header cells of a pivot table. However, you can extend the `customizeNLQWidget` function to perform various other customizations, such as: Adding Icons or Indicators : Inject visual cues for critical data points or thresholds directly into cells. Conditional Formatting: Automatically highlight data cells based on values (e.g., flagging negative values in red). Interactive Elements : Add clickable elements, like links or buttons, to enable dynamic user interactions, such as drilling into specific data. Content Modification : Alter or append units, currency symbols, or other data-specific indicators to ensure clarity and consistency in data presentation. Example: Conditional Formatting Based on Values   const customizeNLQWidget = () => { const NLQ_MAIN_CONTENT_SELECTOR = 'div.nlq-widget__main-content'; const DATA_CELL_SELECTOR = 'td.table-grid__cell--data'; $(`${NLQ_MAIN_CONTENT_SELECTOR} ${DATA_CELL_SELECTOR}`).each(function () { const cellValue = parseFloat($(this).text()); if (cellValue < 0) { $(this).css('color', 'red'); } else if (cellValue > 0) { $(this).css('color', 'green'); } }); }; ​   Explanation : Data Cells Selector : Targets all data cells in the pivot table. Value Parsing : Converts the cell text to a numeric value. Conditional Styling : Applies red color to negative values and green color to positive values. Considerations and Best Practices Scope : Ensure your selectors are specific enough to avoid unintended modifications to other parts of the dashboard. Compatibility Testing : Regularly test your customizations across different Sisense versions to ensure that changes in CSS classes or HTML structures don't break the functionality. CSS Class Updates : CSS class names can change between Sisense versions; keeping CSS selector strings as variables allows for easy updating if needed. Conclusion While NLQ-generated widgets in Sisense's Simply Ask feature do not support standard widget scripting events, the ability to observe DOM changes and manipulate HTML allows for extensive customizations. With JavaScript, you can enhance widget appearance, add interactive functionality, and more—all within the Simply Ask modal, providing a tailored user experience. This approach allows maintaining consistency in dashboards and provides a more customizable experience for users interacting with NLQ-generated visualizations.   Further Reading Sisense Javascript Reference   Sisense Scripting Sisense Simply Ask NLQ

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago
      0
               
      • Embedding AnalyticsChevronRightIcon

      Sisense Compose SDK

                                                                               

      Sisense Compose SDK The Future is Composable In the fast-paced world of data-driven applications, developers are constantly seeking innovative ways to create customized data products that seamlessly integrate into their projects. Sisense has stepped up to the challenge with its groundbreaking Compose SDK, which has fundamentally changed the game. This powerful toolkit empowers developers to harness the full potential of Sisense components, enabling them to craft data products that are not only highly customized but also perfectly tailored to their applications.  Traditionally, the embedded analytics market constrained developers within the boundaries of their chosen tools. However, Compose SDK breaks free from this limitation. Instead of merely embedding a third-party tool, developers now have the freedom to build bespoke data products by selecting from a rich library of Sisense components and seamlessly integrating them with a wide range of other tools within their Integrated Development Environment (IDE).  A Practical Example  In the world of UI design, consistency is key. Imagine your company has chosen the Material UI framework to style your application. It provides a sleek and uniform look, but now you need to populate Material UI tables with dynamic data from Sisense. Enter Compose SDK. With it, you can seamlessly merge Material UI's aesthetics with Sisense's analytical power. Start with Material UI tables that align perfectly with your design guidelines. Then, using the Compose SDK's ExecuteQuery component, you effortlessly infuse these tables with real-time data from Sisense.         The result? Stunning, data-rich components that not only adhere to your design principles but also deliver invaluable insights. Your users get the best of both worlds - visually appealing design and powerful data. Get Started with Compose SDK  Excited to explore the potential of the Compose SDK? Good news - it's currently in Beta and available to all Sisense partners, ready for you to harness its capabilities today. To embark on your journey, you can begin by cloning the example repository from GitHub listed below. To install the necessary packages for your sample application, follow the user-friendly Compose SDK Quickstart Guide. Keep in mind that, for the time being, these packages are accessible on GitHub, requiring a personal access token for access.  In conclusion, Sisense's Compose SDK represents a revolutionary leap forward in the world of data product development. It empowers developers to break free from the constraints of traditional embedded analytics and build highly customized, seamlessly integrated data products. With the Compose SDK, the possibilities are limitless, and the power to transform your  applications lies at your fingertips. So, why wait? Dive into the world of Compose SDK, unleash your creativity, and revolutionize your data products today. Your applications will thank you for it!  Credit : Sample Application initially developed by Sisense Senior Director of Field Engineering: Tom Linton  Compose SDK -- Material UI Example   Sisense Quick Start Guide   Sisense Github

      Sisense User
      Sisense UserPosted 2 years ago • Last reply 1 year ago
      1
               
    • Blog banner
      • Add-ons & Plug-InsChevronRightIcon

      Plugin - customLogoLink

                                                       

      Plugin - Set Custom Link Overriding default In Top Left Logo Icon Description This plugin allows changing the link of the top-left Sisense or white-labeled logo on the server's home page to a custom URL. Disabling the plugin will restore the logo link to its default behavior. Installation To install this plugin, download and unzip the attachment. Then drop the disableLogoLink folder into your plugins folder (/opt/sisense/storage/plugins). Enable the plugin on the Add-ons tab in the Admin section, wait for the plugin to build, and the plugin will be enabled. The plugin API and file management UI can also be used. Config Options - URL - URL navigated to on logo click - iframeLogoLinkChangeParentAddress - Boolean option allows logo click to change the parent address of a Sisense Iframe. It should be noted that JTD uses an iframe Notes This plugin is similar to the DisableLogoLink plugin which is documented here this page includes notes on creating plugins similar to this one.     Learn more: https://docs.sisense.com/main/SisenseLinux/pre-post-build-plugins.htm https://academy.sisense.com/master-class-advanced-dashboards-with-plug-ins-and-scripts

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago
      0
               
      • Widget & Dashboard ScriptsChevronRightIcon

      Conditional Coloring with Scripting in Sisense Polar Bar Charts

                                                       

      Polar Chart Widget Conditional Coloring When working with polar chart-type widgets in Sisense dashboards, it can be helpful to set coloring conditional on the data returned from the data sources. This can improve the utility of the widget. However, natively supported conditional coloring is not included for all widget types, such as Polar Chart charts. This article explains how to achieve conditional coloring for polar bar charts using a custom widget script or plugin.   Menu Does not Include Conditional Coloring Tab Example Use Case The dashboard has a polar-type widget that displays dimensions. You want to highlight bars based on their value: Bars representing high values should be red. Medium values should be yellow. Low values should be green. This conditional coloring helps users quickly identify areas above or below the expected value range. Script Breakdown The script is designed to apply conditional coloring to a Sisense polar bar chart. Below is a detailed explanation of each part of the code:     // Widget Script to change color of Polar Charts based on value // Designed for Polar Chart with only values and no category panel widget.on('processresult', function (w, args) { var dataSeries = args.result.series; // Loop through all values for (let series_index = 0; series_index < dataSeries.length; series_index++) { // Last data point is where non-zero bar is present when no Category panel is used in Polar charts var lastDataIndex = dataSeries[series_index].data.length - 1; var lastDataPoint = dataSeries[series_index].data[lastDataIndex]; // Change numerical cutoffs and color code names as needed if (lastDataPoint.y && lastDataPoint.y > 75) { // Change to CSS Color Name or CSS color code as needed lastDataPoint.color = "red"; } else if (lastDataPoint.y && lastDataPoint.y > 25) { // Change to CSS Color Name or CSS color code as needed lastDataPoint.color = "#ffd54d"; } else { // Change to CSS Color Name or CSS color code as needed lastDataPoint.color = "green"; } } });       Event Listener Registration widget. on ( 'processresult' , function ( w, args ) {       widget.on('processresult', function (w, args)     The script uses the widget event process result. This event occurs when the data JAQL has returned raw results and the data can be structured into the final processed results for the widget render event. Accessing Data Series     var dataSeries = args.result.series;     The args.result.series object contains the data series that the polar chart will display. Each series represents a set of data points, ultimately bars in the widget. Looping Through Data Points     for (let series_index = 0; series_index < dataSeries.length; series_index++)       The script loops through each data series to access individual data points. If the categories panel is used a second nested loop can used to loop through all values.   Identifying the Last Data Point     // Last data point is where non-zero bar is present when no Category panel is used in Polar charts var lastDataIndex = dataSeries[series_index].data.length - 1; var lastDataPoint = dataSeries[series_index].data[lastDataIndex];     For polar widgets with only value metadata panels, the last data point in each series determines the visual bar coloring. The lastDataIndex variable holds the index in the series of the last data point, and lastDataPoint is the actual data point object. Conditional Coloring     // Change numerical cutoffs and color code names as needed if (lastDataPoint.y && lastDataPoint.y > 75) { // Change to CSS Color Name or CSS color code as needed lastDataPoint.color = "red"; } else if (lastDataPoint.y && lastDataPoint.y > 25) { // Change to CSS Color Name or CSS color code as needed lastDataPoint.color = "#ffd54d"; } else { // Change to CSS Color Name or CSS color code as needed lastDataPoint.color = "green"; }     The script applies color based on the value ( y ) of the last data point. The exact conditional and ranges are specific to the exact desired coloring and data being represented, and any Javascript conditional can used, not just simple numerical cutoffs, more complex math or logic can be used as needed.   Conclusion This script provides a straightforward way to add conditional coloring to polar-type widgets in Sisense dashboards, enhancing the visual impact and data visualization capability. By customizing the conditions and colors, you can adapt the script to fit various requirements and use cases. This code template can also used within plugins largely as is, if this functionality is desired as universal functionality throughout the Sisense server. Scatter Widget With Conditional Coloring Check out this related content:  Academy Course Sisense Documentation 

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago
      0
               
    • Blog banner
      • Embedding AnalyticsChevronRightIcon

      Demonstrating ComposeSDK Styling Of Sisense Dashboard Widgets

                                                                                               

      Demonstrating ComposeSDK Styling Of Existing Sisense Dashboard Widgets When developing   ComposeSDK   embedded applications, there are three principal techniques for embedding or converting an existing Sisense widget. Discussing each method, and some of the benefits associated with them: Direct Rendering existing Sisense Dashboard Widgets with the   DashboardWidget   Function: The expedited approach involves rendering an existing Sisense widget directly using the   DashboardWidget   function. While this method ensures swift integration, it does not allow all data options of the widget to be edited within ComposeSDK or defined as variables, one of the features of native ComposeSDK widgets. Nevertheless, customization of other   parameters, including styling , remains feasible based on the props used. Mandatory parameters include the dashboard and widget ID, which allow the rendering of an existing Sisense widget. Widget Recreation using ComposeSDK to Create Native ComposeSDK widgets An alternative method,   detailed in this article , entails recreating the widget as a native ComposeSDK widget by employing an existing Sisense widget as a template for a new not directly linked ComposeSDK widget. Though potentially more time-intensive, this approach yields a fully customizable native ComposeSDK widget. It stands independent of any specific widget or dashboard on the Sisense server, enabling complete independence to changes made to the original dashboard or widget, or complete deletion of the widget used as the model. Loading Widget Metadata with the useGetWidgetModel function Leveraging the  useGetWidgetModel  ComposeSDK function provides a middle option, detailed in this article . It allows automating the return of widget metadata from an existing Sisense widget, facilitating dynamic modifications within ComposeSDK. This method balances somewhat the autonomy of entirely recreating a widget as a native ComposeSDK widget and rendering a widget as a Dashboard Widget. In this article, we will demonstrate and discuss the DashboardWidget rendering feature, a powerful capability within ComposeSDK that allows the embedding and rendering of existing widgets. The focus will be on exploring the large number of styling options provided by this feature. Among the properties of a DashboardWidget component, the styleOptions parameter determines the styling. This parameter accepts a DashboardWidgetStyleOptions object , which includes a large number of parameters documented in detail below and in this documentation page . To demonstrate a practical implementation of using these styling options, the following code example showcases a Compose DashboardWidget utilizing all documented parameters within the DashboardWidgetStyleOptions object.     import { DashboardWidget } from "@sisense/sdk-ui"; export function ComposeSDKChart(props) { let styleOptions = { "backgroundColor": "lightblue", "border": true, "borderColor": "green", "cornerRadius": "Large", "shadow": "Dark", "spaceAround": "Large", "header": { "hidden": false, "titleAlignment": "Center", "backgroundColor": "lightblue", "titleTextColor": "blue" }, "height": 200, "width": 200 }; return (<DashboardWidget widgetOid={'65ab8958857ff900335db870'} dashboardOid={'65ab8948857ff900335db86e'} styleOptions={styleOptions} title={"My Chart"} />) }        The final result in this example is the widget below, showing the effect of this styling in this specific case:   Discussing each style option individually, including the type of parameter accepted by each style setting. backgroundColor: Type: string - Specifically a string containing an  HTML color Description: This option sets the background color of the widget. In the example, the background color is set to "lightblue," a preset standard HTML color code that is equal to #ADD8E6 as a hex color code. border: Type: boolean  Description:  This option determines whether the widget container has a border or not. In the example, the border is enabled with the value set to true , adding a defined boundary to the widget that can be styled. borderColor: Type: string - Specifically a string containing an  HTML color , Description: Specifies the color of the widget container's border.  cornerRadius: Type: "Large" | "Medium" | "Small" - Three specific options set as specific strings Description: Defines the corner radius of the widget container, similar to the CSS property of the same name, allowing for customization of the widget container's shape. header: Type: object- Contains various parameters that control the styling of the header and title. Description: A style object to customize the widget header. It includes options like background color, divider line toggle, divider line color, header visibility toggle, title alignment, and title text color. header.backgroundColor: Type: string - Specifically a string containing an HTML color Description: Sets the background color of the widget header. header.dividerLine: Type: boolean Description: Controls the visibility of the divider line between the widget header and the chart. header.dividerLineColor Type: string - Specifically a string containing an HTML color Description: Specifies the color of the divider line, if visible. header.hidden Type: boolean Description:   Toggles the visibility of the header and title. header.titleAlignment Type: "Left" | "Center" - Two specific options set as specific strings Description: Alignment of the title within the header. The example centers the title using the value "Center. header.titleTextColor Type: string- Specifically a string containing an HTML color Description: Specifies the text color of the header title. height: Type: number - Number in pixels Description: Sets the total height of the widget in pixels. If not explicitly set will use the height of the container. width: Type: number - Number in pixels Description: Sets the total width of the widget in pixels. If not explicitly set will use the width of the container. shadow: Type: "Medium" | "Light" | "Dark" - Three specific options set as specific strings Description: Defines the shadow level of the widget container, similar to the CSS style of the same name.  Effects styling only when spaceAround is defined. spaceAround: Type: "Large" | "Medium" | "Small"  - Three specific options set as specific strings Description: Specifies the space between the widget container edge and the chart.   With these styling options, one can modify the visual styling of a ComposeSDK DashboardWidget, enabling a large amount of style customization for an otherwise unchanged existing dashboard widget selected from an existing dashboard on the Sisense server. While ComposeSDK DashboardWidgets may not offer the extensive modification capabilities available to native ComposeSDK widgets, such as the ability to completely modify all of widget data options, they still provide a large degree of extensive visual customization through the use of styling options. Share your experience in the comments!   

      Jeremy Friedel
      Jeremy FriedelPosted 2 years ago • Last reply 2 years ago
      1
               
    • Blog banner
      • Add-ons & Plug-InsChevronRightIcon

      Plugin - DisableLogoLink

                                                       

      Plugin - Disable Link To Sisense Server Homepage In Top Left Logo Icon This plugin disables the link to the home page of the top left logo in the Sisense UI. The plugin also removes the logo icon being treated as a link on mouse hover. This logo image may be the default Sisense logo or a custom logo icon.   Installation To install this plugin, download and unzip the attachment. Then drop the disableLogoLink folder into your plugins folder (/opt/sisense/storage/plugins). Enable the plugin on the Add-ons tab in the Admin section, wait for the plugin to build, and the plugin will be enabled. The plugin API can also be used, as well as the file management UI. Notes This plugin is a good example of using both custom Javascript and CSS in a Sisense plugin to modify the Sisense UI, using concise code.   The main Javascript file of this plugin uses no external libraries or Sisense-specific events or code and uses the jQuery library that is already loaded in the Sisense UI.  Using two lines of code the link of the top left logo is removed and the mouse hover behavior is modified.   // Disable Link by setting address to null $(".prism-header__logo").href = null; // Disable pointer cursor hover to default cursor as logo no longer link $(".prism-header__logo").css("cursor", 'auto');     The CSS file loaded by the plugin is used to modify the mouse hover behavior further to remove any indication the logo is a link and uses standard CSS parameters and selectors.   /* Disable Pointer link and link cursor via CSS */ .prism-header__logo { pointer-events: none; cursor: default; text-decoration: none; }      The plugin.json file of the plugin is used to load both the Javascript and CSS files, which are placed in separate JS and Styles folders.   { "name": "disableLogoLink", "source": [ "js/disableLink.js" ], "style": [ "styles/style.css" ], "lastUpdate": "2024-02-23T00:04:45.445Z", "folderName": "disableLogoLink", "isEnabled": true, "version": "1.0.0" }   This plugin, which includes both CSS and Javascript functionality, can be used as a basic template for your own Sisense plugins that load custom Javascript and CSS.       How did the plugin work for you? What other type of plugin are you looking to learn more about? Let me know in the comments!

      Jeremy Friedel
      Jeremy FriedelPosted 2 years ago • Last reply 2 years ago
      1
               
      • Embedding AnalyticsChevronRightIcon

      Using "useGetWidgetModel" to Embed an Existing widget in ComposeSDK

                                                                                               

      Using "useGetWidgetModel" to Embed an Existing Widget in ComposeSDK When developing ComposeSDK embedded applications, there are three principal techniques for embedding or converting an existing Sisense widget. Discussing each method, and some of the benefits associated with them: Direct Rendering existing Sisense Dashboard Widgets with the DashboardWidget Function: The expedited approach involves rendering an existing Sisense widget directly using the DashboardWidget function. While this method ensures swift integration, it does not allow all data options of the widget to be edited within ComposeSDK or defined as variables, one of the features of native ComposeSDK widgets. Nevertheless, customization of other parameters, including styling , remains feasible based on the props used. Mandatory parameters include the dashboard and widget ID, which allow the rendering of an existing Sisense widget. Widget Recreation using ComposeSDK to Create Native ComposeSDK widgets An alternative method, detailed in this linked article , entails recreating the widget as a native ComposeSDK widget by employing an existing Sisense widget as a template for a new not directly linked ComposeSDK widget. Though potentially more time-intensive, this approach yields a fully customizable native ComposeSDK widget. It stands independent of any specific widget or dashboard on the Sisense server, enabling complete independence to changes made to the original dashboard or widget, or complete deletion of the widget used as the model. Loading Widget Metadata with the useGetWidgetModel function Leveraging the useGetWidgetModel ComposeSDK function provides a middle ground. It allows automating the return of widget metadata from an existing Sisense widget, facilitating dynamic modifications within ComposeSDK. This method balances somewhat the autonomy of entirely recreating a widget as a native ComposeSDK widget and rendering a widget as a Dashboard Widget. Using the format below, the entire ComposeSDK metadata equivalent of an existing Sisense widget can be imported and be used directly to render a direct ComposeSDK equivalent:   import { Chart, useGetWidgetModel } from "@sisense/sdk-ui"; /** * Retrieves and renders a ComposeSDK equivalent of an existing Sisense widget using one of three methods. * Method 1: Rendering an existing Sisense widget directly as a ComposeSDK DashboardWidget. * Method 2: Recreating the widget independently as a native ComposeSDK widget using a Sisense widget template. * Method 3: Programmatically loading ComposeSDK metadata using the useGetWidgetModel function. * * @param {string} dashboardOid - The ID of the Sisense dashboard containing the widget. * @param {string} widgetOid - The OID of the Sisense widget to be converted. * @returns {JSX.Element} - The ComposeSDK Chart component rendering the converted widget. */ const convertSisenseWidgetToComposeSDK = ({ dashboardOid, widgetOid }) => { const { widget, isLoading, isError } = useGetWidgetModel({ dashboardOid, widgetOid, }); if (isLoading) { return <div>Loading...</div>; } if (isError) { return <div>Error</div>; } if (widget) { // Extract ComposeSDK metadata equivalent of the Sisense widget const widgetProps = widget.getChartProps(); // Render the ComposeSDK Chart component with the converted widget metadata return <Chart {...widgetProps} />; } return null; }; // Example usage: const sisenseToComposeSDKWidget = convertSisenseWidgetToComposeSDK({ dashboardOid: 'yourDashboardID', widgetOid: 'yourWidgetOID', });   The variable widgetProps contains the entire widget metadata equivalent of the Sisense widget. This is a standard Javascript object that contains all of the Sisense metadata in the format ComposeSDK expects. These parameters can be changed in the object similar to any other Javascript object. The useGetWidgetModel function, introduced in the latest ComposeSDK version as of January 2024, fetches the entire ComposeSDK metadata equivalent of a Sisense widget. However, certain errors may arise when the metadata returned is used to render a widget, necessitating minor adjustments to the props returned. For instance, the widget dataSource parameter can includes the server name, which is often "localhost," triggering an "ElastiCube not found" error in ComposeSDK. Mitigate this is possible by eliminating "localhost/" from the returned prop object:   widgetProps.dataSet = widgetProps.dataSet.replace("localhost/" , "")    Similar changes can be made to any other parameters where issues may occur. It is important to note that widgets rendered using useGetWidgetModel retain all limitations and variations of ComposeSDK, and unsupported widget types in ComposeSDK are still not renderable. As an example, ComposeSDK widgets require a Value metadata item, while native Sisense widgets can render with only a category panel item. Additionally, if left permanently in the code, the Sisense widget and its corresponding dashboard must persist, as useGetWidgetModel fetches the widget metadata on each call. As a result, any changes made to the widget in native Sisense will be reflected, which can be useful. Similar to the method discussed here , useGetWidgetModel  can facilitate the conversion of a widget into a fully native ComposeSDK model instead of using the web console of native Sisense, the returned props from "useGetWidgetModel" can be console logged and copied directly. Once the necessary metadata is copied, the function can be "useGetWidgetModel", and the widget will be a fully native ComposeSDK widget. A native Sisense widget: The widget is rendered using ComposeSDK and useGetWidgetModel: The widget metadata returned console logged. These can be copied directly from the console, avoiding potentially the continual use of useGetWidgetModel. Share your experience in the comments!   

      Jeremy Friedel
      Jeremy FriedelPosted 2 years ago
      0