Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
758fbb2
Update readme.md (#152)
jakkyn Aug 29, 2018
9af4dc2
Update trex_configure.md (#156)
d45 Aug 30, 2018
2d1187c
Update trex_getstarted.md (#157)
d45 Aug 30, 2018
f617bb1
Update trex_overview.md (#160)
d45 Oct 11, 2018
48082d8
Doc update to add design guidelines, tableau UI links, and localhost …
d45 Oct 20, 2018
09272d1
Merge branch 'master' into dev
d45 Oct 20, 2018
4db81b2
Update trex_security.md
d45 Oct 20, 2018
5748714
Update datasource.html (#170)
d45 Nov 1, 2018
17a8a81
Doc updates for 2018.3 (#178)
d45 Dec 18, 2018
56c638b
Merge branch 'master' into dev
d45 Dec 18, 2018
2008c89
Update trex_publish.md
d45 Dec 19, 2018
4f11ebd
Update trex_debugging.md
d45 Dec 19, 2018
8d482a5
Update trex_publish.md
d45 Dec 19, 2018
a524e05
Update trex_publish.md
d45 Dec 19, 2018
f55e308
Update trex_publish.md
d45 Dec 19, 2018
cc18de7
Doc updates for 2019.1 and v1.2 library; show-hide, chromium, API syn…
d45 Feb 19, 2019
a7ba46a
Update ux_controls_ui_patterns.md (#199)
d45 Feb 27, 2019
61996d3
Fixing missing .gif file
d45 Feb 27, 2019
a370e14
Delete 3-Small)on_Dark_Background.gif
d45 Feb 27, 2019
f3cb9fc
Release 1.1.0
Feb 27, 2019
f02dd39
Merge branch 'master' into dev
seanmakesgames Feb 27, 2019
d0988d2
[Security] Fix DOM XSS vulnerability in search
jdomingu Mar 14, 2019
d93e356
Update trex_examples.md (#203)
d45 Mar 15, 2019
5e69a12
Release 1.2
Apr 16, 2019
af7697d
updating release notes for the version 1.2 library (#214)
d45 Apr 16, 2019
f01298f
Merge pull request #216 from tableau/nlakshman/releasev1.2
nikhillakshman Apr 16, 2019
7b8018b
Updating documentation for version 1.2 of the library
Apr 16, 2019
90d0ffa
Merge pull request #219 from tableau/nlakshman/docUpdateRelease1.2
nikhillakshman Apr 16, 2019
53e1e5a
Merge branch 'master' into dev
Apr 17, 2019
64aff17
Renaming library files to new naming format
Apr 17, 2019
ff362cc
updated the library file name with new convention; removed page visib…
d45 Apr 18, 2019
50deef0
Nlakshman/library reference (#221)
nikhillakshman Apr 18, 2019
1da69d4
Update trex_security.md (#224)
d45 Apr 19, 2019
0122902
Doc updates for v1.3: Added TypeScript support, misc. doc fixes. (#244)
d45 Jul 31, 2019
4666a50
fixing a typo in Known Issues topic
d45 Aug 7, 2019
c6b5476
Merge branch 'dev' into add_typescript
Aug 7, 2019
5d5dc24
add updated documentation and TypeScript samples; include extensions-…
Aug 9, 2019
ab99a0d
Merge pull request #246 from tableau/add_typescript
brtal Aug 10, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ node_modules/
npm-debug.log
package-lock.json
Gemfile.lock
yarn.lock

# Ignore webpack generated files
dist/
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ The Extensions API lets you do more without leaving Tableau. Build Tableau exten
1. Copy the `.trex` files of the sample you wish to run to `~\Documents\My Tableau Repository (Beta)\Extensions` so they are available to Tableau.
2. Open a command prompt window to the location where you cloned this repo.
3. Run `npm install`.
4. Run `npm start`.
5. Launch Tableau and use the sample in a dashboard.
4. Run `npm run build`.
5. Run `npm start`.
6. Launch Tableau and use the sample in a dashboard.

>**Note** The web server just serves the extension samples and tutorial, which have URLs similar to the following: `http://localhost:8765/Samples/DataSources/datasources.html`
### Typescript Development
Samples written in Typescript are located in the Samples-Typescript folder. To support local typescript development, `npm run dev` command starts up the http server and actively listens for changes to the .ts files located in the Sample-Typescript folder.

>**Note** The web server just serves the extension samples and tutorial, which have URLs similar to the following: `http://localhost:8765/Samples/DataSources/datasources.html` or `http://localhost:8765/Samples-Typescript/DataSources/datasources.html`
> This local web server is not intended to serve the Extensions API Help pages.
> View the Help on GitHub at [https://tableau.github.io/extensions-api](https://tableau.github.io/extensions-api).

Expand Down
22 changes: 22 additions & 0 deletions Samples-Typescript/DataSources/DataSources.trex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest manifest-version="0.1" xmlns="http://www.tableau.com/xml/extension_manifest">
<dashboard-extension id="com.tableau.extensions.samples.datasources" extension-version="0.6.0">
<default-locale>en_US</default-locale>
<name resource-id="name"/>
<description>DataSources Sample</description>
<author name="tableau" email="github@tableau.com" organization="tableau" website="https://www.tableau.com"/>
<min-api-version>0.8</min-api-version>
<source-location>
<url>http://localhost:8765/Samples-Typescript/DataSources/datasources.html</url>
</source-location>
<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>
<permissions>
<permission>full data</permission>
</permissions>
</dashboard-extension>
<resources>
<resource id="name">
<text locale="en_US">DataSources Sample</text>
</resource>
</resources>
</manifest>
97 changes: 97 additions & 0 deletions Samples-Typescript/DataSources/datasources.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Datasources Sample</title>

<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" ></script>

<!-- Extensions Library (this will be hosted on a CDN eventually) -->
<script src="../../lib/tableau.extensions.1.latest.js"></script>

<!-- Our webpack'd extension's code -->
<script src="../../dist/datasources.js"></script>
</head>
<body>
<div class="container">
<!-- DataSources Table -->
<div id="dataSources">
<h4>All DataSources</h4>
<div class="table-responsive">
<table id="loading" class="table">
<tbody><tr><td>Loading...</td></tr></tbody>
</table>
<table id="dataSourcesTable" class="table table-striped hidden">
<thead>
<tr>
<th>DataSource Name</th>
<th>Auto Refresh</th>
<th style="width: 100%">Info</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>

<!-- More dataSource info modal -->
<div class="modal fade" id="infoModal" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">DataSource Details</h4>
</div>
<div id="dataSourceDetails" class="modal-body">
<div class="table-responsive">
<table id="detailsTable" class="table">
<tbody>
<tr>
<td>DataSource Name</td>
<td id="nameDetail"></td>
</tr>
<tr>
<td>DataSource Id</td>
<td id="idDetail"></td>
</tr>
<tr>
<td>Type</td>
<td id="typeDetail"></td>
</tr>
<tr>
<td>Fields</td>
<td id="fieldsDetail"></td>
</tr>
<tr>
<td>Connections</td>
<td id="connectionsDetail"></td>
</tr>
<tr>
<td>Active Tables</td>
<td id="activeTablesDetail"></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

</div>
</div>
</div>
</body>
</html>
141 changes: 141 additions & 0 deletions Samples-Typescript/DataSources/datasources.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { DataSource } from '@tableau/extensions-api-types';

// Wrap everything in an anonymous function to avoid polluting the global namespace
(async () => {
class DataSources {
// Avoid globals.
constructor(private _$: JQueryStatic) { }

/**
* Refreshes the given dataSource
* @param dataSource
*/
private static async refreshDataSource(dataSource: DataSource) {
await dataSource.refreshAsync();
console.log(dataSource.name + ': Refreshed Successfully');
}

/**
* Initializes the extension
*/
public async initialize() {
console.log('Waiting for DOM ready');
await this._$.ready;
console.log('Initializing extension API');
await tableau.extensions.initializeAsync();

// Since dataSource info is attached to the worksheet, we will perform
// one async call per worksheet to get every dataSource used in this
// dashboard. This demonstrates the use of Promise.all to combine
// promises together and wait for each of them to resolve.
const dataSourceFetchPromises: Array<Promise<DataSource[]>> = [];

// To get dataSource info, first get the dashboard.
const dashboard = tableau.extensions.dashboardContent.dashboard;
// Then loop through each worksheet and get its dataSources, save promise for later.
dashboard.worksheets.forEach(worksheet => dataSourceFetchPromises.push(worksheet.getDataSourcesAsync()));
const fetchResults = await Promise.all(dataSourceFetchPromises);

// Maps dataSource id to dataSource so we can keep track of unique dataSources.
const dataSourcesCheck = {};
const dashboardDataSources: DataSource[] = [];

fetchResults.forEach(dss => {
dss.forEach(ds => {
if (!dataSourcesCheck[ds.id]) {
// We've already seen it, skip it.
dataSourcesCheck[ds.id] = true;
dashboardDataSources.push(ds);
}
});
});

this.buildDataSourcesTable(dashboardDataSources);

// This just modifies the UI by removing the loading banner and showing the dataSources table.
this._$('#loading').addClass('hidden');
this._$('#dataSourcesTable')
.removeClass('hidden')
.addClass('show');
}

/**
* Displays a modal dialog with more details about the given dataSource.
* @param dataSource
*/
private async showModal(dataSource: DataSource) {
const modal = this._$('#infoModal');

this._$('#nameDetail').text(dataSource.name);
this._$('#idDetail').text(dataSource.id);
this._$('#typeDetail').text((dataSource.isExtract) ? 'Extract' : 'Live');

// Loop through every field in the dataSource and concat it to a string.
let fieldNamesStr = '';
dataSource.fields.forEach(function(field) {
fieldNamesStr += field.name + ', ';
});
// Slice off the last ", " for formatting.
this._$('#fieldsDetail').text(fieldNamesStr.slice(0, -2));

// Loop through each connection summary and list the connection's
// name and type in the info field
const connectionSummaries = await dataSource.getConnectionSummariesAsync();
let connectionsStr = '';
connectionSummaries.forEach(function(summary) {
connectionsStr += summary.name + ': ' + summary.type + ', ';
});
// Slice of the last ", " for formatting.
this._$('#connectionsDetail').text(connectionsStr.slice(0, -2));

// Loop through each table that was used in creating this datasource
const activeTables = await dataSource.getActiveTablesAsync();
let tableStr = '';
activeTables.forEach(function(table) {
tableStr += table.name + ', ';
});
// Slice of the last ", " for formatting.
this._$('#activeTablesDetail').text(tableStr.slice(0, -2));

// @ts-ignore
modal.modal('show');
}

/**
* Constructs UI that displays all the dataSources in this dashboard
* given a mapping from dataSourceId to dataSource objects.
* @param dataSources
*/
private buildDataSourcesTable(dataSources: DataSource[]) {
// Clear the table first.
this._$('#dataSourcesTable > tbody tr').remove();
const dataSourcesTable = this._$('#dataSourcesTable > tbody')[0];

// Add an entry to the dataSources table for each dataSource.
for (const dataSource of dataSources) {
// @ts-ignore
const newRow = dataSourcesTable.insertRow(dataSourcesTable.rows.length);
const nameCell = newRow.insertCell(0);
const refreshCell = newRow.insertCell(1);
const infoCell = newRow.insertCell(2);

const refreshButton = document.createElement('button');
refreshButton.innerHTML = 'Refresh Now';
refreshButton.type = 'button';
refreshButton.className = 'btn btn-primary';
refreshButton.addEventListener('click', () => DataSources.refreshDataSource(dataSource));

const infoSpan = document.createElement('span');
infoSpan.className = 'glyphicon glyphicon-info-sign';
infoSpan.addEventListener('click', () => this.showModal(dataSource));

nameCell.innerHTML = dataSource.name;
refreshCell.appendChild(refreshButton);
infoCell.appendChild(infoSpan);
}
}
}

console.log('Initializing DataSources extension.');
await new DataSources($).initialize();
})();
19 changes: 19 additions & 0 deletions Samples-Typescript/Filtering/Filtering.trex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest manifest-version="0.1" xmlns="http://www.tableau.com/xml/extension_manifest">
<dashboard-extension id="com.tableau.extensions.samples.filtering" extension-version="0.6.0">
<default-locale>en_US</default-locale>
<name resource-id="name"/>
<description>Filtering Sample</description>
<author name="tableau" email="github@tableau.com" organization="tableau" website="https://www.tableau.com"/>
<min-api-version>0.8</min-api-version>
<source-location>
<url>http://localhost:8765/Samples-Typescript/Filtering/filtering.html</url>
</source-location>
<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>
</dashboard-extension>
<resources>
<resource id="name">
<text locale="en_US">Filtering Sample</text>
</resource>
</resources>
</manifest>
56 changes: 56 additions & 0 deletions Samples-Typescript/Filtering/filtering.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Filtering Sample</title>

<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" ></script>

<!-- Extensions Library (this will be hosted on a CDN eventually) -->
<script src="../../lib/tableau.extensions.1.latest.js"></script>

<!-- Our webpack'd extension's code -->
<script src="../../dist/filtering.js"></script>
</head>
<body>
<div class="container">
<!-- Filters Table -->
<div id="filters">
<h4>Current Filters</h4>
<div class="table-responsive">
<table id="loading" class="table">
<tbody><tr><td>Loading...</td></tr></tbody>
</table>
<table id="filtersTable" class="table table-striped hidden">
<thead>
<tr>
<th>Filtered Field</th>
<th>Filtered Worksheet</th>
<th>Filter Type</th>
<th style="width: 100%">Current Values</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<table id="noFiltersWarning" class="table bg-danger hidden">
<tbody><tr><td>There are no filters currently active in this dashboard.</td></tr></tbody>
</table>
</div>
</div>

<!-- New Settings Submission Form -->
<div id="filterActions">
<button id="clear" type="button" class="btn btn-primary">Clear All</button>
</div>
</div>
</body>
</html>
Loading