Merge remote-tracking branch 'powerbi/master' into release-1-4-0
This commit is contained in:
Коммит
f0e97cd40a
|
@ -3,14 +3,13 @@
|
|||
/.pnp
|
||||
.pnp.js
|
||||
**/package-lock.json
|
||||
**/.pipelines
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
/compiledTests
|
||||
coverage
|
||||
compiledTests
|
||||
|
||||
# production
|
||||
/dist
|
||||
dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
|
|
@ -9,6 +9,11 @@ git clone <url>
|
|||
|
||||
Navigate to the cloned directory
|
||||
|
||||
Navigate to the React\powerbi-client-react workspace folder:
|
||||
```
|
||||
cd React\powerbi-client-react
|
||||
```
|
||||
|
||||
Install local dependencies:
|
||||
```
|
||||
npm install
|
||||
|
@ -24,14 +29,13 @@ Or if using VScode: `Ctrl + Shift + B`
|
|||
```
|
||||
npm test
|
||||
```
|
||||
By default the tests run using PhantomJS browser
|
||||
By default the tests run using ChromeHeadless browser
|
||||
|
||||
The build and tests use webpack to compile all the source modules into bundled module that can be executed in the browser.
|
||||
|
||||
## Running the demo
|
||||
|
||||
```
|
||||
npm run install:demo
|
||||
npm run demo
|
||||
```
|
||||
|
||||
|
@ -40,4 +44,4 @@ Open the address to view in the browser:
|
|||
http://localhost:8080/
|
||||
|
||||
## Flow Diagram for the PowerBIEmbed Component:
|
||||
![Flow Diagram](https://github.com/microsoft/powerbi-client-react/raw/master/resources/react_wrapper_flow_diagram.png)
|
||||
![Flow Diagram](/resources/react_wrapper_flow_diagram.png)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
powerbi-client-react
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
MIT License
|
||||
|
|
124
README.md
124
README.md
|
@ -1,5 +1,5 @@
|
|||
# powerbi-client-react
|
||||
Power BI React component. This library lets you embed Power BI report, dashboard, dashboard tile, report visual, or Q&A in your React application.
|
||||
Power BI React component. This library enables you to embed Power BI reports, dashboards, dashboard tiles, report visuals, Q&A or paginated reports in your React application, and to create new Power BI reports directly in your application.
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
@ -13,11 +13,11 @@ import { PowerBIEmbed } from 'powerbi-client-react';
|
|||
```jsx
|
||||
<PowerBIEmbed
|
||||
embedConfig = {{
|
||||
type: 'report', // Supported types: report, dashboard, tile, visual and qna
|
||||
type: 'report', // Supported types: report, dashboard, tile, visual, qna, paginated report and create
|
||||
id: '<Report Id>',
|
||||
embedUrl: '<Embed Url>',
|
||||
accessToken: '<Access Token>',
|
||||
tokenType: models.TokenType.Embed,
|
||||
tokenType: models.TokenType.Embed, // Use models.TokenType.Aad for SaaS embed
|
||||
settings: {
|
||||
panes: {
|
||||
filters: {
|
||||
|
@ -29,15 +29,17 @@ import { PowerBIEmbed } from 'powerbi-client-react';
|
|||
}
|
||||
}}
|
||||
|
||||
eventHandlers = {
|
||||
eventHandlers = {
|
||||
new Map([
|
||||
['loaded', function () {console.log('Report loaded');}],
|
||||
['rendered', function () {console.log('Report rendered');}],
|
||||
['error', function (event) {console.log(event.detail);}]
|
||||
['error', function (event) {console.log(event.detail);}],
|
||||
['visualClicked', () => console.log('visual clicked')],
|
||||
['pageChanged', (event) => console.log(event)],
|
||||
])
|
||||
}
|
||||
|
||||
cssClassName = { "report-style-class" }
|
||||
|
||||
cssClassName = { "reportClass" }
|
||||
|
||||
getEmbeddedComponent = { (embeddedReport) => {
|
||||
this.report = embeddedReport as Report;
|
||||
|
@ -45,30 +47,31 @@ import { PowerBIEmbed } from 'powerbi-client-react';
|
|||
/>
|
||||
```
|
||||
|
||||
### How to [bootstrap a PowerBI report](https://aka.ms/PbieBootstrap):
|
||||
### How to [bootstrap a PowerBI report](https://learn.microsoft.com/javascript/api/overview/powerbi/bootstrap-better-performance):
|
||||
```jsx
|
||||
<PowerBIEmbed
|
||||
embedConfig = {{
|
||||
type: 'report', // Supported types: report, dashboard, tile, visual and qna
|
||||
id: undefined,
|
||||
type: 'report', // Supported types: report, dashboard, tile, visual, qna and paginated report
|
||||
id: undefined,
|
||||
embedUrl: undefined,
|
||||
accessToken: undefined, // Keep as empty string, null or undefined
|
||||
tokenType: models.TokenType.Embed
|
||||
}}
|
||||
/>
|
||||
```
|
||||
__Note__: To embed the report after bootstrap, update the props (with atleast accessToken).
|
||||
__Note__: To embed the report after bootstrap, update the props (with at least accessToken).
|
||||
|
||||
### Demo
|
||||
|
||||
A React application that embeds a sample report using the _PowerBIEmbed_ component.<br/>
|
||||
It demonstrates the complete flow from bootstrapping the report, to embedding and updating the embedded report.<br/>
|
||||
It also demonstrates the usage of _powerbi report authoring_ library by deleting a visual from report on click of "Delete a Visual" button.
|
||||
This demo includes a React application that demonstrates the complete flow of embedding a sample report using the PowerBIEmbed component.
|
||||
|
||||
The demo shows how to bootstrap the report, embed it, and update it. Additionally, the demo showcases the usage of the powerbi report authoring library by enabling the user to change the type of visual from a report using the "Change visual type" button.
|
||||
|
||||
The demo also sets a "DataSelected" event, which allows the user to interact with the embedded report and retrieve information about the selected data.
|
||||
|
||||
To run the demo on localhost, run the following commands:
|
||||
|
||||
```
|
||||
npm run install:demo
|
||||
npm run demo
|
||||
```
|
||||
|
||||
|
@ -84,11 +87,12 @@ Redirect to http://localhost:8080/ to view in the browser.
|
|||
|Reset event handlers|To reset event handler for an event, set the event handler's value as `null` in the _eventHandlers_ map of props.|
|
||||
|Set new accessToken|To set new accessToken in the same embedded powerbi artifact, pass the updated _accessToken_ in _embedConfig_ of props. <br/>Reload manually with report.reload() after providing new token if the current token in report has already expired<br/>Example scenario: _Current token has expired_.|
|
||||
|Update settings (Report type only)|To update the report settings, update the _embedConfig.settings_ property of props.<br/>Refer to the _embedConfig.settings_ prop in [Quick Start](#quick-start).<br/>__Note__: Update the settings only by updating embedConfig prop|
|
||||
|Bootstrap Power BI|To [bootstrap your powerbi entity](https://aka.ms/PbieBootstrap), pass the props to the component without _accessToken_ in _embedConfig_.<br/>__Note__: _embedConfig_ of props should atleast contain __type__ of the powerbi entity being embedded. <br/>Available types: "report", "dashboard", "tile", "visual" and "qna".<br/>Refer to _How to bootstrap a report_ section in [Quick Start](#quick-start).|
|
||||
|Bootstrap Power BI|To [bootstrap your powerbi entity](https://learn.microsoft.com/javascript/api/overview/powerbi/bootstrap-better-performance), pass the props to the component without _accessToken_ in _embedConfig_.<br/>__Note__: _embedConfig_ of props should at least contain __type__ of the powerbi entity being embedded. <br/>Available types: "report", "dashboard", "tile", "visual", "qna" and "paginated report".<br/>Refer to _How to bootstrap a report_ section in [Quick Start](#quick-start).|
|
||||
|Using with PowerBI Report Authoring|1. Install [powerbi-report-authoring](https://www.npmjs.com/package/powerbi-report-authoring) as npm dependency.<br>2. Use the report authoring APIs using the embedded report's instance|
|
||||
|Phased embedding (Report type only)|Set phasedEmbedding prop's value as `true` <br/> Refer to [Phased embedding docs](https://github.com/microsoft/PowerBI-JavaScript/wiki/Phased-Embedding).|
|
||||
|Phased embedding (Report type only)|Set phasedEmbedding prop's value as `true` <br/> Refer to [Phased embedding docs](https://learn.microsoft.com/javascript/api/overview/powerbi/phased-embedding).|
|
||||
|Apply Filters (Report type only)|1. To apply updated filters, update filters in _embedConfig_ props.<br/>2. To remove the applied filters, update the _embedConfig_ prop with the filters removed or set as undefined/null.|
|
||||
|Set Page (Report type only)|To set a page when embedding a report or on an embedded report, provide pageName field in the _embedConfig_.
|
||||
|Set Page (Report type only)|To set a page when embedding a report or on an embedded report, provide pageName field in the _embedConfig_.|
|
||||
|Create report|To create a new report, pass the component with at least _type_, _embedUrl_ and _datasetId_ in _embedConfig_ prop.|
|
||||
|
||||
__Note__: To use this library in IE browser, use [react-app-polyfill](https://www.npmjs.com/package/react-app-polyfill) to add support for the incompatible features. Refer to the imports of [demo/index.tsx](https://github.com/microsoft/powerbi-client-react/blob/master/demo/index.tsx).
|
||||
|
||||
|
@ -105,7 +109,8 @@ interface EmbedProps {
|
|||
| ITileEmbedConfiguration
|
||||
| IQnaEmbedConfiguration
|
||||
| IVisualEmbedConfiguration
|
||||
| IEmbedConfiguration
|
||||
| IPaginatedReportLoadConfiguration
|
||||
| IReportCreateConfiguration
|
||||
|
||||
// Callback method to get the embedded PowerBI entity object (optional)
|
||||
getEmbeddedComponent?: { (embeddedComponent: Embed): void }
|
||||
|
@ -122,12 +127,67 @@ interface EmbedProps {
|
|||
// Provide instance of PowerBI service (optional)
|
||||
service?: service.Service
|
||||
}
|
||||
|
||||
type EventHandler = {
|
||||
(event?: service.ICustomEvent<any>, embeddedEntity?: Embed): void | null;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Supported Events
|
||||
|
||||
|
||||
### Events supported by various Power BI entities:
|
||||
|
||||
|Entity|Event|
|
||||
|:----- |:----- |
|
||||
| Report | "buttonClicked", "commandTriggered", "dataHyperlinkClicked", "dataSelected", "loaded", "pageChanged", "rendered", "saveAsTriggered", "saved", "selectionChanged", "visualClicked", "visualRendered" |
|
||||
| Dashboard | "loaded", "tileClicked" |
|
||||
| Tile | "tileLoaded", "tileClicked" |
|
||||
| QnA | "visualRendered" |
|
||||
|
||||
### Event Handler to be used with Map
|
||||
```ts
|
||||
type EventHandler = (event?: service.ICustomEvent<any>, embeddedEntity?: Embed) => void | null;
|
||||
```
|
||||
|
||||
|
||||
## Using supported SDK methods for Power BI artifacts
|
||||
|
||||
### Import
|
||||
*Import the 'PowerBIEmbed' inside your targeted component file:*
|
||||
```ts
|
||||
import { PowerBIEmbed } from 'powerbi-client-react';
|
||||
```
|
||||
|
||||
### Use
|
||||
You can use ```report``` state to call supported SDK APIs.
|
||||
|
||||
Steps:
|
||||
1. Create one state for storing the report object, for example, ```const [report, setReport] = useState<Report>();```.
|
||||
|
||||
2. Use the ```setReport``` method inside the component to set the report object.
|
||||
<br />
|
||||
|
||||
```ts
|
||||
<PowerBIEmbed
|
||||
embedConfig = { sampleReportConfig }
|
||||
eventHandlers = { eventHandlersMap }
|
||||
cssClassName = { reportClass }
|
||||
getEmbeddedComponent = { (embedObject: Embed) => {
|
||||
setReport(embedObject as Report);
|
||||
} }
|
||||
/>
|
||||
```
|
||||
|
||||
3. Once the report object is set, it can be used to call SDK methods such as ```getVisuals```, ```getBookmarks```, etc.
|
||||
<br />
|
||||
|
||||
```ts
|
||||
async getReportPages(): Page[] {
|
||||
// this.report is a class variable, initialized in step 3
|
||||
const activePage: Page | undefined = await report.getActivePage();
|
||||
console.log(pages);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
[powerbi-client](https://www.npmjs.com/package/powerbi-client)
|
||||
|
@ -136,6 +196,10 @@ type EventHandler = {
|
|||
|
||||
[react](https://www.npmjs.com/package/react)
|
||||
|
||||
### Trademarks
|
||||
|
||||
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft’s Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies.
|
||||
|
||||
### Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
|
@ -146,6 +210,14 @@ When you submit a pull request, a CLA bot will automatically determine whether y
|
|||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments
|
||||
|
||||
### Data Collection.
|
||||
|
||||
The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications.
|
||||
|
||||
If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement.
|
||||
Our privacy statement is located at [Microsoft Privacy Statement](https://privacy.microsoft.com/privacystatement). You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
|
||||
|
||||
### Support
|
||||
Our public support page is available at [Microsoft Support Statement](https://powerbi.microsoft.com/support/).
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,4 @@
|
|||
# Auto generated file from Gardener Plugin CentralFeedServiceAdoptionPlugin
|
||||
registry=https://pkgs.dev.azure.com/powerbi/embedded/_packaging/embedded_PublicPackages/npm/registry/
|
||||
|
||||
always-auth=true
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,11 +3,11 @@
|
|||
"version": "1.0.0",
|
||||
"description": "Demo for usage of powerbi-client-react",
|
||||
"scripts": {
|
||||
"demo": "webpack-dev-server --static ./ --open"
|
||||
"demo": "webpack-dev-server --static ./src/ --open"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"powerbi-client-react": "^1.3.5",
|
||||
"powerbi-client-react": "^1.4.0",
|
||||
"powerbi-report-authoring": "^1.1",
|
||||
"react-app-polyfill": "^1.0.6"
|
||||
},
|
||||
|
@ -21,10 +21,10 @@
|
|||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^7.0.5",
|
||||
"typescript": "^3.9.3",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^4.8.4",
|
||||
"webpack": "^5.71.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.8.1"
|
||||
"webpack-dev-server": "^4.11.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/* Copyright (c) Microsoft Corporation.
|
||||
Licensed under the MIT License. */
|
||||
|
||||
.report-container {
|
||||
height: 75vh;
|
||||
margin: 8px auto;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI';
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #3476ae 0 0 no-repeat padding-box;
|
||||
border: 1px solid #707070;
|
||||
color: #ffffff;
|
||||
font: 700 22px/27px 'Segoe UI';
|
||||
padding: 13px 13px 13px 36px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.display-message {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font: 400 18px/27px 'Segoe UI';
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
margin-top: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.position {
|
||||
margin-top: 40vh;
|
||||
}
|
||||
|
||||
.embed-report {
|
||||
margin-top: 18px;
|
||||
text-align: center;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.footer {
|
||||
align-items: center;
|
||||
background: #f7f8fa 0 0 no-repeat padding-box;
|
||||
display: flex;
|
||||
font: 400 16px/21px 'Segoe UI';
|
||||
height: 42px;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer * {
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
.footer-icon {
|
||||
border-radius: 50%;
|
||||
height: 22px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #3a3a3a;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #337ab7;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
height: 35px;
|
||||
margin-right: 15px;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { models, Report, Embed, service, Page } from 'powerbi-client';
|
||||
import { IHttpPostMessageResponse } from 'http-post-message';
|
||||
import { PowerBIEmbed } from 'powerbi-client-react';
|
||||
import 'powerbi-report-authoring';
|
||||
|
||||
import { sampleReportUrl } from './public/constants';
|
||||
import './DemoApp.css';
|
||||
|
||||
// Root Component to demonstrate usage of embedded component
|
||||
function DemoApp (): JSX.Element {
|
||||
|
||||
// PowerBI Report object (to be received via callback)
|
||||
const [report, setReport] = useState<Report>();
|
||||
|
||||
// Track Report embedding status
|
||||
const [isEmbedded, setIsEmbedded] = useState<boolean>(false);
|
||||
|
||||
// Overall status message of embedding
|
||||
const [displayMessage, setMessage] = useState(`The report is bootstrapped. Click the Embed Report button to set the access token`);
|
||||
|
||||
// CSS Class to be passed to the embedded component
|
||||
const reportClass = 'report-container';
|
||||
|
||||
// Pass the basic embed configurations to the embedded component to bootstrap the report on first load
|
||||
// Values for properties like embedUrl, accessToken and settings will be set on click of button
|
||||
const [sampleReportConfig, setReportConfig] = useState<models.IReportEmbedConfiguration>({
|
||||
type: 'report',
|
||||
embedUrl: undefined,
|
||||
tokenType: models.TokenType.Embed,
|
||||
accessToken: undefined,
|
||||
settings: undefined,
|
||||
});
|
||||
|
||||
/**
|
||||
* Map of event handlers to be applied to the embedded report
|
||||
* Update event handlers for the report by redefining the map using the setEventHandlersMap function
|
||||
* Set event handler to null if event needs to be removed
|
||||
* More events can be provided from here
|
||||
* https://docs.microsoft.com/en-us/javascript/api/overview/powerbi/handle-events#report-events
|
||||
*/
|
||||
const[eventHandlersMap, setEventHandlersMap] = useState<Map<string, (event?: service.ICustomEvent<any>, embeddedEntity?: Embed) => void | null>>(new Map([
|
||||
['loaded', () => console.log('Report has loaded')],
|
||||
['rendered', () => console.log('Report has rendered')],
|
||||
['error', (event?: service.ICustomEvent<any>) => {
|
||||
if (event) {
|
||||
console.error(event.detail);
|
||||
}
|
||||
},
|
||||
],
|
||||
['visualClicked', () => console.log('visual clicked')],
|
||||
['pageChanged', (event) => console.log(event)],
|
||||
]));
|
||||
|
||||
useEffect(() => {
|
||||
if (report) {
|
||||
report.setComponentTitle('Embedded Report');
|
||||
}
|
||||
}, [report]);
|
||||
|
||||
/**
|
||||
* Embeds report
|
||||
*
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
const embedReport = async (): Promise<void> => {
|
||||
console.log('Embed Report clicked');
|
||||
|
||||
// Get the embed config from the service
|
||||
const reportConfigResponse = await fetch(sampleReportUrl);
|
||||
|
||||
if (reportConfigResponse === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reportConfigResponse?.ok) {
|
||||
console.error(`Failed to fetch config for report. Status: ${ reportConfigResponse.status } ${ reportConfigResponse.statusText }`);
|
||||
return;
|
||||
}
|
||||
|
||||
const reportConfig = await reportConfigResponse.json();
|
||||
|
||||
// Update the reportConfig to embed the PowerBI report
|
||||
setReportConfig({
|
||||
...sampleReportConfig,
|
||||
embedUrl: reportConfig.EmbedUrl,
|
||||
accessToken: reportConfig.EmbedToken.Token
|
||||
});
|
||||
setIsEmbedded(true);
|
||||
|
||||
// Update the display message
|
||||
setMessage('Use the buttons above to interact with the report using Power BI Client APIs.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide Filter Pane
|
||||
*
|
||||
* @returns Promise<IHttpPostMessageResponse<void> | undefined>
|
||||
*/
|
||||
const hideFilterPane = async (): Promise<IHttpPostMessageResponse<void> | undefined> => {
|
||||
// Check if report is available or not
|
||||
if (!report) {
|
||||
setDisplayMessageAndConsole('Report not available');
|
||||
return;
|
||||
}
|
||||
|
||||
// New settings to hide filter pane
|
||||
const settings = {
|
||||
panes: {
|
||||
filters: {
|
||||
expanded: false,
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response: IHttpPostMessageResponse<void> = await report.updateSettings(settings);
|
||||
|
||||
// Update display message
|
||||
setDisplayMessageAndConsole('Filter pane is hidden.');
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set data selected event
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const setDataSelectedEvent = () => {
|
||||
setEventHandlersMap(new Map<string, (event?: service.ICustomEvent<any>, embeddedEntity?: Embed) => void | null> ([
|
||||
...eventHandlersMap,
|
||||
['dataSelected', (event) => console.log(event)],
|
||||
]));
|
||||
|
||||
setMessage('Data Selected event set successfully. Select data to see event in console.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Change visual type
|
||||
*
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
const changeVisualType = async (): Promise<void> => {
|
||||
// Check if report is available or not
|
||||
if (!report) {
|
||||
setDisplayMessageAndConsole('Report not available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get active page of the report
|
||||
const activePage: Page | undefined = await report.getActivePage();
|
||||
|
||||
if (!activePage) {
|
||||
setMessage('No Active page found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Change the visual type using powerbi-report-authoring
|
||||
// For more information: https://docs.microsoft.com/en-us/javascript/api/overview/powerbi/report-authoring-overview
|
||||
const visual = await activePage.getVisualByName('VisualContainer6');
|
||||
|
||||
const response = await visual.changeType('lineChart');
|
||||
|
||||
setDisplayMessageAndConsole(`The ${visual.type} was updated to lineChart.`);
|
||||
|
||||
return response;
|
||||
}
|
||||
catch (error) {
|
||||
if (error === 'PowerBIEntityNotFound') {
|
||||
console.log('No Visual found with that name');
|
||||
} else {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set display message and log it in the console
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const setDisplayMessageAndConsole = (message: string): void => {
|
||||
setMessage(message);
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
const controlButtons =
|
||||
isEmbedded ?
|
||||
<>
|
||||
<button onClick = { changeVisualType }>
|
||||
Change visual type</button>
|
||||
|
||||
<button onClick = { hideFilterPane }>
|
||||
Hide filter pane</button>
|
||||
|
||||
<button onClick = { setDataSelectedEvent }>
|
||||
Set event</button>
|
||||
|
||||
<label className = "display-message">
|
||||
{ displayMessage }
|
||||
</label>
|
||||
</>
|
||||
:
|
||||
<>
|
||||
<label className = "display-message position">
|
||||
{ displayMessage }
|
||||
</label>
|
||||
|
||||
<button onClick = { embedReport } className = "embed-report">
|
||||
Embed Report</button>
|
||||
</>;
|
||||
|
||||
const header =
|
||||
<div className = "header">Power BI Embedded React Component Demo</div>;
|
||||
|
||||
const reportComponent =
|
||||
<PowerBIEmbed
|
||||
embedConfig = { sampleReportConfig }
|
||||
eventHandlers = { eventHandlersMap }
|
||||
cssClassName = { reportClass }
|
||||
getEmbeddedComponent = { (embedObject: Embed) => {
|
||||
console.log(`Embedded object of type "${ embedObject.embedtype }" received`);
|
||||
setReport(embedObject as Report);
|
||||
} }
|
||||
/>;
|
||||
|
||||
const footer =
|
||||
<div className = "footer">
|
||||
<p>This demo is powered by Power BI Embedded Analytics</p>
|
||||
<label className = "separator-pipe">|</label>
|
||||
<img title = "Power-BI" alt = "PowerBI_Icon" className = "footer-icon" src = "./assets/PowerBI_Icon.png" />
|
||||
<p>Explore our<a href = "https://aka.ms/pbijs/" target = "_blank" rel = "noreferrer noopener">Playground</a></p>
|
||||
<label className = "separator-pipe">|</label>
|
||||
<img title = "GitHub" alt = "GitHub_Icon" className = "footer-icon" src = "./assets/GitHub_Icon.png" />
|
||||
<p>Find the<a href = "https://github.com/microsoft/PowerBI-client-react" target = "_blank" rel = "noreferrer noopener">source code</a></p>
|
||||
</div>;
|
||||
|
||||
return (
|
||||
<div className = "container">
|
||||
{ header }
|
||||
|
||||
<div className = "controls">
|
||||
{ controlButtons }
|
||||
|
||||
{ isEmbedded ? reportComponent : null }
|
||||
</div>
|
||||
|
||||
{ footer }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DemoApp;
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 7.1 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.0 KiB |
|
@ -0,0 +1,16 @@
|
|||
<!-- Copyright (c) Microsoft Corporation.
|
||||
Licensed under the MIT License. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="./public/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>React Wrapper demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import 'react-app-polyfill/ie11'; // For IE compatibility
|
||||
import 'react-app-polyfill/stable'; // For IE compatibility
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import DemoApp from './DemoApp';
|
||||
|
||||
ReactDOM.render(
|
||||
<DemoApp/>,
|
||||
document.getElementById('root')
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import 'react-app-polyfill/ie11'; // For IE compatibility
|
||||
import 'react-app-polyfill/stable'; // For IE compatibility
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import DemoApp from './DemoApp';
|
||||
|
||||
ReactDOM.render(
|
||||
<DemoApp/>,
|
||||
document.getElementById('root')
|
||||
);
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// Endpoint to get report config
|
||||
export const sampleReportUrl = 'https://aka.ms/CaptureViewsReportEmbedConfig';
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.8 KiB |
|
@ -1,18 +1,18 @@
|
|||
{
|
||||
"include": [
|
||||
"./**/*.tsx",
|
||||
"./**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noErrorTruncation": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ES6",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react",
|
||||
}
|
||||
{
|
||||
"include": [
|
||||
"./**/*.tsx",
|
||||
"./**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noErrorTruncation": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ES6",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react",
|
||||
}
|
||||
}
|
|
@ -1,36 +1,36 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: path.resolve('index.tsx'),
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: 'bundle.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts(x)?$/,
|
||||
loader: 'ts-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader'
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.tsx',
|
||||
'.ts',
|
||||
'.js',
|
||||
]
|
||||
},
|
||||
devtool: 'source-map',
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: path.resolve('src/index.tsx'),
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: 'bundle.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts(x)?$/,
|
||||
loader: 'ts-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader'
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.tsx',
|
||||
'.ts',
|
||||
'.js',
|
||||
]
|
||||
},
|
||||
devtool: 'source-map',
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
# Auto generated file from Gardener Plugin CentralFeedServiceAdoptionPlugin
|
||||
registry=https://pkgs.dev.azure.com/powerbi/embedded/_packaging/embedded_PublicPackages/npm/registry/
|
||||
|
||||
always-auth=true
|
|
@ -1,21 +1,21 @@
|
|||
{
|
||||
"include": [
|
||||
"../../src/**/*.tsx",
|
||||
"../../src/**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2016"],
|
||||
"target": "es5",
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noErrorTruncation": true,
|
||||
"module": "ES6",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"declaration": true,
|
||||
"outDir": "../../dist",
|
||||
}
|
||||
{
|
||||
"include": [
|
||||
"../../src/**/*.tsx",
|
||||
"../../src/**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2016"],
|
||||
"target": "es5",
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noErrorTruncation": true,
|
||||
"module": "ES6",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"declaration": true,
|
||||
"outDir": "../../dist",
|
||||
}
|
||||
}
|
|
@ -1,40 +1,40 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
let path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve('src/PowerBIEmbed.tsx'),
|
||||
output: {
|
||||
library: 'powerbi-client-react',
|
||||
libraryTarget: 'umd',
|
||||
path: path.resolve('dist'),
|
||||
filename: 'powerbi-client-react.js'
|
||||
},
|
||||
externals: [
|
||||
'react',
|
||||
'powerbi-client',
|
||||
'lodash.isequal'
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts(x)?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve('config/src/tsconfig.json')
|
||||
},
|
||||
exclude: /node_modules/
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
extensions: [
|
||||
'.tsx',
|
||||
'.ts',
|
||||
'.js'
|
||||
]
|
||||
},
|
||||
devtool: 'source-map',
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve('src/PowerBIEmbed.tsx'),
|
||||
output: {
|
||||
library: 'powerbi-client-react',
|
||||
libraryTarget: 'umd',
|
||||
path: path.resolve('dist'),
|
||||
filename: 'powerbi-client-react.js'
|
||||
},
|
||||
externals: [
|
||||
'react',
|
||||
'powerbi-client',
|
||||
'lodash.isequal'
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts(x)?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve('config/src/tsconfig.json')
|
||||
},
|
||||
exclude: /node_modules/
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
extensions: [
|
||||
'.tsx',
|
||||
'.ts',
|
||||
'.js'
|
||||
]
|
||||
},
|
||||
devtool: 'source-map',
|
||||
};
|
|
@ -1,70 +1,70 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
let path = require('path');
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
path.resolve('compiledTests/**/*spec.js')
|
||||
],
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress'],
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
],
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ["Chrome_headless"],
|
||||
|
||||
customLaunchers: {
|
||||
'Chrome_headless': {
|
||||
base: 'Chrome',
|
||||
flags: [
|
||||
'--no-sandbox',
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity
|
||||
})
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
let path = require('path');
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
path.resolve('compiledTests/**/*spec.js')
|
||||
],
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress'],
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
],
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ["Chrome_headless"],
|
||||
|
||||
customLaunchers: {
|
||||
'Chrome_headless': {
|
||||
base: 'Chrome',
|
||||
flags: [
|
||||
'--no-sandbox',
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity
|
||||
})
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
{
|
||||
"include": [
|
||||
"../../test/**/*.tsx",
|
||||
"../../test/**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"target": "ES5",
|
||||
"lib": [
|
||||
"ES2016",
|
||||
"dom"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": false,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react"
|
||||
}
|
||||
{
|
||||
"include": [
|
||||
"../../test/**/*.tsx",
|
||||
"../../test/**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"target": "ES5",
|
||||
"lib": [
|
||||
"ES2016",
|
||||
"dom"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": false,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react"
|
||||
}
|
||||
}
|
|
@ -1,36 +1,36 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
let path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: {
|
||||
PowerBIEmbedTest: path.resolve('test/PowerBIEmbed.spec.tsx'),
|
||||
utilsTest: path.resolve('test/utils.spec.ts'),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve('compiledTests'),
|
||||
filename: '[name].spec.js'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts(x)?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve('config/test/tsconfig.json')
|
||||
},
|
||||
exclude: /node_modules/
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.tsx',
|
||||
'.ts',
|
||||
'.js'
|
||||
]
|
||||
},
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: {
|
||||
PowerBIEmbedTest: path.resolve('test/PowerBIEmbed.spec.tsx'),
|
||||
utilsTest: path.resolve('test/utils.spec.ts'),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve('compiledTests'),
|
||||
filename: '[name].spec.js'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts(x)?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve('config/test/tsconfig.json')
|
||||
},
|
||||
exclude: /node_modules/
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.tsx',
|
||||
'.ts',
|
||||
'.js'
|
||||
]
|
||||
},
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "powerbi-client-react",
|
||||
"version": "1.3.5",
|
||||
"version": "1.4.0",
|
||||
"description": "React wrapper for powerbi-client library",
|
||||
"main": "dist/powerbi-client-react.js",
|
||||
"types": "dist/powerbi-client-react.d.ts",
|
||||
|
@ -13,8 +13,7 @@
|
|||
"build:dev": "webpack --mode=development --config config/src/webpack.config.js",
|
||||
"pretest": "webpack --config config/test/webpack.config.js",
|
||||
"test": "karma start config/test/karma.conf.js",
|
||||
"install:demo": "cd demo && npm install",
|
||||
"demo": "cd demo && npm run demo",
|
||||
"demo": "cd ../demo && npm install && npm run demo",
|
||||
"lint": "eslint --fix src/**/*.{ts,tsx}"
|
||||
},
|
||||
"keywords": [
|
||||
|
@ -41,8 +40,8 @@
|
|||
"@types/node": "^14.0.5",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@typescript-eslint/eslint-plugin": "^3.1.0",
|
||||
"@typescript-eslint/parser": "^3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
||||
"@typescript-eslint/parser": "^5.42.0",
|
||||
"eslint": "^7.4.0",
|
||||
"eslint-plugin-react": "^7.20.0",
|
||||
"jasmine-core": "^3.5.0",
|
||||
|
@ -52,8 +51,8 @@
|
|||
"react": "^16.13.1",
|
||||
"react-app-polyfill": "^1.0.6",
|
||||
"react-dom": "^16.13.1",
|
||||
"ts-loader": "^7.0.5",
|
||||
"typescript": "^3.9.6",
|
||||
"ts-loader": "^9.4.1",
|
||||
"typescript": "^4.8.4",
|
||||
"webpack": "^5.71.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
}
|
|
@ -1,386 +1,399 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
service,
|
||||
factories,
|
||||
Report,
|
||||
Embed,
|
||||
Dashboard,
|
||||
Tile,
|
||||
Qna,
|
||||
Visual,
|
||||
IEmbedSettings,
|
||||
IEmbedConfiguration,
|
||||
IQnaEmbedConfiguration,
|
||||
IVisualEmbedConfiguration,
|
||||
IReportEmbedConfiguration,
|
||||
IDashboardEmbedConfiguration,
|
||||
ITileEmbedConfiguration,
|
||||
} from 'powerbi-client';
|
||||
import { ReportLevelFilters, FiltersOperations } from 'powerbi-models';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import { stringifyMap, SdkType, SdkWrapperVersion } from './utils';
|
||||
|
||||
/**
|
||||
* Type for event handler function of embedded entity
|
||||
*/
|
||||
export type EventHandler = {
|
||||
(event?: service.ICustomEvent<any>, embeddedEntity?: Embed): void | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Props interface for PowerBIEmbed component
|
||||
*/
|
||||
export interface EmbedProps {
|
||||
|
||||
// Configuration for embedding the PowerBI entity (Required)
|
||||
embedConfig:
|
||||
| IReportEmbedConfiguration
|
||||
| IDashboardEmbedConfiguration
|
||||
| ITileEmbedConfiguration
|
||||
| IQnaEmbedConfiguration
|
||||
| IVisualEmbedConfiguration
|
||||
| IEmbedConfiguration;
|
||||
|
||||
// Callback method to get the embedded PowerBI entity object (Optional)
|
||||
getEmbeddedComponent?: { (embeddedComponent: Embed): void };
|
||||
|
||||
// Map of pair of event name and its handler method to be triggered on the event (Optional)
|
||||
eventHandlers?: Map<string, EventHandler>;
|
||||
|
||||
// CSS class to be set on the embedding container (Optional)
|
||||
cssClassName?: string;
|
||||
|
||||
// Phased embedding flag (Optional)
|
||||
phasedEmbedding?: boolean;
|
||||
|
||||
// Provide a custom implementation of PowerBI service (Optional)
|
||||
service?: service.Service;
|
||||
}
|
||||
|
||||
export enum EmbedType {
|
||||
Report = 'report',
|
||||
Dashboard = 'dashboard',
|
||||
Tile = 'tile',
|
||||
Qna = 'qna',
|
||||
Visual = 'visual'
|
||||
}
|
||||
|
||||
/**
|
||||
* Base react component to embed Power BI entities like: reports, dashboards, tiles, visual and qna containers.
|
||||
*/
|
||||
export class PowerBIEmbed extends React.Component<EmbedProps> {
|
||||
|
||||
// Embedded entity
|
||||
// Note: Do not read or assign to this member variable directly, instead use the getter and setter
|
||||
private _embed?: Embed;
|
||||
|
||||
// Powerbi service
|
||||
private powerbi: service.Service;
|
||||
|
||||
// Ref to the HTML div element
|
||||
private containerRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
// JSON stringify of prev event handler map
|
||||
private prevEventHandlerMapString = '';
|
||||
|
||||
// Getter for this._embed
|
||||
private get embed(): Embed | undefined {
|
||||
return this._embed;
|
||||
};
|
||||
|
||||
// Setter for this._embed
|
||||
private set embed(newEmbedInstance: Embed | undefined) {
|
||||
this._embed = newEmbedInstance;
|
||||
|
||||
// Invoke callback method in props to return this embed instance
|
||||
this.invokeGetEmbedCallback();
|
||||
};
|
||||
|
||||
constructor(props: EmbedProps) {
|
||||
super(props);
|
||||
|
||||
if (this.props.service) {
|
||||
this.powerbi = this.props.service;
|
||||
}
|
||||
else {
|
||||
this.powerbi = new service.Service(
|
||||
factories.hpmFactory,
|
||||
factories.wpmpFactory,
|
||||
factories.routerFactory);
|
||||
}
|
||||
|
||||
this.powerbi.setSdkInfo(SdkType, SdkWrapperVersion);
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
|
||||
// Check if HTML container is available
|
||||
if (this.containerRef.current) {
|
||||
|
||||
// Decide to embed, load or bootstrap
|
||||
if (this.props.embedConfig.accessToken && this.props.embedConfig.embedUrl) {
|
||||
this.embedEntity();
|
||||
}
|
||||
else {
|
||||
this.embed = this.powerbi.bootstrap(this.containerRef.current, this.props.embedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// Set event handlers if available
|
||||
if (this.props.eventHandlers && this.embed) {
|
||||
this.setEventHandlers(this.embed, this.props.eventHandlers);
|
||||
}
|
||||
};
|
||||
|
||||
async componentDidUpdate(prevProps: EmbedProps): Promise<void> {
|
||||
|
||||
this.embedOrUpdateAccessToken(prevProps);
|
||||
|
||||
// Set event handlers if available
|
||||
if (this.props.eventHandlers && this.embed) {
|
||||
this.setEventHandlers(this.embed, this.props.eventHandlers);
|
||||
}
|
||||
|
||||
// Allow settings update only when settings object in embedConfig of current and previous props is different
|
||||
if (!isEqual(this.props.embedConfig.settings, prevProps.embedConfig.settings)) {
|
||||
await this.updateSettings();
|
||||
}
|
||||
|
||||
// Update pageName and filters for a report
|
||||
if (this.props.embedConfig.type === EmbedType.Report) {
|
||||
try {
|
||||
// Typecasting to IReportEmbedConfiguration
|
||||
const embedConfig = this.props.embedConfig as IReportEmbedConfiguration;
|
||||
const filters = embedConfig.filters as ReportLevelFilters[];
|
||||
const prevEmbedConfig = prevProps.embedConfig as IReportEmbedConfiguration;
|
||||
|
||||
// Set new page if available and different from the previous page
|
||||
if (embedConfig.pageName && embedConfig.pageName !== prevEmbedConfig.pageName) {
|
||||
// Upcast to Report and call setPage
|
||||
await (this.embed as Report).setPage(embedConfig.pageName);
|
||||
}
|
||||
|
||||
// Set filters on the embedded report if available and different from the previous filter
|
||||
if (filters && !isEqual(filters, prevEmbedConfig.filters)) {
|
||||
// Upcast to Report and call updateFilters with the Replace filter operation
|
||||
await (this.embed as Report).updateFilters(FiltersOperations.Replace, filters);
|
||||
}
|
||||
|
||||
// Remove filters on the embedded report, if previously applied
|
||||
else if (!filters && prevEmbedConfig.filters) {
|
||||
// Upcast to Report and call updateFilters with the RemoveAll filter operation
|
||||
await (this.embed as Report).updateFilters(FiltersOperations.RemoveAll);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// Clean Up
|
||||
if (this.containerRef.current) {
|
||||
this.powerbi.reset(this.containerRef.current);
|
||||
}
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
ref={this.containerRef}
|
||||
className={this.props.cssClassName}>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
/**
|
||||
* Embed the powerbi entity (Load for phased embedding)
|
||||
*/
|
||||
private embedEntity(): void {
|
||||
// Check if the HTML container is rendered and available
|
||||
if (!this.containerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load when props.phasedEmbedding is true and embed type is report, embed otherwise
|
||||
if (this.props.phasedEmbedding && this.props.embedConfig.type === EmbedType.Report) {
|
||||
this.embed = this.powerbi.load(this.containerRef.current, this.props.embedConfig);
|
||||
}
|
||||
else {
|
||||
if (this.props.phasedEmbedding) {
|
||||
console.error(`Phased embedding is not supported for type ${this.props.embedConfig.type}`)
|
||||
}
|
||||
this.embed = this.powerbi.embed(this.containerRef.current, this.props.embedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When component updates, choose to _embed_ the powerbi entity or _update the accessToken_ in the embedded entity
|
||||
* or do nothing if the embedUrl and accessToken did not update in the new props
|
||||
*
|
||||
* @param prevProps EmbedProps
|
||||
* @returns void
|
||||
*/
|
||||
private embedOrUpdateAccessToken(prevProps: EmbedProps): void {
|
||||
|
||||
// Check if Embed URL and Access Token are present in current props
|
||||
if (!this.props.embedConfig.accessToken || !this.props.embedConfig.embedUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Embed or load in the following scenarios
|
||||
// 1. AccessToken was not provided in prev props (E.g. Report was bootstrapped earlier)
|
||||
// 2. Embed URL is updated (E.g. New report is to be embedded)
|
||||
if (
|
||||
this.containerRef.current &&
|
||||
(!prevProps.embedConfig.accessToken ||
|
||||
this.props.embedConfig.embedUrl !== prevProps.embedConfig.embedUrl)
|
||||
) {
|
||||
this.embedEntity();
|
||||
}
|
||||
|
||||
// Set new access token,
|
||||
// when access token is updated but embed Url is same
|
||||
else if (
|
||||
this.props.embedConfig.accessToken !== prevProps.embedConfig.accessToken &&
|
||||
this.props.embedConfig.embedUrl === prevProps.embedConfig.embedUrl &&
|
||||
this.embed
|
||||
) {
|
||||
this.embed.setAccessToken(this.props.embedConfig.accessToken)
|
||||
.catch((error) => {
|
||||
console.error(`setAccessToken error: ${error}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all event handlers from the props on the embedded entity
|
||||
*
|
||||
* @param embed Embedded object
|
||||
* @param eventHandlers Array of eventhandlers to be set on embedded entity
|
||||
* @returns void
|
||||
*/
|
||||
private setEventHandlers(
|
||||
embed: Embed,
|
||||
eventHandlerMap: Map<string, EventHandler>
|
||||
): void {
|
||||
// Get string representation of eventHandlerMap
|
||||
const eventHandlerMapString = stringifyMap(this.props.eventHandlers);
|
||||
|
||||
// Check if event handler map changed
|
||||
if (this.prevEventHandlerMapString === eventHandlerMapString) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update prev string representation of event handler map
|
||||
this.prevEventHandlerMapString = eventHandlerMapString;
|
||||
|
||||
// List of allowed events
|
||||
let allowedEvents = Embed.allowedEvents;
|
||||
|
||||
const entityType = embed.embedtype;
|
||||
|
||||
// Append entity specific events
|
||||
switch (entityType) {
|
||||
case EmbedType.Report:
|
||||
allowedEvents = [...allowedEvents, ...Report.allowedEvents]
|
||||
break;
|
||||
case EmbedType.Dashboard:
|
||||
allowedEvents = [...allowedEvents, ...Dashboard.allowedEvents]
|
||||
break;
|
||||
case EmbedType.Tile:
|
||||
allowedEvents = [...allowedEvents, ...Tile.allowedEvents]
|
||||
break;
|
||||
case EmbedType.Qna:
|
||||
allowedEvents = [...allowedEvents, ...Qna.allowedEvents]
|
||||
break;
|
||||
case EmbedType.Visual:
|
||||
allowedEvents = [...allowedEvents, ...Visual.allowedEvents]
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid embed type ${entityType}`);
|
||||
}
|
||||
|
||||
// Holds list of events which are not allowed
|
||||
const invalidEvents: Array<string> = [];
|
||||
|
||||
// Apply all provided event handlers
|
||||
eventHandlerMap.forEach((eventHandlerMethod, eventName) => {
|
||||
// Check if this event is allowed
|
||||
if (allowedEvents.includes(eventName)) {
|
||||
|
||||
// Removes event handler for this event
|
||||
embed.off(eventName);
|
||||
|
||||
// Event handler is effectively removed for this event when eventHandlerMethod is null
|
||||
if (eventHandlerMethod) {
|
||||
|
||||
// Set single event handler
|
||||
embed.on(eventName, (event: service.ICustomEvent<any>): void => {
|
||||
eventHandlerMethod(event, this.embed);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Add this event name to the list of invalid events
|
||||
invalidEvents.push(eventName);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle invalid events
|
||||
if (invalidEvents.length) {
|
||||
console.error(`Following events are invalid: ${invalidEvents.join(',')}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the embedded object via _getEmbed_ callback method provided in props
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
private invokeGetEmbedCallback(): void {
|
||||
if (this.props.getEmbeddedComponent && this.embed) {
|
||||
this.props.getEmbeddedComponent(this.embed);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update settings from props of the embedded artifact
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
private async updateSettings(): Promise<void> {
|
||||
if (!this.embed || !this.props.embedConfig.settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.props.embedConfig.type) {
|
||||
case EmbedType.Report: {
|
||||
// Typecasted to IEmbedSettings as props.embedConfig.settings can be ISettings via IQnaEmbedConfiguration
|
||||
const settings = this.props.embedConfig.settings as IEmbedSettings;
|
||||
|
||||
try {
|
||||
// Upcast to Report and call updateSettings
|
||||
await (this.embed as Report).updateSettings(settings);
|
||||
} catch (error) {
|
||||
console.error(`Error in method updateSettings: ${error}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EmbedType.Dashboard:
|
||||
case EmbedType.Tile:
|
||||
case EmbedType.Qna:
|
||||
case EmbedType.Visual:
|
||||
// updateSettings not applicable for these embedding types
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(`Invalid embed type ${this.props.embedConfig.type}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
service,
|
||||
factories,
|
||||
Report,
|
||||
Embed,
|
||||
Dashboard,
|
||||
Tile,
|
||||
Qna,
|
||||
Visual,
|
||||
IEmbedSettings,
|
||||
IQnaEmbedConfiguration,
|
||||
IVisualEmbedConfiguration,
|
||||
IReportEmbedConfiguration,
|
||||
IDashboardEmbedConfiguration,
|
||||
ITileEmbedConfiguration,
|
||||
} from 'powerbi-client';
|
||||
import { IReportCreateConfiguration, IPaginatedReportLoadConfiguration, ReportLevelFilters, FiltersOperations } from 'powerbi-models';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import { stringifyMap, SdkType, SdkWrapperVersion } from './utils';
|
||||
|
||||
/**
|
||||
* Type for event handler function of embedded entity
|
||||
*/
|
||||
export type EventHandler = {
|
||||
(event?: service.ICustomEvent<any>, embeddedEntity?: Embed): void | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Props interface for PowerBIEmbed component
|
||||
*/
|
||||
export interface EmbedProps {
|
||||
|
||||
// Configuration for embedding the PowerBI entity (Required)
|
||||
embedConfig:
|
||||
| IReportEmbedConfiguration
|
||||
| IDashboardEmbedConfiguration
|
||||
| ITileEmbedConfiguration
|
||||
| IQnaEmbedConfiguration
|
||||
| IVisualEmbedConfiguration
|
||||
| IPaginatedReportLoadConfiguration
|
||||
| IReportCreateConfiguration;
|
||||
|
||||
// Callback method to get the embedded PowerBI entity object (Optional)
|
||||
getEmbeddedComponent?: { (embeddedComponent: Embed): void };
|
||||
|
||||
// Map of pair of event name and its handler method to be triggered on the event (Optional)
|
||||
eventHandlers?: Map<string, EventHandler>;
|
||||
|
||||
// CSS class to be set on the embedding container (Optional)
|
||||
cssClassName?: string;
|
||||
|
||||
// Phased embedding flag (Optional)
|
||||
phasedEmbedding?: boolean;
|
||||
|
||||
// Provide a custom implementation of PowerBI service (Optional)
|
||||
service?: service.Service;
|
||||
}
|
||||
|
||||
export enum EmbedType {
|
||||
Create = 'create',
|
||||
Report = 'report',
|
||||
Dashboard = 'dashboard',
|
||||
Tile = 'tile',
|
||||
Qna = 'qna',
|
||||
Visual = 'visual'
|
||||
}
|
||||
|
||||
/**
|
||||
* Base react component to embed Power BI entities like: reports, dashboards, tiles, visual and qna containers.
|
||||
*/
|
||||
export class PowerBIEmbed extends React.Component<EmbedProps> {
|
||||
|
||||
// Embedded entity
|
||||
// Note: Do not read or assign to this member variable directly, instead use the getter and setter
|
||||
private _embed?: Embed;
|
||||
|
||||
// Powerbi service
|
||||
private powerbi: service.Service;
|
||||
|
||||
// Ref to the HTML div element
|
||||
private containerRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
// JSON stringify of prev event handler map
|
||||
private prevEventHandlerMapString = '';
|
||||
|
||||
// Getter for this._embed
|
||||
private get embed(): Embed | undefined {
|
||||
return this._embed;
|
||||
};
|
||||
|
||||
// Setter for this._embed
|
||||
private set embed(newEmbedInstance: Embed | undefined) {
|
||||
this._embed = newEmbedInstance;
|
||||
|
||||
// Invoke callback method in props to return this embed instance
|
||||
this.invokeGetEmbedCallback();
|
||||
};
|
||||
|
||||
constructor(props: EmbedProps) {
|
||||
super(props);
|
||||
|
||||
if (this.props.service) {
|
||||
this.powerbi = this.props.service;
|
||||
}
|
||||
else {
|
||||
this.powerbi = new service.Service(
|
||||
factories.hpmFactory,
|
||||
factories.wpmpFactory,
|
||||
factories.routerFactory);
|
||||
}
|
||||
|
||||
this.powerbi.setSdkInfo(SdkType, SdkWrapperVersion);
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
|
||||
// Check if HTML container is available
|
||||
if (this.containerRef.current) {
|
||||
|
||||
// Decide to embed, load or bootstrap
|
||||
if (this.props.embedConfig.accessToken && this.props.embedConfig.embedUrl) {
|
||||
this.embedEntity();
|
||||
}
|
||||
else {
|
||||
this.embed = this.powerbi.bootstrap(this.containerRef.current, this.props.embedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// Set event handlers if available
|
||||
if (this.props.eventHandlers && this.embed) {
|
||||
this.setEventHandlers(this.embed, this.props.eventHandlers);
|
||||
}
|
||||
};
|
||||
|
||||
async componentDidUpdate(prevProps: EmbedProps): Promise<void> {
|
||||
|
||||
this.embedOrUpdateAccessToken(prevProps);
|
||||
|
||||
// Set event handlers if available
|
||||
if (this.props.eventHandlers && this.embed) {
|
||||
this.setEventHandlers(this.embed, this.props.eventHandlers);
|
||||
}
|
||||
|
||||
// Allow settings update only when settings object in embedConfig of current and previous props is different
|
||||
if (!isEqual(this.props.embedConfig.settings, prevProps.embedConfig.settings)) {
|
||||
await this.updateSettings();
|
||||
}
|
||||
|
||||
// Update pageName and filters for a report
|
||||
if (this.props.embedConfig.type === EmbedType.Report) {
|
||||
try {
|
||||
// Typecasting to IReportEmbedConfiguration
|
||||
const embedConfig = this.props.embedConfig as IReportEmbedConfiguration;
|
||||
const filters = embedConfig.filters as ReportLevelFilters[];
|
||||
const prevEmbedConfig = prevProps.embedConfig as IReportEmbedConfiguration;
|
||||
|
||||
// Set new page if available and different from the previous page
|
||||
if (embedConfig.pageName && embedConfig.pageName !== prevEmbedConfig.pageName) {
|
||||
// Upcast to Report and call setPage
|
||||
await (this.embed as Report).setPage(embedConfig.pageName);
|
||||
}
|
||||
|
||||
// Set filters on the embedded report if available and different from the previous filter
|
||||
if (filters && !isEqual(filters, prevEmbedConfig.filters)) {
|
||||
// Upcast to Report and call updateFilters with the Replace filter operation
|
||||
await (this.embed as Report).updateFilters(FiltersOperations.Replace, filters);
|
||||
}
|
||||
|
||||
// Remove filters on the embedded report, if previously applied
|
||||
else if (!filters && prevEmbedConfig.filters) {
|
||||
// Upcast to Report and call updateFilters with the RemoveAll filter operation
|
||||
await (this.embed as Report).updateFilters(FiltersOperations.RemoveAll);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// Clean Up
|
||||
if (this.containerRef.current) {
|
||||
this.powerbi.reset(this.containerRef.current);
|
||||
}
|
||||
|
||||
// Set the previous event handler map string to empty
|
||||
this.prevEventHandlerMapString = '';
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
ref={this.containerRef}
|
||||
className={this.props.cssClassName}>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
/**
|
||||
* Embed the powerbi entity (Load for phased embedding)
|
||||
*/
|
||||
private embedEntity(): void {
|
||||
// Check if the HTML container is rendered and available
|
||||
if (!this.containerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load when props.phasedEmbedding is true and embed type is report, embed otherwise
|
||||
if (this.props.phasedEmbedding && this.props.embedConfig.type === EmbedType.Report) {
|
||||
this.embed = this.powerbi.load(this.containerRef.current, this.props.embedConfig);
|
||||
}
|
||||
else {
|
||||
if (this.props.phasedEmbedding) {
|
||||
console.error(`Phased embedding is not supported for type ${this.props.embedConfig.type}`)
|
||||
}
|
||||
|
||||
if (this.props.embedConfig.type === EmbedType.Create) {
|
||||
this.embed = this.powerbi.createReport(this.containerRef.current, this.props.embedConfig as IReportCreateConfiguration);
|
||||
}
|
||||
else {
|
||||
this.embed = this.powerbi.embed(this.containerRef.current, this.props.embedConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When component updates, choose to _embed_ the powerbi entity or _update the accessToken_ in the embedded entity
|
||||
* or do nothing if the embedUrl and accessToken did not update in the new props
|
||||
*
|
||||
* @param prevProps EmbedProps
|
||||
* @returns void
|
||||
*/
|
||||
private async embedOrUpdateAccessToken(prevProps: EmbedProps): Promise<void> {
|
||||
|
||||
// Check if Embed URL and Access Token are present in current props
|
||||
if (!this.props.embedConfig.accessToken || !this.props.embedConfig.embedUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Embed or load in the following scenarios
|
||||
// 1. AccessToken was not provided in prev props (E.g. Report was bootstrapped earlier)
|
||||
// 2. Embed URL is updated (E.g. New report is to be embedded)
|
||||
if (
|
||||
this.containerRef.current &&
|
||||
(!prevProps.embedConfig.accessToken ||
|
||||
this.props.embedConfig.embedUrl !== prevProps.embedConfig.embedUrl)
|
||||
) {
|
||||
this.embedEntity();
|
||||
}
|
||||
|
||||
// Set new access token,
|
||||
// when access token is updated but embed Url is same
|
||||
else if (
|
||||
this.props.embedConfig.accessToken !== prevProps.embedConfig.accessToken &&
|
||||
this.props.embedConfig.embedUrl === prevProps.embedConfig.embedUrl &&
|
||||
this.embed
|
||||
) {
|
||||
try {
|
||||
await this.embed.setAccessToken(this.props.embedConfig.accessToken);
|
||||
} catch(error) {
|
||||
console.error("setAccessToken error:\n", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all event handlers from the props on the embedded entity
|
||||
*
|
||||
* @param embed Embedded object
|
||||
* @param eventHandlers Array of eventhandlers to be set on embedded entity
|
||||
* @returns void
|
||||
*/
|
||||
private setEventHandlers(
|
||||
embed: Embed,
|
||||
eventHandlerMap: Map<string, EventHandler>
|
||||
): void {
|
||||
// Get string representation of eventHandlerMap
|
||||
const eventHandlerMapString = stringifyMap(this.props.eventHandlers);
|
||||
|
||||
// Check if event handler map changed
|
||||
if (this.prevEventHandlerMapString === eventHandlerMapString) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update prev string representation of event handler map
|
||||
this.prevEventHandlerMapString = eventHandlerMapString;
|
||||
|
||||
// List of allowed events
|
||||
let allowedEvents = Embed.allowedEvents;
|
||||
|
||||
const entityType = embed.embedtype;
|
||||
|
||||
// Append entity specific events
|
||||
switch (entityType) {
|
||||
case EmbedType.Create:
|
||||
break;
|
||||
case EmbedType.Report:
|
||||
allowedEvents = [...allowedEvents, ...Report.allowedEvents];
|
||||
break;
|
||||
case EmbedType.Dashboard:
|
||||
allowedEvents = [...allowedEvents, ...Dashboard.allowedEvents];
|
||||
break;
|
||||
case EmbedType.Tile:
|
||||
allowedEvents = [...allowedEvents, ...Tile.allowedEvents];
|
||||
break;
|
||||
case EmbedType.Qna:
|
||||
allowedEvents = [...allowedEvents, ...Qna.allowedEvents];
|
||||
break;
|
||||
case EmbedType.Visual:
|
||||
allowedEvents = [...allowedEvents, ...Visual.allowedEvents];
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid embed type ${entityType}`);
|
||||
}
|
||||
|
||||
// Holds list of events which are not allowed
|
||||
const invalidEvents: Array<string> = [];
|
||||
|
||||
// Apply all provided event handlers
|
||||
eventHandlerMap.forEach((eventHandlerMethod, eventName) => {
|
||||
// Check if this event is allowed
|
||||
if (allowedEvents.includes(eventName)) {
|
||||
|
||||
// Removes event handler for this event
|
||||
embed.off(eventName);
|
||||
|
||||
// Event handler is effectively removed for this event when eventHandlerMethod is null
|
||||
if (eventHandlerMethod) {
|
||||
|
||||
// Set single event handler
|
||||
embed.on(eventName, (event: service.ICustomEvent<any>): void => {
|
||||
eventHandlerMethod(event, this.embed);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Add this event name to the list of invalid events
|
||||
invalidEvents.push(eventName);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle invalid events
|
||||
if (invalidEvents.length) {
|
||||
console.error(`Following events are invalid: ${invalidEvents.join(',')}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the embedded object via _getEmbed_ callback method provided in props
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
private invokeGetEmbedCallback(): void {
|
||||
if (this.props.getEmbeddedComponent && this.embed) {
|
||||
this.props.getEmbeddedComponent(this.embed);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update settings from props of the embedded artifact
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
private async updateSettings(): Promise<void> {
|
||||
if (!this.embed || !this.props.embedConfig.settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.props.embedConfig.type) {
|
||||
case EmbedType.Report: {
|
||||
// Typecasted to IEmbedSettings as props.embedConfig.settings can be ISettings via IQnaEmbedConfiguration
|
||||
const settings = this.props.embedConfig.settings as IEmbedSettings;
|
||||
|
||||
try {
|
||||
// Upcast to Report and call updateSettings
|
||||
await (this.embed as Report).updateSettings(settings);
|
||||
} catch (error) {
|
||||
console.error(`Error in method updateSettings: ${error}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EmbedType.Dashboard:
|
||||
case EmbedType.Tile:
|
||||
case EmbedType.Qna:
|
||||
case EmbedType.Visual:
|
||||
// updateSettings not applicable for these embedding types
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(`Invalid embed type ${this.props.embedConfig.type}`);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
export {
|
||||
PowerBIEmbed,
|
||||
EmbedProps,
|
||||
EmbedType,
|
||||
EventHandler
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
export {
|
||||
PowerBIEmbed,
|
||||
EmbedProps,
|
||||
EmbedType,
|
||||
EventHandler
|
||||
} from './PowerBIEmbed'
|
|
@ -1,47 +1,47 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { EmbedProps } from "./PowerBIEmbed";
|
||||
|
||||
/**
|
||||
* Get JSON string representation of the given map.
|
||||
*
|
||||
* @param map Map of event and corresponding handler method
|
||||
*
|
||||
* For example:
|
||||
* Input:
|
||||
* ```
|
||||
* Map([
|
||||
['loaded', null],
|
||||
['rendered', function () { console.log('Rendered'); }]
|
||||
]);
|
||||
* ```
|
||||
* Output:
|
||||
* ```
|
||||
* `[["loaded",""],["rendered","function () { console.log('Rendered'); }"]]`
|
||||
* ```
|
||||
*/
|
||||
export function stringifyMap(map: EmbedProps['eventHandlers']): string {
|
||||
|
||||
// Return empty string for empty/null map
|
||||
if (!map) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get entries of map as array
|
||||
const mapEntries = Array.from(map);
|
||||
|
||||
// Return JSON string
|
||||
return JSON.stringify(mapEntries.map((mapEntry) => {
|
||||
|
||||
// Convert event handler method to a string containing its source code for comparison
|
||||
return [
|
||||
mapEntry[0],
|
||||
mapEntry[1] ? mapEntry[1].toString() : ''
|
||||
];
|
||||
}));
|
||||
};
|
||||
|
||||
// SDK information to be used with service instance
|
||||
export const SdkType = "powerbi-client-react";
|
||||
export const SdkWrapperVersion = "1.3.5";
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { EmbedProps } from "./PowerBIEmbed";
|
||||
|
||||
/**
|
||||
* Get JSON string representation of the given map.
|
||||
*
|
||||
* @param map Map of event and corresponding handler method
|
||||
*
|
||||
* For example:
|
||||
* Input:
|
||||
* ```
|
||||
* Map([
|
||||
['loaded', null],
|
||||
['rendered', function () { console.log('Rendered'); }]
|
||||
]);
|
||||
* ```
|
||||
* Output:
|
||||
* ```
|
||||
* `[["loaded",""],["rendered","function () { console.log('Rendered'); }"]]`
|
||||
* ```
|
||||
*/
|
||||
export function stringifyMap(map: EmbedProps['eventHandlers']): string {
|
||||
|
||||
// Return empty string for empty/null map
|
||||
if (!map) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get entries of map as array
|
||||
const mapEntries = Array.from(map);
|
||||
|
||||
// Return JSON string
|
||||
return JSON.stringify(mapEntries.map((mapEntry) => {
|
||||
|
||||
// Convert event handler method to a string containing its source code for comparison
|
||||
return [
|
||||
mapEntry[0],
|
||||
mapEntry[1] ? mapEntry[1].toString() : ''
|
||||
];
|
||||
}));
|
||||
};
|
||||
|
||||
// SDK information to be used with service instance
|
||||
export const SdkType = "powerbi-client-react";
|
||||
export const SdkWrapperVersion = "1.4.0";
|
|
@ -7,10 +7,12 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import { act, isElement } from 'react-dom/test-utils';
|
||||
import { Report, Dashboard, service, factories, IEmbedSettings, IReportEmbedConfiguration } from 'powerbi-client';
|
||||
import { PowerBIEmbed } from '../src/PowerBIEmbed';
|
||||
import { mockPowerBIService, mockedMethods } from "./mockService";
|
||||
import { IBasicFilter, FilterType, FiltersOperations } from 'powerbi-models';
|
||||
|
||||
import { PowerBIEmbed } from '../src/PowerBIEmbed';
|
||||
import { stringifyMap } from '../src/utils';
|
||||
|
||||
// Use this function to render powerbi entity with only config
|
||||
function renderReport(container: HTMLDivElement, config) {
|
||||
let testReport: Report = undefined;
|
||||
|
@ -944,6 +946,39 @@ describe('tests of PowerBIEmbed', function () {
|
|||
});
|
||||
|
||||
describe('tests for setting event handlers', () => {
|
||||
it('test event handlers are setting when remounting twice', () => {
|
||||
// Arrange
|
||||
const eventHandlers = new Map([
|
||||
['loaded', function () { }],
|
||||
['rendered', function () { }],
|
||||
['error', function () { }]
|
||||
]);
|
||||
|
||||
const powerbi = new service.Service(
|
||||
factories.hpmFactory,
|
||||
factories.wpmpFactory,
|
||||
factories.routerFactory);
|
||||
const embed = powerbi.bootstrap(container, { type: 'report' });
|
||||
|
||||
// Act
|
||||
const powerbiembed = new PowerBIEmbed({
|
||||
embedConfig: { type: 'report' },
|
||||
eventHandlers: eventHandlers
|
||||
});
|
||||
|
||||
// Ignoring next line as setEventHandlers is a private method
|
||||
// @ts-ignore
|
||||
powerbiembed.setEventHandlers(embed, eventHandlers);
|
||||
powerbiembed.componentWillUnmount();
|
||||
expect((powerbiembed as any).prevEventHandlerMapString).toBe('');
|
||||
powerbiembed.componentDidMount();
|
||||
// @ts-ignore
|
||||
powerbiembed.setEventHandlers(embed, eventHandlers);
|
||||
|
||||
// Assert
|
||||
expect((powerbiembed as any).prevEventHandlerMapString).toBe(stringifyMap(eventHandlers));
|
||||
});
|
||||
|
||||
it('clears and sets the event handlers', () => {
|
||||
|
||||
// Arrange
|
||||
|
@ -1138,4 +1173,4 @@ describe('tests of PowerBIEmbed', function () {
|
|||
expect(testReport.on).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
const mockedMethods = ['init', 'embed', 'bootstrap', 'load', 'get', 'reset', 'preload', 'setSdkInfo'];
|
||||
|
||||
const mockPowerBIService = jasmine.createSpyObj('mockService', mockedMethods);
|
||||
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
const mockedMethods = ['init', 'embed', 'bootstrap', 'load', 'get', 'reset', 'preload', 'setSdkInfo'];
|
||||
|
||||
const mockPowerBIService = jasmine.createSpyObj('mockService', mockedMethods);
|
||||
|
||||
export { mockPowerBIService, mockedMethods };
|
|
@ -1,71 +1,71 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { service } from 'powerbi-client';
|
||||
import { stringifyMap } from '../src/utils';
|
||||
|
||||
describe('tests of PowerBIEmbed', function () {
|
||||
|
||||
let container: HTMLDivElement | null;
|
||||
|
||||
beforeEach(function () {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (container){
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Tests for utils stringifyMap
|
||||
describe('tests PowerBIEmbed stringifyMap method', () => {
|
||||
|
||||
it('stringifies the event handler map', () => {
|
||||
|
||||
// Arrange
|
||||
const eventHandlerMap = new Map([
|
||||
['loaded', function () { console.log('Report loaded'); }],
|
||||
['rendered', function () { console.log('Rendered'); }]
|
||||
]);
|
||||
const expectedString = `[["loaded","function () { console.log('Report loaded'); }"],["rendered","function () { console.log('Rendered'); }"]]`;
|
||||
|
||||
// Act
|
||||
const jsonStringOutput = stringifyMap(eventHandlerMap);
|
||||
|
||||
// Assert
|
||||
expect(jsonStringOutput).toBe(expectedString);
|
||||
});
|
||||
|
||||
it('stringifies empty event handler map', () => {
|
||||
|
||||
// Arrange
|
||||
const eventHandlerMap = new Map<string, service.IEventHandler<any>>([]);
|
||||
const expectedString = `[]`;
|
||||
|
||||
// Act
|
||||
const jsonStringOutput = stringifyMap(eventHandlerMap);
|
||||
|
||||
// Assert
|
||||
expect(jsonStringOutput).toBe(expectedString);
|
||||
});
|
||||
|
||||
it('stringifies null in event handler map', () => {
|
||||
|
||||
// Arrange
|
||||
const eventHandlerMap = new Map([
|
||||
['loaded', null],
|
||||
['rendered', function () { console.log('Rendered'); }]
|
||||
]);
|
||||
const expectedString = `[["loaded",""],["rendered","function () { console.log('Rendered'); }"]]`;
|
||||
|
||||
// Act
|
||||
const jsonStringOutput = stringifyMap(eventHandlerMap);
|
||||
|
||||
// Assert
|
||||
expect(jsonStringOutput).toBe(expectedString);
|
||||
});
|
||||
});
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { service } from 'powerbi-client';
|
||||
import { stringifyMap } from '../src/utils';
|
||||
|
||||
describe('tests of PowerBIEmbed', function () {
|
||||
|
||||
let container: HTMLDivElement | null;
|
||||
|
||||
beforeEach(function () {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (container){
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Tests for utils stringifyMap
|
||||
describe('tests PowerBIEmbed stringifyMap method', () => {
|
||||
|
||||
it('stringifies the event handler map', () => {
|
||||
|
||||
// Arrange
|
||||
const eventHandlerMap = new Map([
|
||||
['loaded', function () { console.log('Report loaded'); }],
|
||||
['rendered', function () { console.log('Rendered'); }]
|
||||
]);
|
||||
const expectedString = `[["loaded","function () { console.log('Report loaded'); }"],["rendered","function () { console.log('Rendered'); }"]]`;
|
||||
|
||||
// Act
|
||||
const jsonStringOutput = stringifyMap(eventHandlerMap);
|
||||
|
||||
// Assert
|
||||
expect(jsonStringOutput).toBe(expectedString);
|
||||
});
|
||||
|
||||
it('stringifies empty event handler map', () => {
|
||||
|
||||
// Arrange
|
||||
const eventHandlerMap = new Map<string, service.IEventHandler<any>>([]);
|
||||
const expectedString = `[]`;
|
||||
|
||||
// Act
|
||||
const jsonStringOutput = stringifyMap(eventHandlerMap);
|
||||
|
||||
// Assert
|
||||
expect(jsonStringOutput).toBe(expectedString);
|
||||
});
|
||||
|
||||
it('stringifies null in event handler map', () => {
|
||||
|
||||
// Arrange
|
||||
const eventHandlerMap = new Map([
|
||||
['loaded', null],
|
||||
['rendered', function () { console.log('Rendered'); }]
|
||||
]);
|
||||
const expectedString = `[["loaded",""],["rendered","function () { console.log('Rendered'); }"]]`;
|
||||
|
||||
// Act
|
||||
const jsonStringOutput = stringifyMap(eventHandlerMap);
|
||||
|
||||
// Assert
|
||||
expect(jsonStringOutput).toBe(expectedString);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -14,7 +14,7 @@ Instead, please report them to the Microsoft Security Response Center (MSRC) at
|
|||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
.report-style-class {
|
||||
height: 69vh;
|
||||
margin: 1% auto;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI';
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #3476AE 0 0 no-repeat padding-box;
|
||||
border: 1px solid #707070;
|
||||
height: 55px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.displayMessage {
|
||||
color: #000000;
|
||||
font: normal 22px/27px Segoe UI;
|
||||
letter-spacing: 0;
|
||||
margin-top: 1%;
|
||||
opacity: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hr {
|
||||
border: 1px solid #E0E0E0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 1%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: #EEF3F8 0 0 no-repeat padding-box;
|
||||
bottom: 0;
|
||||
height: 39px;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font: Regular 16px/21px Segoe UI;
|
||||
height: 21px;
|
||||
letter-spacing: 0;
|
||||
margin-top: 9px;
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer-text > a {
|
||||
color: #278CE2;
|
||||
font: Regular 16px/21px Segoe UI;
|
||||
letter-spacing: 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #FFFFFF;
|
||||
font: Bold 22px/27px Segoe UI;
|
||||
letter-spacing: 0;
|
||||
margin: 13px;
|
||||
margin-left: 36px;
|
||||
opacity: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #337AB7;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
color: #FFFFFF;
|
||||
font-size: medium;
|
||||
height: 35px;
|
||||
margin-right: 15px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
button:onfocus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
}
|
199
demo/DemoApp.tsx
199
demo/DemoApp.tsx
|
@ -1,199 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { models, Report, Embed, service, Page } from 'powerbi-client';
|
||||
import { PowerBIEmbed } from 'powerbi-client-react';
|
||||
import 'powerbi-report-authoring';
|
||||
import './DemoApp.css';
|
||||
|
||||
// Root Component to demonstrate usage of wrapper component
|
||||
function DemoApp (): JSX.Element {
|
||||
|
||||
// PowerBI Report object (to be received via callback)
|
||||
const [report, setReport] = useState<Report>();
|
||||
|
||||
// API end-point url to get embed config for a sample report
|
||||
const sampleReportUrl = 'https://playgroundbe-bck-1.azurewebsites.net/Reports/SampleReport';
|
||||
|
||||
// Report config useState hook
|
||||
// Values for properties like embedUrl, accessToken and settings will be set on click of buttons below
|
||||
const [sampleReportConfig, setReportConfig] = useState<models.IReportEmbedConfiguration>({
|
||||
type: 'report',
|
||||
embedUrl: undefined,
|
||||
tokenType: models.TokenType.Embed,
|
||||
accessToken: undefined,
|
||||
settings: undefined,
|
||||
});
|
||||
|
||||
// Map of event handlers to be applied to the embedding report
|
||||
const eventHandlersMap = new Map([
|
||||
['loaded', function () {
|
||||
console.log('Report has loaded');
|
||||
}],
|
||||
['rendered', function () {
|
||||
console.log('Report has rendered');
|
||||
|
||||
// Update display message
|
||||
setMessage('The report is rendered')
|
||||
}],
|
||||
['error', function (event?: service.ICustomEvent<any>) {
|
||||
if (event) {
|
||||
console.error(event.detail);
|
||||
}
|
||||
}]
|
||||
]);
|
||||
|
||||
// Fetch sample report's config (eg. embedUrl and AccessToken) for embedding
|
||||
const mockSignIn = async () => {
|
||||
|
||||
// Fetch sample report's embed config
|
||||
const reportConfigResponse = await fetch(sampleReportUrl);
|
||||
|
||||
if (!reportConfigResponse.ok) {
|
||||
console.error(`Failed to fetch config for report. Status: ${ reportConfigResponse.status } ${ reportConfigResponse.statusText }`);
|
||||
return;
|
||||
}
|
||||
|
||||
const reportConfig = await reportConfigResponse.json();
|
||||
|
||||
// Update display message
|
||||
setMessage('The access token is successfully set. Loading the Power BI report')
|
||||
|
||||
// Set the fetched embedUrl and embedToken in the report config
|
||||
setReportConfig({
|
||||
...sampleReportConfig,
|
||||
embedUrl: reportConfig.EmbedUrl,
|
||||
accessToken: reportConfig.EmbedToken.Token
|
||||
});
|
||||
};
|
||||
|
||||
const changeSettings = () => {
|
||||
|
||||
// Update the state "sampleReportConfig" and re-render DemoApp component
|
||||
setReportConfig({
|
||||
...sampleReportConfig,
|
||||
settings: {
|
||||
panes: {
|
||||
filters: {
|
||||
expanded: false,
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Delete the first visual using powerbi-report-authoring library
|
||||
const deleteVisual = async () => {
|
||||
|
||||
if (!report) {
|
||||
console.log('Report not available');
|
||||
return;
|
||||
}
|
||||
|
||||
const activePage = await getActivePage(report);
|
||||
|
||||
if (!activePage) {
|
||||
console.log('No active page');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all visuals in the active page
|
||||
const visuals = await activePage.getVisuals();
|
||||
|
||||
if (visuals.length === 0) {
|
||||
console.log('No visual left');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get first visible visual
|
||||
const visual = visuals.find((v) => {
|
||||
return v.layout.displayState?.mode === models.VisualContainerDisplayMode.Visible;
|
||||
});
|
||||
|
||||
// No visible visual found
|
||||
if (!visual) {
|
||||
console.log('No visible visual available to delete');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// Documentation link: https://github.com/microsoft/powerbi-report-authoring/wiki/Visualization
|
||||
// Delete the visual
|
||||
await activePage.deleteVisual(visual.name);
|
||||
|
||||
console.log('Visual was deleted');
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
async function getActivePage(powerbiReport: Report): Promise<Page | undefined> {
|
||||
const pages = await powerbiReport.getPages();
|
||||
|
||||
// Get the active page
|
||||
const activePage = pages.filter(function (page) {
|
||||
return page.isActive
|
||||
})[0];
|
||||
|
||||
return activePage;
|
||||
}
|
||||
|
||||
const [displayMessage, setMessage] = useState(`The report is bootstrapped. Click the Embed Report button to set the access token`);
|
||||
|
||||
const controlButtons =
|
||||
<div className = "controls">
|
||||
<button onClick = { mockSignIn }>
|
||||
Embed Report</button>
|
||||
|
||||
<button onClick = { changeSettings }>
|
||||
Hide filter pane</button>
|
||||
|
||||
<button onClick = { deleteVisual }>
|
||||
Delete a Visual</button>
|
||||
</div>;
|
||||
|
||||
const header =
|
||||
<div className = "header">
|
||||
<div className = "title">Power BI React component demo</div>
|
||||
</div>;
|
||||
|
||||
const footer =
|
||||
<div className = "footer">
|
||||
<div className = "footer-text">
|
||||
GitHub:
|
||||
<a href="https://github.com/microsoft/PowerBI-client-react">https://github.com/microsoft/PowerBI-client-react</a>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ header }
|
||||
|
||||
<PowerBIEmbed
|
||||
embedConfig = { sampleReportConfig }
|
||||
eventHandlers = { eventHandlersMap }
|
||||
cssClassName = { "report-style-class" }
|
||||
getEmbeddedComponent = { (embedObject:Embed) => {
|
||||
console.log(`Embedded object of type "${ embedObject.embedtype }" received`);
|
||||
setReport(embedObject as Report);
|
||||
} }
|
||||
/>
|
||||
|
||||
<div className = "hr"></div>
|
||||
|
||||
<div className = "displayMessage">
|
||||
{ displayMessage }
|
||||
</div>
|
||||
|
||||
{ controlButtons }
|
||||
|
||||
{ footer }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DemoApp;
|
|
@ -1,10 +0,0 @@
|
|||
<!-- Copyright (c) Microsoft Corporation.
|
||||
Licensed under the MIT License. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
corembed
|
Загрузка…
Ссылка в новой задаче