Skip to content

Commit 065e5e1

Browse files
authored
Merge pull request #483 from tableau/dev
Merge dev with main for release 1.8.1
2 parents 3202321 + fb018b9 commit 065e5e1

File tree

94 files changed

+29721
-10777
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+29721
-10777
lines changed

README.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The Extensions API lets you do more without leaving Tableau. Build Tableau exten
99
## Setup and Running Samples
1010

1111
### Prerequisites
12-
* You must have Node.js and npm installed. You can get these from [http://nodejs.org](http://nodejs.org).
12+
* You must have Node.js and npm installed. You can get these from [https://nodejs.org](https://nodejs.org).
1313

1414
### Install Extensions API SDK Components and Start Server
1515

@@ -35,15 +35,14 @@ The Extensions API lets you do more without leaving Tableau. Build Tableau exten
3535
3636
### Typescript Development
3737
Samples written in Typescript are located in the `Samples-Typescript` folder.
38-
If you want to use TypeScript to write your extensions, you can run a script that starts up the HTTP server and actively listens for changes to the `.ts` files located in the `Sample-Typescript` folder. You can then add your extension to the folder and use the script to transpile your extension to JavaScript.
38+
If you want to use TypeScript to write your extensions, you can run a script that starts up the HTTP server and actively listens for changes to the `.ts` files located in the `Samples-Typescript` folder. You can then add your extension to the folder and use the script to transpile your extension to JavaScript.
3939

4040
* To start the the HTTP server and listen for changes to the `.ts` files.
4141

4242
**npm run dev**
4343

4444
For more information, see [Use TypeScript with the Extensions API](https://tableau.github.io/extensions-api/docs/trex_typescript.html).
4545

46-
4746
### Sandboxed Extension Development Environment
4847

4948
Tableau is introducing development support for Sandboxed Extensions with Tableau 2019.3. Sandboxed Extensions run in a virtual sandbox and ensure the extension can’t make network calls outside of the hosting Tableau Server. The Extensions API SDK provides a local development environment that replicates the Tableau Hosting Cloud Service for Sandboxed Extensions. You can test your Sandboxed extensions locally with the same sandbox policies.
@@ -56,22 +55,19 @@ Tableau is introducing development support for Sandboxed Extensions with Tableau
5655

5756
For more information, see [Create and Test Sandboxed Extensions](https://tableau.github.io/extensions-api/docs/trex_sandbox_test.html).
5857

58+
## Contributions
59+
Contributions and improvements by the community are welcomed!
60+
See the LICENSE file for current open-source licensing and use information.
5961

60-
## Submissions
61-
We would love submissions to either the Docs or Sample code! To contribute, first sign our CLA that can be found [here](https://tableau.github.io/contributing.html). To submit a contribution, please fork the repository then submit a pull request to the `submissions` branch.
62+
Before we can accept pull requests from contributors, we require a signed [Contributor License Agreement (CLA)](https://tableau.github.io/contributing.html). To submit a contribution, please fork the repository then submit a pull request to the `main` branch.
6263

6364
## Code Style
6465
Our sample code follows the [Semi-Standard Style](https://github.com/Flet/semistandard) for JavaScript samples linting and [tslint](https://palantir.github.io/tslint/) for TypeScript. If you add your own extension code to the Samples or Samples-Typescript directories, you can run `npm run lint` to validate the style of your code. Please run this command before submitting any pull requests for Sample code.
6566

66-
## Contributions
67-
Code contributions and improvements by the community are welcomed!
68-
See the LICENSE file for current open-source licensing and use information.
69-
70-
Before we can accept pull requests from contributors, we require a signed [Contributor License Agreement (CLA)](http://tableau.github.io/contributing.html).
67+
`npx semistandard --fix` to fix linting issues which can be fixed automatically.
7168

7269
## Documentation
7370
[Visit the project website and read the documentation here.](https://tableau.github.io/extensions-api/)
7471

75-
7672
## Issues
77-
Use [Issues](https://github.com/tableau/ProjectFrelard/issues) to log any problems or bugs you encounter in the docs or sample code.
73+
Use [Issues](https://github.com/tableau/extensions-api/issues) to log any problems or bugs you encounter in the docs or sample code, or to discuss any proposed changes or additions.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest manifest-version="0.1" xmlns="http://www.tableau.com/xml/extension_manifest">
3+
<dashboard-extension id="com.tableau.extensions.samples.dashboardobjectvisibility" extension-version="0.1.0">
4+
<default-locale>en_US</default-locale>
5+
<name resource-id="name"/>
6+
<description>Dashboard Object Visibility Sample</description>
7+
<author name="tableau" email="[email protected]" organization="tableau" website="https://www.tableau.com"/>
8+
<min-api-version>1.7</min-api-version>
9+
<source-location>
10+
<url>http://localhost:8765/Samples-Typescript/DashboardObjectVisibility/dashboardObjectVisibility.html</url>
11+
</source-location>
12+
<icon>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</icon>
13+
</dashboard-extension>
14+
<resources>
15+
<resource id="name">
16+
<text locale="en_US">Dashboard Object Visibility Sample</text>
17+
</resource>
18+
</resources>
19+
</manifest>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>Dashboard Object Visibility Sample</title>
5+
<!-- jQuery -->
6+
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
7+
8+
<!-- Bootstrap -->
9+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
10+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
11+
12+
<script src="../../lib/tableau.extensions.1.latest.js"></script>
13+
14+
<!-- Our extension's code -->
15+
<script src="../../dist/dashboardObjectVisibility.js"></script>
16+
</head>
17+
<body>
18+
<div class="container">
19+
<h1>Dashboard Object Visibility Sample</h1>
20+
<h3>Dashboard Content List</h3>
21+
<div id="dashboard-object-list" class="container"></div>
22+
</body>
23+
</html>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { DashboardObject, TableauEvent } from '@tableau/extensions-api-types';
2+
import * as React from 'react';
3+
import * as ReactDOM from 'react-dom';
4+
5+
// Wrap everything in an anonymous function to avoid polluting the global namespace
6+
(async () => {
7+
interface IDashboardObjectVisibilityState {
8+
dashboardObjects: DashboardObject[];
9+
visiblityOverrides: Map<number, boolean>;
10+
}
11+
12+
class DashboardObjectVisibility extends React.Component<{}, IDashboardObjectVisibilityState> {
13+
public constructor(props = {}) {
14+
super(props);
15+
const dashboard = tableau.extensions.dashboardContent.dashboard;
16+
console.log(dashboard.objects);
17+
this.state = { dashboardObjects: dashboard.objects, visiblityOverrides: new Map() };
18+
dashboard.addEventListener(tableau.TableauEventType.DashboardLayoutChanged,
19+
(event) => this.onDashboardLayoutChange(event));
20+
}
21+
22+
public static async initializeAndRender(): Promise<void> {
23+
// This is the entry point into the extension. It initializes the Tableau Extensions Api, and then
24+
// will create button elements to show/hide dashboard objects on the dashboard.
25+
console.log('Initializing extension API');
26+
await tableau.extensions.initializeAsync();
27+
28+
ReactDOM.render(<DashboardObjectVisibility></DashboardObjectVisibility>,
29+
document.getElementById('dashboard-object-list'));
30+
}
31+
32+
public render(): JSX.Element {
33+
return <>
34+
{this.state.dashboardObjects.map(dashboardObject => (
35+
this.renderListItem(dashboardObject)
36+
))}
37+
</>;
38+
}
39+
40+
private renderListItem(dashboardObject: DashboardObject): JSX.Element {
41+
const isVisible = this.getCurrentIsVisible(dashboardObject);
42+
const buttonText = (isVisible ? 'Hide "' : 'Show "') + dashboardObject.name + '"';
43+
44+
return <li className='list-group-item list-group-item-primary'>
45+
<button onClick={() => this.showHideDashboardObject(dashboardObject)}>{buttonText}</button>
46+
</li>;
47+
}
48+
49+
private getCurrentIsVisible(dashboardObject: DashboardObject): boolean {
50+
if (this.state.visiblityOverrides.has(dashboardObject.id)) {
51+
return this.state.visiblityOverrides.get(dashboardObject.id);
52+
} else {
53+
return dashboardObject.isVisible;
54+
}
55+
}
56+
57+
private onDashboardLayoutChange(event: TableauEvent): void {
58+
const dashboard = tableau.extensions.dashboardContent.dashboard;
59+
this.setState({ dashboardObjects: dashboard.objects, visiblityOverrides: new Map() });
60+
}
61+
62+
private showHideDashboardObject(dashboardObject: DashboardObject): void {
63+
const currentIsVisible = this.getCurrentIsVisible(dashboardObject);
64+
this.updateStateForVisiblityChange(dashboardObject, currentIsVisible);
65+
66+
const dashboardObjectVisibilityMap = new Map();
67+
const newDashboardObjectVisibilityType = currentIsVisible ?
68+
tableau.DashboardObjectVisibilityType.Hide : tableau.DashboardObjectVisibilityType.Show;
69+
70+
dashboardObjectVisibilityMap.set(dashboardObject.id, newDashboardObjectVisibilityType);
71+
const dashboard = tableau.extensions.dashboardContent.dashboard;
72+
dashboard.setDashboardObjectVisibilityAsync(dashboardObjectVisibilityMap).then(() => {
73+
console.log('Done changing dashboard object visiblity');
74+
});
75+
}
76+
77+
private updateStateForVisiblityChange(dashboardObject: DashboardObject, currentIsVisible: boolean): void {
78+
const id = dashboardObject.id;
79+
const newIsVisible = !currentIsVisible;
80+
this.setState(prevState => {
81+
const newVisiblityOverrides = new Map(prevState.visiblityOverrides);
82+
newVisiblityOverrides.set(id, newIsVisible);
83+
return { visiblityOverrides: newVisiblityOverrides };
84+
});
85+
}
86+
}
87+
88+
await DashboardObjectVisibility.initializeAndRender();
89+
})();
Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,65 @@
1+
import { TableauEvent } from '@tableau/extensions-api-types';
12
import * as React from 'react';
23
import * as ReactDOM from 'react-dom';
34

45
// Wrap everything in an anonymous function to avoid polluting the global namespace
56
(async () => {
7+
interface IFormattingState {
8+
formattingUpdated: number;
9+
}
10+
11+
class Formatting extends React.PureComponent<{}, IFormattingState> {
12+
public constructor(props = {}) {
13+
super(props);
14+
this.state = { formattingUpdated: 0 };
15+
tableau.extensions.dashboardContent.dashboard.addEventListener(tableau.TableauEventType.WorkbookFormattingChanged,
16+
(event) => this.onWorkbookFormattingChanged(event));
17+
}
618

7-
class Formatting {
8-
// This is the entry point into the extension. It initializes the Tableau Extensions Api, and then
9-
// will create elements with appropriate class names to get Tableau formatting
10-
public async initialize() {
19+
public static async initializeAndRender(): Promise<void> {
20+
// This is the entry point into the extension. It initializes the Tableau Extensions Api, and then
21+
// will create elements with appropriate class names to get Tableau formatting
1122
console.log('Initializing extension API');
1223
await tableau.extensions.initializeAsync();
13-
await this.Render();
24+
ReactDOM.render(<Formatting></Formatting>, document.getElementById('formattingExample'));
25+
}
26+
27+
private onWorkbookFormattingChanged(event: TableauEvent): void {
28+
this.setState(prevState => {
29+
return { formattingUpdated: prevState.formattingUpdated + 1 };
30+
});
1431
}
1532

16-
private async Render() {
33+
public render(): JSX.Element {
1734
if (tableau.extensions.environment.workbookFormatting) {
18-
await this.RenderFormatting();
35+
return this.renderFormatting();
1936
} else {
20-
await this.RenderInfoMissing();
37+
return this.renderInfoMissing();
2138
}
2239
}
2340

24-
private async RenderFormatting() {
41+
private renderFormatting(): JSX.Element {
2542
const tooltipClassNames = 'tooltiptext ' + tableau.ClassNameKey.Tooltip;
43+
const formattingChangedText = 'WorkbookFormattingChanged called ' + this.state.formattingUpdated + ' times';
2644

27-
ReactDOM.render(<>
45+
return <>
2846
<h2 className={tableau.ClassNameKey.WorksheetTitle}>Subheader, using tableau-worksheet-title class</h2>
2947
<text className={tableau.ClassNameKey.Worksheet}>Text, using tableau-worksheet class</text>
3048
<h3 className='tooltip-header'>Hover to see tooltip text, which is using tableau-tooltip class
31-
<span className={tooltipClassNames}>Tooltip text, using tableau-tooltip class</span>
49+
<span className={tooltipClassNames}>Tooltip text, using tableau-tooltip class</span>
3250
</h3>
3351
<li className={tableau.ClassNameKey.StoryTitle}>Bullet Point, using tableau-story-title class</li>
3452
<text className={tableau.ClassNameKey.DashboardTitle}>Text, using tableau-dashboard-title class</text>
35-
</>, document.getElementById('formattingExample'));
53+
<text className={tableau.ClassNameKey.DashboardTitle}>{formattingChangedText}</text>
54+
</>;
3655
}
3756

38-
private async RenderInfoMissing() {
57+
private renderInfoMissing(): JSX.Element {
3958
const message = 'Tableau formatting information for extensions is missing from this version of Tableau.';
4059

41-
ReactDOM.render(<>
42-
<div style={{color: 'blue'}}>{message}</div>
43-
</>, document.getElementById('formattingExample'));
60+
return <div style={{ color: 'blue' }}>{message}</div>;
4461
}
4562
}
4663

47-
console.log('Initializing Formatting extension.');
48-
await new Formatting().initialize();
64+
await Formatting.initializeAndRender();
4965
})();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest manifest-version="0.1" xmlns="http://www.tableau.com/xml/extension_manifest">
3+
<dashboard-extension id="com.tableau.extensions.samples.moveAndResize" extension-version="0.1.0">
4+
<default-locale>en_US</default-locale>
5+
<name resource-id="name"/>
6+
<description>MoveAndResize Sample</description>
7+
<author name="tableau" email="[email protected]" organization="tableau" website="https://www.tableau.com"/>
8+
<min-api-version>1.7</min-api-version>
9+
<source-location>
10+
<url>http://localhost:8765/Samples-Typescript/MoveAndResize/moveandresize.html</url>
11+
</source-location>
12+
<icon>iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAlhJREFUOI2Nkt9vy1EYh5/3bbsvRSySCZbIxI+ZCKsN2TKtSFyIrV2WuRCJuBiJWxfuxCVXbvwFgiEtposgLFJElnbU1SxIZIIRJDKTrdu+53Uhra4mce7Oe57Pcz7JOULFisViwZ+29LAzOSjQYDgz1ZcCvWuXV11MJpN+OS/lm6179teqH0yDqxPTCyKSA8DcDsyOmOprnCaeP7459pdgy969i0LTC3IO/RQMyoHcQN+3cnljW3dNIFC47qDaK3g7BwdTkwBaBELT4ZPOUVWgKl4ZBnjxJPUlMDnTDrp0pmr6RHFeEjjcUUXPDGeSEwDN0Xg8sivxMhJNjGzbHd8PkM3eHRfkrBM5NkcQaY2vUnTlrDIA0NoaX+KLXFFlowr14tvVpqb2MICzmQcKqxvbumv+NAhZGCCIPwEw6QWXKYRL/VUXO0+rAUJiPwAk5MIlgVfwPjjHLCL1APmHN94ZdqeYN+NW/mn6I4BvwQYchcLnwFhJMDiYmlRxAzjpKWZkYkUCcZ2I61wi37tLbYyjiN0fHk5Oz3nGSLSzBbNHCF35R7f6K1/hN9PRhek11FrymfQQQKB4+Gl05P2qNRtmETlXW7e+b2z01dfycGNbfFMAbqNyKp9Jp4rzOT8RYFs0njJkc2iqsCObvTsOsDWWqA5C1uFy+Uz/oXJeKwVT4h0RmPUXhi79vuC0Ku6yOffTK3g9lfxfDQAisY516sg5kfOCiJk7HoLt2cf9b/9LANAc7dznm98PagG1fUOZ9IP5uMB8Q4CPoyNvausapkTt3rNMuvdf3C/o6+czhtdwmwAAAABJRU5ErkJggg==</icon>
13+
</dashboard-extension>
14+
<resources>
15+
<resource id="name">
16+
<text locale="en_US">MoveAndResize Sample</text>
17+
</resource>
18+
</resources>
19+
</manifest>

0 commit comments

Comments
 (0)