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
    Embedding
    • 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
               
    • Micael Santana

      Knowledge Base Docs

               
      Micael Santana
      Posted 2 weeks ago
      How to Replace Plugin Widgets in React Compose SDK [Linux]
                               

      This article explains how to replace plugin widgets inside embedded dashboards that use Compose SDK. You’ll learn how the system works internally, registration functions, and how to override the default behavior of the registration. Step 1: Understand the goal When working with embedded dashboards that contain Custom Widget Plugins, for example: Histogram, Tabber etc. The default behavior of CSDK is show an error placeholder: The main idea of Custom Widgets is to replace them, so the CSDK renders it instead of the original widget inside the DashboardById component. Step 2: Custom Component To start you must create the component itself, to do it you should implement CustomWidgetComponent , and fetch widget data using the useExecuteCustomWidgetQuery hook. import { CustomWidgetComponent, useExecuteCustomWidgetQuery } from '@sisense/sdk-ui'; const ResultsTable: CustomWidgetComponent = (props) => { const { data } = useExecuteCustomWidgetQuery(props); if (!data) { return null; } return ( <table style={{ margin: '20px' }}> <thead> <tr> {data.columns.map((column, columnIndex) => ( <th key={columnIndex}>{column.name}</th> ))} </tr> </thead> <tbody> {data.rows.map((row, rowIndex) => ( <tr key={rowIndex}> {row.map((cell, cellIndex) => ( <td key={cellIndex}>{cell.text}</td> ))} </tr> ))} </tbody> </table> ); }; What it does: The component itself it's just a component from React, so you implement: charts, simple tables etc. The key is the useExecuteCustomWidgetQuery that executes the widget query and returns structured data so your component can render however you want. Note: If you prefer working with raw query results instead of formatted data, you should use extractDimensionsAndMeasures together with useExecuteQuery . Step 3: The Data The CustomWidgetComponent receives properties similar to those by plugins internally, like: metadata, filters, configuration. This means: Some logic from plugin widgets can often be reused. Data remains compatible with Fusion. Migration from plugin widgets → Compose SDK is simplified Compose SDK mainly handles rendering, while configuration and data still come from the Sisense. Step 4: Register the custom widget After creating your component, you must register it before rendering the dashboard. Registration tells the SDK which component should be used when it encounters a Custom Widget Type. import {DashboardById,useCustomWidgets }from'@sisense/sdk-ui'; functionApp() { const { registerCustomWidget }=useCustomWidgets(); registerCustomWidget('histogramwidget',ResultsTable); return<DashboardByIddashboardOid={'66f4d4dd384428002ae0a21d'}/>; } Important Registration must happen before the dashboard renders. Once registered, the widget is stored inside the main Compose SDK context. Any dashboard within that context will automatically use your custom widget. Step 5: Understand more about how it works internally Internally, the SDK keeps registered widgets inside a registry managed by the CSDK Context: So the rendering flow will be: Dashboard loads widget metadata. CSDK checks if that widget type exists in the registry: If registered → renders your custom component If not → renders the error placeholder The registry uses a key-value structure for fast lookup and to avoid repeated registrations. Step 6: Why widgets are designed to register only once Registration happens once per context lifecycle by design. This provides several benefits: Performance: prevents repeated registrations on re-renders Predictability: ensures only one implementation per widget type Stability: avoids conflicts between components trying to register the same widget Memory safety: prevents the registry from growing uncontrollably Since registrations are stored in a Context map, calling registerCustomWidget multiple times is unnecessary and discouraged. Step 7: How to unregister it? If you need to unregister a component, recently we added a new method named unregisterCustomWidget , to the CSDK Custom Widgets, that you can use to do it: Note: this was added in v2.26.0 , of Compose SDK. import { useCustomWidgets } from '@sisense/sdk-ui'; export const FunnelWidget = () => { const { registerCustomWidget, unregisterCustomWidget } = useCustomWidgets(); const [type, setType] = useState<'funnel' | 'treemap'>('funnel'); const widget = type === 'funnel' ? CustomFunnel : CustomTreemap; useEffect(() => { registerCustomWidget('histogramwidget', widget); return () => unregisterCustomWidget('histogramwidget'); }, [registerCustomWidget, unregisterCustomWidget, widget]); return ( <div> <Select value={type} onValueChange={(v) => setType(v as 'funnel' | 'treemap')} > <SelectTrigger className="w-[180px] my-3"> <SelectValue placeholder="Chart Type" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectItem value="funnel">Funnel</SelectItem> <SelectItem value="treemap">Treemap</SelectItem> </SelectGroup> </SelectContent> </Select> <DashboardById key={`dashboard-${type}`} dashboardOid={'68dae1429e8028a69c6a41ee'} /> </div> ); }; Using it you don't need to re-render the Sisense Main Context to change the register type, but you still need to re-render the Dashboard Component. Best practice recommendation Register custom widgets once during application initialization whenever possible. Only force a context remount if you specifically need dynamic registration behavior. Conclusion:  Custom Widgets in Compose SDK provide a powerful way to replace plugin widgets while still using the original widget data. Understanding that registration is stored in a context-level registry explains: why widgets are registered once, why repeated registration isn’t needed and why remounting the context is sometimes required for advanced scenarios. References/Related Content  Compose SDK documentation: DashboardById Component . Compose SDK documentation: Custom Widgets . Compose SDK documentation: Sinsense Context Provider . Mentioned Plugins: Tabber , Histogram . 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.

                                             
      0
               
    • Blog banner
      • Embedding AnalyticsChevronRightIcon

      Adding an expand widget button in SisenseJS embedding [Linux]

                                       

      Introduction:  An article demonstrates a way to enhance SisenseJS embedded dashboards by adding an "Expand" widget button. This allows to use the space more efficiently by embedding smaller versions of widgets but granting users with an option to view larger, detailed versions of charts in a modal, improving data readability and user experience. Step-by-Step Guide: Step 1: HTML layout  A simple structure with a main container to render widgets in, div elements for overlay, a modal, a widget container inside a modal, and a button as a close icon. <body>   <div id="sisenseApp" style="display: flex; width:100%">     <main id="main" style="display: flex; flex-direction: column; justify-content: center; width: 80%">              <!-- The Modal Overlay -->       <div class="overlay">         <div class="modal">           <div class="modalWidgetContainer"></div>           <button class="close-btn">              <svg>...</svg> <!-- Close Icon -->           </button>         </div>       </div>            </main>   </div> </body> Step 2: Basic styles for the Modal  Add CSS to handle the visibility of the modal window and define the sizing for widget containers. The .isOpen class will be toggled dynamically later. CSS <style>   .overlay {     position: fixed;     inset: 0;     background-color: rgba(0, 0, 0, 0.2);     visibility: hidden;     z-index: 3;   }   .modal {     position: absolute;     inset: 5%;     background-color: white;   }   .close-btn {     position: absolute;     top: 5px;     right: 0;     cursor: pointer;     z-index: 5;   }   .isOpen {     opacity: 1;     visibility: visible;   }   .primaryWidgetContainer {     width: 400px;     height: 400px;     border: 1px solid sandybrown;   }   .modalWidgetContainer {     width: 100%;     height: 100%;     border: 1px solid sandybrown;   } </style> Step 3: Connect to SisenseJS and Render Widgets  Dynamically load the Sisense.v1.js library, connect to Sisense instance, and fetch the widgets associated with a specific dashboard ID. For each widget loaded, we generate an "Expand" button. javascript const url = document.location.origin; const dashboardId = "697aa04c63601f8c9e5a478c"; // Replace with your dashboard ID const startSisense = async () => { const sisensejs = document.createElement('script'); sisensejs.src = url + '/js/sisense.v1.js'; sisensejs.onload = async () => { await renderdash(); } document.head.append(sisensejs); } const renderdash = async () => { const main = document.getElementById("main"); let app = window.Sisense.app; if (!app) { app = await Sisense.connect(url, true); window.Sisense.app = app; } let dash = new Dashboard(); app.dashboards.add(dash); // Fetch widgets from the dashboard const widgetsRaw = await fetch(url + "/api/v1/dashboards/" + dashboardId + "/widgets?fields=oid", { method: 'GET', credentials: 'include' }).then(r => r.json()); if (widgetsRaw.length) { const promises = widgetsRaw.map(w => dash.widgets.load(w.oid)); const widgetsResolved = await Promise.all(promises); widgetsResolved.forEach(widget => { const groupWrapper = document.createElement('div'); const widgetContainerEl = document.createElement('div'); widgetContainerEl.classList.add('primaryWidgetContainer'); widgetContainerEl.setAttribute("id", "widget_" + widget.$$model.oid); const expandBtnEl = document.createElement('button'); expandBtnEl.textContent = "Expand"; // Bind the modal open event with the current widget context expandBtnEl.addEventListener('click', () => handleModalOpen(widget)); main.append(groupWrapper); groupWrapper.append(widgetContainerEl, expandBtnEl); // Render widget into primary container widget.container = document.getElementById("widget_" + widget.$$model.oid); }); dash.refresh(); } } startSisense(); Step 4: Handling the Modal State and Widget Re-rendering   A SisenseJS widget cannot be simultaneously rendered in two different elements in the DOM. Therefore, when opening the modal, we must first destroy() the widget in the main view and initialize() it inside the modal container. We reverse this process when the user closes the modal. const overlayEl = document.querySelector('.overlay'); const closeModalBtn = document.querySelector('.close-btn'); const modalWidgetContainer = document.querySelector('.modalWidgetContainer'); let activeWidget = null; const handleModalOpen = (w) => {   overlayEl.classList.add('isOpen');   // Destroy the widget and repopulate inside the modal   w.destroy();   w.initialize();   w.container = modalWidgetContainer;      closeModalBtn.addEventListener('click', handleModalClose);   activeWidget = w; } const handleModalClose = () => {   overlayEl.classList.remove('isOpen');      if (activeWidget) {     // Reverse the process to push the widget back to the main view     activeWidget.destroy();     activeWidget.initialize();     activeWidget.container = document.getElementById("widget_" + activeWidget.$$model.oid);   }      closeModalBtn.removeEventListener('click', handleModalClose);   activeWidget = null; } (Full index.html can be found in the comments section) Note: This feature may be beneficial for all native chart types, such as Column, Line, Bar Charts, Scatter Plot/Map, etc. The exceptions are Pivot and Indicator widgets, which don’t get properly expanded. Note: The example doesn’t include authentication and is designed to be tested within the Sisense itself. Upload the HTML file into /opt/sisense/storage/plugins and it will be accessible at your_sisense_url/plugins/sisensejs.html Conclusion:   Adding an expansion feature is an effective way to improve the user experience with embedded dashboards, allowing clients to examine deeper chart details. By understanding that SisenseJS widgets require destruction and re-initialization before being assigned to a new DOM container, you can successfully move them freely across your application while maintaining high interactivity and performance. References/Related Content  https://developer.sisense.com/guides/embeddingCharts/sisense.js/ https://developer.sisense.com/guides/embeddingCharts/jsGettingStarted.html 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.

      Ivan Amoshyi
      Ivan AmoshyiPosted 3 weeks ago
      0
               
    • Blog banner
      • Embedding AnalyticsChevronRightIcon

      Adding an expand widget button in SisenseJS embedding [Linux]

                                       

      Introduction:  An article demonstrates a way to enhance SisenseJS embedded dashboards by adding an "Expand" widget button. This allows to use the space more efficiently by embedding smaller versions of widgets but granting users with an option to view larger, detailed versions of charts in a modal, improving data readability and user experience. Step-by-Step Guide: Step 1: HTML layout  A simple structure with a main container to render widgets in, div elements for overlay, a modal, a widget container inside a modal, and a button as a close icon. <body>   <div id="sisenseApp" style="display: flex; width:100%">     <main id="main" style="display: flex; flex-direction: column; justify-content: center; width: 80%">              <!-- The Modal Overlay -->       <div class="overlay">         <div class="modal">           <div class="modalWidgetContainer"></div>           <button class="close-btn">              <svg>...</svg> <!-- Close Icon -->           </button>         </div>       </div>            </main>   </div> </body> Step 2: Basic styles for the Modal  Add CSS to handle the visibility of the modal window and define the sizing for widget containers. The .isOpen class will be toggled dynamically later. CSS <style>   .overlay {     position: fixed;     inset: 0;     background-color: rgba(0, 0, 0, 0.2);     visibility: hidden;     z-index: 3;   }   .modal {     position: absolute;     inset: 5%;     background-color: white;   }   .close-btn {     position: absolute;     top: 5px;     right: 0;     cursor: pointer;     z-index: 5;   }   .isOpen {     opacity: 1;     visibility: visible;   }   .primaryWidgetContainer {     width: 400px;     height: 400px;     border: 1px solid sandybrown;   }   .modalWidgetContainer {     width: 100%;     height: 100%;     border: 1px solid sandybrown;   } </style> Step 3: Connect to SisenseJS and Render Widgets  Dynamically load the Sisense.v1.js library, connect to Sisense instance, and fetch the widgets associated with a specific dashboard ID. For each widget loaded, we generate an "Expand" button. javascript const url = document.location.origin; const dashboardId = "697aa04c63601f8c9e5a478c"; // Replace with your dashboard ID const startSisense = async () => { const sisensejs = document.createElement('script'); sisensejs.src = url + '/js/sisense.v1.js'; sisensejs.onload = async () => { await renderdash(); } document.head.append(sisensejs); } const renderdash = async () => { const main = document.getElementById("main"); let app = window.Sisense.app; if (!app) { app = await Sisense.connect(url, true); window.Sisense.app = app; } let dash = new Dashboard(); app.dashboards.add(dash); // Fetch widgets from the dashboard const widgetsRaw = await fetch(url + "/api/v1/dashboards/" + dashboardId + "/widgets?fields=oid", { method: 'GET', credentials: 'include' }).then(r => r.json()); if (widgetsRaw.length) { const promises = widgetsRaw.map(w => dash.widgets.load(w.oid)); const widgetsResolved = await Promise.all(promises); widgetsResolved.forEach(widget => { const groupWrapper = document.createElement('div'); const widgetContainerEl = document.createElement('div'); widgetContainerEl.classList.add('primaryWidgetContainer'); widgetContainerEl.setAttribute("id", "widget_" + widget.$$model.oid); const expandBtnEl = document.createElement('button'); expandBtnEl.textContent = "Expand"; // Bind the modal open event with the current widget context expandBtnEl.addEventListener('click', () => handleModalOpen(widget)); main.append(groupWrapper); groupWrapper.append(widgetContainerEl, expandBtnEl); // Render widget into primary container widget.container = document.getElementById("widget_" + widget.$$model.oid); }); dash.refresh(); } } startSisense(); Step 4: Handling the Modal State and Widget Re-rendering   A SisenseJS widget cannot be simultaneously rendered in two different elements in the DOM. Therefore, when opening the modal, we must first destroy() the widget in the main view and initialize() it inside the modal container. We reverse this process when the user closes the modal. const overlayEl = document.querySelector('.overlay'); const closeModalBtn = document.querySelector('.close-btn'); const modalWidgetContainer = document.querySelector('.modalWidgetContainer'); let activeWidget = null; const handleModalOpen = (w) => {   overlayEl.classList.add('isOpen');   // Destroy the widget and repopulate inside the modal   w.destroy();   w.initialize();   w.container = modalWidgetContainer;      closeModalBtn.addEventListener('click', handleModalClose);   activeWidget = w; } const handleModalClose = () => {   overlayEl.classList.remove('isOpen');      if (activeWidget) {     // Reverse the process to push the widget back to the main view     activeWidget.destroy();     activeWidget.initialize();     activeWidget.container = document.getElementById("widget_" + activeWidget.$$model.oid);   }      closeModalBtn.removeEventListener('click', handleModalClose);   activeWidget = null; } (Full index.html can be found in the comments section) Note: This feature may be beneficial for all native chart types, such as Column, Line, Bar Charts, Scatter Plot/Map, etc. The exceptions are Pivot and Indicator widgets, which don’t get properly expanded. Note: The example doesn’t include authentication and is designed to be tested within the Sisense itself. Upload the HTML file into /opt/sisense/storage/plugins and it will be accessible at your_sisense_url/plugins/sisensejs.html Conclusion:   Adding an expansion feature is an effective way to improve the user experience with embedded dashboards, allowing clients to examine deeper chart details. By understanding that SisenseJS widgets require destruction and re-initialization before being assigned to a new DOM container, you can successfully move them freely across your application while maintaining high interactivity and performance. References/Related Content  https://developer.sisense.com/guides/embeddingCharts/sisense.js/ https://developer.sisense.com/guides/embeddingCharts/jsGettingStarted.html 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.

      Ivan Amoshyi
      Ivan AmoshyiPosted 3 weeks ago
      0
               
      • Product Feedback ForumChevronRightIcon
      CORP Support for iFrame Embedding
               
      SamAlbertCSM
      SamAlbertCSMPosted 6 months ago • Last reply 6 months ago
               
      1
               
      • Product Feedback ForumChevronRightIcon
      Dynamic link generation based on values in table/pivot cells in ComposeSDK
                               
      Miroslav_Y
      Miroslav_YPosted 6 months ago
               
      0
               
      • Product Feedback ForumChevronRightIcon
      scripts and plugins working with ComposeSDK
                       
      Martha
      MarthaPosted 7 months ago
               
      0
               
      • Product Feedback ForumChevronRightIcon
      Add indicator for downloading a widget
                       
      ccoelho
      ccoelhoPosted 1 year ago • Last reply 7 months ago
               
      2
               
    • Bohdan

      Help and How-To

               
      Bohdan
      Posted 8 months ago • Last reply 8 months ago
      Update_with_SSL
                       

      Subject: Update  L2025.2.0.249 fails at nginx-ingress (IngressClass ownership conflict) Hello Sisense Support We’re update our single-node Sisense environment (L2025.2.0.249) from HTTP to HTTPS. After setting SSL in single_config.yaml, the installer consistently fails while deploying nginx-ingress. While the updating we encounter a error. Please see below logs

                                             
      4
               
      • Product Feedback ForumChevronRightIcon
      Responsive pivot table widths
                       
      Arpan
      ArpanPosted 8 months ago
               
      0
               
    …