Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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