Skip to content

Commit

Permalink
add updated documentation and TypeScript samples; include extensions-…
Browse files Browse the repository at this point in the history
…api-types 1.3 and new extensions-api 1.3.
  • Loading branch information
Brett Taylor committed Aug 9, 2019
1 parent c6b5476 commit 5d5dc24
Show file tree
Hide file tree
Showing 113 changed files with 14,595 additions and 17,309 deletions.
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="[email protected]" 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="[email protected]" 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

0 comments on commit 5d5dc24

Please sign in to comment.