Supercharging Your Tabular Views: AG Grid Powered by Sisense Compose SDK
Supercharging Your Tabular Views: AG Grid Powered by Sisense Compose SDK How to Build a Tabular View with Sisense Compose SDK and AG Grid This guide walks through how to use Sisense Compose SDK's query hooks to power an AG Grid table component in React. We'll break down the steps, explain the code, and highlight the key functionalities of AG Grid. Introduction Tables are essential in BI applications for summarizing large datasets and providing users with a simple way to navigate, filter, and analyze data. AG Grid is an ideal solution for tabular views, offering robust capabilities like sorting, filtering, grouping, and data exporting. When combined with the Sisense Compose SDK, you can power real-time, data-driven applications that provide users with seamless, customizable data interactions. In this example, we are using a sample eCommerce dataset, available through the Sisense Compose SDK free trial, to demonstrate how powerful this combination can be. Users can explore product sales by country, age range, and time period in a fully interactive grid. AG Grid's advanced tabular capabilities include features like multi-level row grouping, custom value formatting, pivot mode for creating complex data hierarchies, and the ability to export data to formats like CSV. These features, when integrated with Sisense's query hooks and real-time data, enable developers to create highly dynamic dashboards where users can manipulate large datasets with ease. This combination of Sisense Compose SDK and AG Grid empowers developers to create rich, interactive data experiences, allowing users to filter and manipulate data at granular levels, all while leveraging real-time querying powered by Sisense. Step-by-Step Breakdown of the Code Setting Up the Project packages used: npm install ag-grid-react ag-grid-community @mui/material @sisense/sdk-ui @sisense/sdk-data Register AG Grid Modules AG Grid uses modules to enable functionality like client-side row models, and sorting and filtering. We register the modules to be used within AG Grid: import { AgGridReact } from "ag-grid-react"; import "ag-grid-community/styles/ag-grid.css"; import "ag-grid-community/styles/ag-theme-alpine.css"; These imports ensure that AG Grid is properly styled and functional. Setting Up the Query with Sisense SDK Sisense Compose SDK’s `useExecuteQuery` is used to fetch data from your data sources. The query can include dimensions and measures which AG Grid will render. const queryProps = useMemo(() => ({ dataSource: DM.DataSource, dimensions: [DM.Country.Country, DM.Commerce.AgeRange, DM.Commerce.Date.Years], measures: [ measureFactory.sum(DM.Commerce.Revenue, "Total Revenue"), measureFactory.sum(DM.Commerce.Quantity, "Total Quantity"), ], }), []); Here, `useExecuteQuery` executes the query based on the defined data source (`DM.DataSource`), dimensions (e.g., country, age range), and measures (e.g., revenue, quantity). Fetching and Displaying Data We leverage React's `useEffect` hook to update the state of `rowData` once data is fetched. This ensures AG Grid displays up-to-date information. const { data, isLoading, isError } = useExecuteQuery(queryProps); useEffect(() => { if (!isLoading && !isError && data) { const rows = data.rows.map((row) => ({ country: row[0]?.text || "N/A", ageRange: row[1]?.text || "N/A", year: row[2]?.text || "N/A", revenue: row[3]?.data || 0, quantity: row[4]?.data || 0, })); setRowData(rows); } }, [data, isLoading, isError]); This block processes the raw data returned from the query and formats it for use in the AG Grid. Column Definitions AG Grid requires column definitions that define how each field should be displayed. const columnDefs = useMemo(() => [ { field: "country", headerName: "Country" }, { field: "ageRange", headerName: "Age Range" }, { field: "year", headerName: "Year" }, { field: "revenue", headerName: "Total Revenue", valueFormatter: (params) => abbreviateNumber(params.value), // Helper function for number formatting }, { field: "quantity", headerName: "Total Quantity", valueFormatter: (params) => abbreviateNumber(params.value), }, ], []); We define five columns: country, age range, year, revenue, and quantity. The `valueFormatter` function ensures that numbers are displayed in an abbreviated format (e.g., "1.2K" for thousands). AG Grid Configuration The grid configuration includes `defaultColDef` for common properties across all columns (like filtering and sorting), and `animateRows` for smoother transitions. const defaultColDef = useMemo(() => ({ flex: 1, minWidth: 100, sortable: true, filter: true, resizable: true, }), []); Here, all columns are set to be sortable, filterable, and resizable by default. Exporting Data AG Grid’s API allows exporting table data to CSV. We use a button to trigger the export functionality: const onBtnExport = useCallback(() => { gridRef.current.api.exportDataAsCsv(); }, []); Rendering the Grid Finally, we render the AG Grid component, passing the `rowData`, `columnDefs`, and `defaultColDef` as props: <AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} animateRows={true} /> This sets up the AG Grid to dynamically render data retrieved via Sisense Compose SDK. Value of Tabular Views Tabular views are crucial for presenting structured data, providing an easy way to explore, filter, and analyze datasets. AG Grid’s built-in features like sorting, filtering, and exporting make it a perfect fit for visualizing data-rich tables in BI environments. Features of AG Grid - Sorting and Filtering: Users can sort and filter data by column. - Grouping: Group rows by common fields. - Customization: Full flexibility in column definitions and row configurations. - Exporting: Allows exporting table data to CSV format. - Performance: Handles large datasets efficiently. Using Sisense Compose SDK to Power Components Sisense Compose SDK is an API-first approach to data querying, which powers the `useExecuteQuery` hook in this component. By combining it with AG Grid, you can easily visualize dynamic, real-time data in table format. Here is a functional example in Github https://github.com/sisensers/ag-grid-compose-sdk.git1.1KViews0likes0CommentsEmbedding Sisense Dashboards In Salesforce
Embedding Sisense content into the applications where end users spend most of their time is one of the best ways to leverage your analytics investment. In this post, we'll go over embedding a Sisense dashboard in Salesforce, using Salesforce objects to filter the dashboard to the right context. 1. Create a new VisualForce page From the Setup menu, select the Developer Console option The Developer Console will open, select File -> New -> Visualforce Page 2. Add an iFrame to embed the Sisense dashboard Add a standardController to specify the desired page where the Sisense dashboard will be added. In this example, we'll use the Account record page: <apex:page standardController="Account"> <apex:iframe src="https://<Sisense Server URL>/app/main#/dashboards/<dashboardID>?embed=true&h=f&t=f&r=f&l=f" width="100%" height="1024px" scrolling="true" id="sisenseIframe"/> </apex:page> Use the embed=true parameter to remove the headers, and also pass which page components you want displayed (toolbars, filters, navigation, etcetera). Documentation. 3. Make it dynamic Salesforce allows you to pass object values down to the embedded content. In this case, we will pass the Account ID to filter the dashboard to the account record. NOTE: The field you are passing as filter needs to exist in your Sisense data model. First, filter the dashboard by the column that contains the Salesforce object values, and get the encoded filter JAQL using the following post: passing dashboard filters by URL parameters. Find the part of the encoded JAQL that contains the dimensional value (in this example the Account ID), and replace it with the Salesforce placeholder: {!Account.Id}: filter=%5B%7B%22jaql%22%3A%7B%22table%22%3A%22Customers%22%2C%22column%22%3A%22SFDCAccountID%22%2C%22dim%22%3A%22%5BCustomers.SFDCAccountID%5D%22%2C%22datatype%22%3A%22text%22%2C%22merged%22%3Atrue%2C%22title%22%3A%22SFDCAccountID%22%2C%22filter%22%3A%7B%22explicit%22%3Atrue%2C%22multiSelection%22%3Atrue%2C%22members%22%3A%5B%22{!Account.Id}%22%5D%7D%2C%22collapsed%22%3Afalse%2C%22datasource%22%3A%7B%22title%22%3A%22MW%22%2C%22fullname%22%3A%22LocalHost%2FMW%22%2C%22id%22%3A%22aLOCALHOST_aMW%22%2C%22address%22%3A%22LocalHost%22%2C%22database%22%3A%22aMW%22%2C%22lastBuildTime%22%3A%222020-09-30T14%3A53%3A50.547Z%22%7D%7D%2C%22%24%24events%22%3A%7B%7D%2C%22instanceid%22%3A%22DC3F2-74F4-1C%22%2C%22%24filter%22%3A%7B%7D%2C%22%24permissionsService%22%3A%7B%22sourceToGlobalObjects%22%3A%7B%22dashboard%22%3A%22prism.activeDashboard.userAuth%22%2C%22widget%22%3A%22prism.activeWidget.dashboard.userAuth%22%7D%7D%2C%22isCascading%22%3Afalse%2C%22%24%24guid%22%3A%22B7684-3347-E875-9BBB%22%2C%22%24%24hashKey%22%3A%22object%3A2661%22%7D%5D Append it to the URL in the Visualforce page by adding &filter=<encoded jaql> Save the Visualforce page 4. Make the Visualforce Page Available In Salesforce, under Setup, search for Visualforce pages and edit the newly created page Check the "Available for Lighting Experience, Lighting Communities, and the mobile app" check box. Save the page 5. Add the Visualforce Page In Salesforce, under Setup, search for the page where the Sisense dashboard will be displayed. In this example, it's the Account Record Page. Click the Edit button The page layout will be opened. Choose Visualforce from the left side bar and drag it to the desired location in the layout. Select the new component, the settings will open in the right side. Select the newly created Visualforce page, add a label, and enter the desired height (in pixels) for the component. Save the page layout. Now the page will contain the Sisense dashboard, filtered down by the Salesforce object: Other Considerations: SSO must be set up for a seamless experience, otherwise users may get the Sisense login page when they don't have an active session in Sisense. Visualforce lets you use CSS and Javascript to customize height, width and other styling aspects of the iFrame. Make sure to line up the height of the iFrame with the height of the Lighting component containing the Visualforce page in the layout, so that users don't get multiple scrollbars.2.7KViews1like5CommentsSisense 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 Github2KViews3likes1CommentDemonstrating 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!1.2KViews1like1CommentDynamic Resize For Embedded IFrames
If you've ever attempted to dynamically resize your embedded iFrames in your parent application, you may have experienced a CORS conflict. Basically, since your parent application and sisense application serve from different domains, your browser restricts HTTP responses for security reasons. Luckily, we have a workaround that leverages the Window.postMessage() function. This solution dynamically resizes your embedded iFrame based on the height of the contents. // Within your SISENSE APPLICATION, apply this script on the dashboard to be embedded via iFrame: dashboard.on('refreshend', function () { var heightValue = 0, columnArr = dashboard.layout.columns, columnHeight = [], targetWindow = window.parent, // replace with your target domain targetOrigin = 'http://main.mytestapp.com:8083'; // iterate through columns for (var i = 0; i < columnArr.length; i++) { var cellsLength = columnArr[i].cells.length, heightTotal = 0; // increment through each column widget for (var j = 0; j < cellsLength; j++) { var height = columnArr[i].cells[j].subcells[0].elements[0].height; heightTotal += height; } columnHeight.push(heightTotal); // assign max aggregate column height heightValue = columnHeight.reduce(function (a, b) { return Math.max(a, b); }); } targetWindow.postMessage({ heightValue: heightValue }, targetOrigin); }); // Within your PARENT APPLICATION: window.addEventListener('message', receiveMessage, false); function receiveMessage(event) { // replace with your sisense application origin var sendingOrigin = 'http://reporting.mytestapp.com:8080'; if (event.origin !== sendingOrigin) { throw new Error('bad origin:', event.origin); } else { try { // replace with your iFrame ID document.getElementById('ifm').height = event.data.heightValue; } catch (err) { throw new Error(err.name + ':', err.message); } } }2.4KViews2likes1CommentUsing "useGetWidgetModel" to Embed an Existing widget in ComposeSDK
Loading Widget Metadata with the useGetWidgetModel function Leveraging the useGetWidgetModel ComposeSDK function provides a middle ground way of embedding existing Sisense widgets. It allows automating 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 directly as a Dashboard Widget.1.2KViews2likes0CommentsSisense.js Demo with Filter and Dashboard Changing with Native Web Components
This is a Sisense.js Demo built with React, which includes functionality such as changing filters and dashboards with native web components. This can be used as a reference or starting point for building and customizing Sisense.js applications with custom styling and functionality. React Sisense.js Demo Getting Started Make sure CORS is enabled and the location/port your hosting this applicaiton on is whitelisted on Sisense instance, documentation on changing CORS settings is here. Open the terminal (on Mac/Linux search for terminal, on Windows search for Powershell, Command Prompt, or Windows Subsystem for Linux if installed ) and set path to folder containing project (using cd command). Open the config file in the folder src folder and set Sisense Server URL to the IP or domain name of your Sisense server and set the dashboard id's and optionally filters keys. Run the command. npm install && npm start If npm and node is not installed, install them using the package manager or using nvm (Node version manager). Nvm can be installed by running this command in a terminal curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash Once install script is finished, follow any instructions listed in terminal, restart or open a new terminal and run these command to install node and npm: nvm install node nvm install-latest-npm To start hosting the site, run this command: npm install && npm start The default location is IP of hosting server on port 3000. If the IP address of the server hosting this program is 10.50.74.149 for example, then enter 10.50.74.149:3000 in the address bar of a browser to view application. if hosted locally enter your ip on port 3000 or enter localhost:3000 To use a different port, set the port variable in terminal using this command: export PORT=NEW_PORT_NUMBER and then run npm start. To run in the background use PM2 or system. Requirements Npm (and Node) - Installs all requirements Access to a Sisense instance Description This is a demonstration of some of the capabilities of Sisense.js and Sisense. It is a React single-page application using Typescript that can switch between multiple dashboards, loading all widgets in those dashboards to individual elements (created on dashboard switch). Multiple dashboard filters can be changed (and multiple members in a filter) using a native React dropdown bar to filter the data with the new filtered data visualized with the widgets in the dashboard. Changes made to filters using widgets in Sisense.js using right click are reflected in dropdown filter. Connecting to Sisense // Loads Sisense.js from Server, saves Sisense App copy in window as object to indicate already loaded to prevent loading twice const loadSisense = () => { // If Sisense is already loaded stop if (window.sisenseAppObject) { return; } // Loads sisense app object into app variable, edits being saved to dashboard set to false window.Sisense.connect(config.SisenseUrl, config.saveEdits) // Sisense app object .then((app: SisenseAppObject) => { // loads Sisense app object into window object, so connect is only run once, alternative is export and import window.sisenseAppObject = app; // Calls loadDashboard after Sisense is connected, uses initial widget and dashboard variables loadDashboard(config.DashboardConfig[0].DashboardID, config.DashboardConfig[0].DimArray); }) // error catching and log in console .catch((e: Error) => { console.error(e); }); } Loading Dashboard and Creating Container Element for each Widget // Function to load dashboard taking dashboard id as parameter to load dashboard. Every widget in dashboard is rendered into an element created in loop, looks for parent element with ID 'widgets'. On calling again existing widgets are replaced by new ones. const loadDashboard = (dashboardID: string = '') => { // if empty dashboard id return if (dashboardID === '') { return; } // load dashboard into being active dashboard window.sisenseAppObject.dashboards.load(dashboardID) // after load dashboard is in dash variable .then((dash: DashObject) => { window.currentDashObject = dash // array of loaded widgets // let widgetArray: Array<String> = prism.activeDashboard.widgets.toArray().map( let widgetArray: Array<String> = dash.$$widgets.$$widgets.map( function (widget: Widget) { // widget id for loading return widget.id } ); // set state with loaded dashboard if prism loaded if (widgetArray.length > 0) { this.setState((state, props) => { return { dashboardID: dash.id, }; }); } // get widgets element let widgetsElement: HTMLElement | null = document.getElementById(`widgets`); // type checking if (widgetsElement === null) { return; } // erase previous widgets widgetsElement.innerHTML = ''; // loop through array of widget arrays, loads them into containers by id with first in widget1, second in widget2 and so on widgetArray.forEach((widget, index) => { // check if they exist, type checking if (widgetsElement === null) { return; } // element to load widget into later let widgetElement: HTMLElement | null = document.createElement("div"); // Class included index for widget rendering later widgetElement.classList.add(`widget${index + 1}`, 'widget') // add empty div to widgets parent div widgetsElement.appendChild(widgetElement) // get widget and filter elements by ID let filterElement: HTMLElement | null = document.getElementById("filters"); // check if they exist, type checking if (widgetElement === null || filterElement === null) { return; } // Clear widget and filter elements from previous render filterElement.innerHTML = ''; // put widget in container element dash.widgets.get(widget).container = widgetElement; // Renders filter in HTML element with id of filters dash.renderFilters(filterElement); // reloads and refresh dashboard }); dash.refresh(); }) // error catching and log in console .catch((e: Error) => { console.error(e); }); } Changing a Dashboard Filter // Change a dashboard filter, takes dim to filter by, and new values in filter. const changeDashboardFilter = (dashboard: dashboard, dim: string, newFilterArray: Array<String>) => { // Find matching filter and to make changes to let filterToChange = dashboard.filters.$$filters.find(item => item.jaql.dim === `[${dim}]`); // If filter is undefined create a new filter if (filterToChange === undefined) { // Create the filter options let filterOptions = { // Save to dashboard save: false, // Refresh on change refresh: true, // If filter already used, make changes to that filter instead of creating new ones unionIfSameDimensionAndSameType: true }; // Create the jaql for the filter let jaql = { 'datatype': 'text', 'dim': dim, 'filter': { // Multiple items can be selected 'multiSelection': true, // New filter items 'members': newFilterArray, 'explicit': true }, }; // Create the filter jaql object let applyJaql = { jaql: jaql }; // Set the new filter using update function dashboard.$$model.filters.update(applyJaql, filterOptions); } if (filterToChange && filterToChange.$$model.jaql.filter) { let members = filterToChange.$$model.jaql.filter.members; // Check if members exist if (members !== undefined) { // Set members to new selected filter filterToChange.$$model.jaql.filter.members = newFilterArray; // Save the dashboard // dashboard.$$model.$dashboard.updateDashboard(dashboard.currentDashObject.$$model, "filters"); dashboard.filters.update(filterToChange, { refresh: true, save: false }); // Refresh the dashboard // dashboard.refresh(); } } } Clearing a filter Clearing a filter is done by simply setting the members to an empty array, which disables the filter. changeFilter([]); Monitor For Filter Change to Ensure Dropdown Accurately Displays Filter Change // Watch for element change to indicate filter changes and make change to dropdown if filters don't match displayed filter in dropdown const watchForFilterChange = new MutationObserver((mutations) => { // If element changes mutations.forEach(mu => { // If not class or attribute change return if (mu.type !== "attributes" && mu.attributeName !== "class") return; // Find matching Filter to dropdown let filterToChange = (dashboard.filters.$$filters as Array<DashObject>).filter(element => element.jaql.dim === `[${dim}]`); // If filter values displayed and filter values active don't match each other, set displayed filter to match filter, filter value stays as is filterToChange.forEach((filter) => { if (filter.jaql.filter.members.sort().join(',') !== selectedFilterValues.sort().join(',')) { // Change state of filter values to new filter, sets displayed filter in dropdown in correct one. setSelectedFilterValues(filter.jaql.filter.members); } }); }); }); // Array of element with 'widget-body' class (created by Sisense.js on widgets) for change to check for filter change const widget_body_array = document.querySelectorAll(".widget-body") // Watch 'widget-body' class for filter changed by other means, such as right click select on value widget_body_array.forEach(el => watchForFilterChange.observe(el, { attributes: true })); Component Details Filter - One for each filter, gets dropdown values and other props for dropdown filter component, takes filter dim as prop, parent of Dropdown Filter component Dropdown Filter - Renders individual filter dropdown, renders dropdown of one filter, handles filter changes Clickable Button - Button that calls a function on click, props include text, color and function called Input Number - Sets widgets per row, has up and down button as well as keyboard input, controlled input only accepts numbers, has default value in config file Load Sisense - Loads Sisense, gets URL of server from config file, has load dashboard function, creates elements to load widgets into on dashboard load call Sidebar - Collapsible Sidebar, on click loads dashboard, content of dashboard can be configured by config file App - Parent component, has loading indicator before Sisense has loaded, contains all other components Config Settings DashboardConfig - Array of objects describing dashboards selectable in sidebar DashboardLabel - text to show in expanded sidebar DashboardID - Dashboard ID, get from url of dashboard in native Sisense Icon - Icon to show in sidebar for dashboard DimArray - Values to filter by SisenseUrl - URL of Sisense server initialDashboardCube - Title of initial dashboard to show defaultSidebarCollapsed - Dashboard initial state, collapsed or not defaultSidebarCollapsedMobile - Dashboard initial state, collapsed or not, on mobile collapseSideBarText - Text shown on element that collapses sidebar* hideFilterNativeText - Text to hide native embedded filter useV1 - Use v1 version of Sisense script defaultWidth - initial state of selector for widgets per row saveEdits - Write back to Sisense changes made to filters, and any other persistent changes loadingIndicatorColor - Color of loading indicator loadingIndicatorType - Type of loading indicator, options from ReactLoading sidebarBackgroundColor - Background color of sidebar widgetMargin - Margin of individual widget Available Scripts In the project directory, you can run: npm start Runs the app in the development mode. Open http://localhost:3000 to view it in the browser. The page will reload if you make edits. You will also see any lint errors in the console. npm test Launches the test runner in the interactive watch mode. See the section about running tests for more information. npm run build Builds the app for production to the build folder. It correctly bundles React in production mode and optimizes the build for the best performance. The build is minified and the filenames include the hashes. Your app is ready to be deployed! See the section about deployment for more information. npm run eject Note: this is a one-way operation. Once you eject, you can’t go back! If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single-build dependency from your project. Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point, you’re on your own. You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However, we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. Download and extract the zip below:5.3KViews5likes0Comments