+
+
diff --git a/Samples-Typescript/DashboardLayout/dashboardLayout.ts b/Samples-Typescript/DashboardLayout/dashboardLayout.ts
new file mode 100644
index 00000000..29e6fbe9
--- /dev/null
+++ b/Samples-Typescript/DashboardLayout/dashboardLayout.ts
@@ -0,0 +1,154 @@
+import { DashboardLayoutChange, DashboardLayoutChangedEvent, DashboardObject, TableauEvent } from '@tableau/extensions-api-types';
+
+// Wrap everything in an anonymous function to avoid polluting the global namespace
+(async () => {
+ class DashboardLayout {
+ public dashboardObjects: DashboardObject[];
+ private self: DashboardLayout;
+
+ // Avoid globals.
+ constructor(private _$: JQueryStatic) {}
+
+ /**
+ * Initializes the extension
+ */
+ public async initialize() {
+ console.log('Waiting for DOM ready');
+ await this._$.ready;
+ console.log('Initializing extension API');
+ await tableau.extensions.initializeAsync();
+
+ const dashboard = tableau.extensions.dashboardContent.dashboard;
+ this.dashboardObjects = dashboard.objects;
+ console.log(this.dashboardObjects);
+
+ // enabling dashboard event button
+ this._$('#dashboard-event-btn').prop('disabled', false);
+ this._$('#dashboard-event-btn').click(this.onEventButtonClick.bind(this));
+ }
+
+ // When changes are made to the dashboard we get all the details for each of the
+ // dashboard objects that were changed and compare it with their previous values.
+ // The dashboardLayoutChangeDetails property is a map of dashboard obj3ct ids to
+ // an array of dashboard layout changes.
+ // Dashboard layout change events are invoked when dashboard objects are resized,
+ // repositioned, added, and more. See DashboardLayoutChange in the API documentation
+ // for all possible actions.
+ // Extension reloads when worksheets are added / removed.
+ private onDashboardLayoutChange(event: TableauEvent) {
+ console.log(event);
+ const dashboardEvent = event as DashboardLayoutChangedEvent;
+ const dashboardEventDetails = dashboardEvent.dashboardLayoutChangeDetails;
+ const dashboard = tableau.extensions.dashboardContent.dashboard;
+
+ // updating dashboard objects and storing the previous dashboard objects for referrence.
+ const oldDashboardObjects = this.dashboardObjects;
+ this.dashboardObjects = dashboard.objects;
+
+ // An empty dashboard layout change event may be invoked when loading an extension from the manifest.
+ // In this case we ignore it and return.
+ if (dashboardEventDetails === undefined || dashboardEventDetails.size === 0) {
+ return;
+ }
+
+ // Emptying previous content from the UI's change list.
+ this._$('#dashboard-layout-change-list').empty();
+
+ // Updating UI's change list to display information on the current dashboard event.
+ dashboardEventDetails.forEach((changesMade: DashboardLayoutChange[], dashboardObjectId: number) => {
+ // getting dashboard object from its id
+ const dashboardObject = dashboard.getDashboardObjectById(dashboardObjectId);
+
+ // building a div for the changes made to this dashboard object.
+ const changesDiv = this._$('
');
+
+ // checking if this dashboard object was added as part of the event.
+ if (changesMade.includes(tableau.DashboardLayoutChange.Added)) {
+ const toAppend = this._$('');
+ toAppend.text(`Dashboard Object ${dashboardObjectId} added: "${dashboardObject.name}"`);
+ changesDiv.append(toAppend);
+ this._$('#dashboard-layout-change-list').append(changesDiv);
+ return;
+ }
+
+ // getting old dashboard object before event to compare it with the current one.
+ const oldDashboardObject = oldDashboardObjects.find(o => o.id === dashboardObjectId);
+
+ // checking if this dashboard object was removed as part of the event.
+ if (changesMade.includes(tableau.DashboardLayoutChange.Removed)) {
+ const toAppend = this._$('');
+ toAppend.text(`Dashboard Object ${dashboardObjectId} removed: "${oldDashboardObject.name}"`);
+ changesDiv.append(toAppend);
+ this._$('#dashboard-layout-change-list').append(changesDiv);
+ return;
+ }
+
+ // the following dashboard changes are not mutually exclusive, so we list them together.
+ const h6 = this._$('');
+ h6.text(`Dashboard Object ${dashboardObjectId}: "${dashboardObject.name}"`);
+ changesDiv.append(h6);
+ const ul = this._$('
');
+
+ // checking if the dashboard object had changes to its floating state.
+ if (changesMade.includes(tableau.DashboardLayoutChange.IsFloatingChanged)) {
+ const li = this._$('');
+ li.text(`Floating is now ${dashboardObject.isFloating}, was ${oldDashboardObject.isFloating}`);
+ ul.append(li);
+ }
+
+ // checking if the dashbaord object had changes to its visibility.
+ if (changesMade.includes(tableau.DashboardLayoutChange.IsVisibleChanged)) {
+ const li = this._$('');
+ li.text(`Visibility is now ${dashboardObject.isVisible}, was ${oldDashboardObject.isVisible}`);
+ ul.append(li);
+ }
+
+ // checking if the dashboard object was repositioned.
+ if (changesMade.includes(tableau.DashboardLayoutChange.PositionChanged)) {
+ const li = this._$('');
+ const newPos = dashboardObject.position;
+ const oldPos = oldDashboardObject.position;
+ li.text(`Position is now (${newPos.x},${newPos.y}), was (${oldPos.x},${oldPos.y})`);
+ ul.append(li);
+ }
+
+ // checking if the dashboard object was resized.
+ if (changesMade.includes(tableau.DashboardLayoutChange.SizeChanged)) {
+ const li = this._$('');
+ const newSize = dashboardObject.size;
+ const oldSize = oldDashboardObject.size;
+ li.text(`Size is now ${newSize.width}x${newSize.height}, was ${oldSize.width}x${oldSize.height}`);
+ ul.append(li);
+ }
+
+ // checking if the dashboard object was renamed.
+ if (changesMade.includes(tableau.DashboardLayoutChange.NameChanged)) {
+ const li = this._$('');
+ li.text(`Name is now "${dashboardObject.name}", was "${oldDashboardObject.name}"`);
+ ul.append(li);
+ }
+
+ changesDiv.append(ul);
+ this._$('#dashboard-layout-change-list').append(changesDiv);
+ });
+ }
+
+ // This function adds a dashboard event if there is not one already, and removes it if there is.
+ private onEventButtonClick() {
+ const dashboard = tableau.extensions.dashboardContent.dashboard;
+ if ($('#dashboard-event-btn').text() === 'Add Dashboard Event') {
+ dashboard.addEventListener(tableau.TableauEventType.DashboardLayoutChanged,
+ (event) => this.onDashboardLayoutChange(event));
+ $('#dashboard-event-btn').text('Remove Dashboard Event');
+ } else {
+ dashboard.removeEventListener(tableau.TableauEventType.DashboardLayoutChanged,
+ (event) => this.onDashboardLayoutChange(event));
+ $('#dashboard-layout-change-list').empty();
+ $('#dashboard-event-btn').text('Add Dashboard Event');
+ }
+ }
+ }
+
+ console.log('Initializing DashboardLayout extension.');
+ await new DashboardLayout($).initialize();
+})();
diff --git a/Samples/DashboardLayout/ClickThrough.trex b/Samples/DashboardLayout/ClickThrough.trex
new file mode 100644
index 00000000..9afd1f8b
--- /dev/null
+++ b/Samples/DashboardLayout/ClickThrough.trex
@@ -0,0 +1,19 @@
+
+
+
+ en_US
+
+ ClickThrough Sample
+
+ 1.7
+
+ http://localhost:8765/Samples/DashboardLayout/clickThrough.html
+
+ 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
+
+
+
+ ClickThrough Sample
+
+
+
diff --git a/Samples/DashboardLayout/DashboardLayout.trex b/Samples/DashboardLayout/DashboardLayout.trex
new file mode 100644
index 00000000..ddaf84b8
--- /dev/null
+++ b/Samples/DashboardLayout/DashboardLayout.trex
@@ -0,0 +1,19 @@
+
+
+
+ en_US
+
+ Dashboard Layout Sample
+
+ 1.7
+
+ http://localhost:8765/Samples/DashboardLayout/dashboardLayout.html
+
+ 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
+
+
+
+ Dashboard Layout Sample
+
+
+
diff --git a/Samples/DashboardLayout/clickThrough.html b/Samples/DashboardLayout/clickThrough.html
new file mode 100644
index 00000000..682df9f0
--- /dev/null
+++ b/Samples/DashboardLayout/clickThrough.html
@@ -0,0 +1,25 @@
+
+
+
+ ClickThrough Sample
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Set Click Through (reload to disable)
+
+
+
+
diff --git a/Samples/DashboardLayout/clickThrough.js b/Samples/DashboardLayout/clickThrough.js
new file mode 100644
index 00000000..4c00ed18
--- /dev/null
+++ b/Samples/DashboardLayout/clickThrough.js
@@ -0,0 +1,19 @@
+'use strict';
+
+// Wrap everything in an anonymous function to avoid polluting the global namespace
+(function () {
+ $(document).ready(function () {
+ tableau.extensions.initializeAsync().then(function () {
+ $('#enable-click-through').click(() => {
+ tableau.extensions.setClickThroughAsync(true).then(() => {
+ console.log('click through enabled! (reload to disable)');
+ }, (err) => {
+ console.log(err);
+ });
+ });
+ }, function (err) {
+ // Something went wrong in initialization.
+ console.log('Error while Initializing: ' + err.toString());
+ });
+ });
+})();
diff --git a/Samples/DashboardLayout/dashboardLayout.html b/Samples/DashboardLayout/dashboardLayout.html
new file mode 100644
index 00000000..dc392969
--- /dev/null
+++ b/Samples/DashboardLayout/dashboardLayout.html
@@ -0,0 +1,28 @@
+
+
+
+ Dashboard Layout Sample
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dashboard Layout Sample
+
+
+
Dashboard Changes
+
+
+
+
+
diff --git a/Samples/DashboardLayout/dashboardLayout.js b/Samples/DashboardLayout/dashboardLayout.js
new file mode 100644
index 00000000..e2c14fb6
--- /dev/null
+++ b/Samples/DashboardLayout/dashboardLayout.js
@@ -0,0 +1,142 @@
+'use strict';
+
+// Wrap everything in an anonymous function to avoid polluting the global namespace
+(function () {
+ let dashboardObjects = [];
+
+ $(document).ready(function () {
+ tableau.extensions.initializeAsync().then(function () {
+ const dashboard = tableau.extensions.dashboardContent.dashboard;
+ dashboardObjects = dashboard.objects;
+ console.log(dashboardObjects);
+
+ // enabling dashboard event button
+ $('#dashboard-event-btn').prop('disabled', false);
+ $('#dashboard-event-btn').click(handleEventButtonClick);
+ });
+ });
+
+ // When changes are made to the dashboard we get all the details for each of the
+ // dashboard objects that were changed and compare it with their previous values.
+ // The dashboardLayoutChangeDetails property is a map of dashboard obejct ids to an array of dashboard layout changes.
+ // Dashboard layout change events are invoked when dashboard objects are resized, repositioned,
+ // added, and more. See DashboardLayoutChange in the API documentation for all possible actions.
+ // Extension reloads when worksheets are added / removed.
+ function onDashboardLayoutChange (event) {
+ console.log(event);
+
+ // getting the dashboard event's details map
+ const dashboardEventDetails = event.dashboardLayoutChangeDetails;
+ const dashboard = tableau.extensions.dashboardContent.dashboard;
+
+ // updating dashboard objects and storing the previous dashboard objects for referrence.
+ let oldDashboardObjects = dashboardObjects;
+ dashboardObjects = dashboard.objects;
+
+ // An empty dashboard layout change event may be invoked when loading an extension from the manifest.
+ // In this case we ignore it and return.
+ if (dashboardEventDetails === undefined || dashboardEventDetails.size === 0) {
+ return;
+ }
+
+ // Emptying previous content from the UI's change list.
+ $('#dashboard-layout-change-list').empty();
+
+ // Updating UI's change list to display information on the current dashboard event.
+ dashboardEventDetails.forEach(function (changesMade, dashboardObjectId) {
+ // getting dashboard object from its id
+ let dashboardObject = dashboard.getDashboardObjectById(dashboardObjectId);
+
+ // building a div for the changes made to this dashboard object.
+ let changesDiv = $('
');
+
+ // checking if this dashboard object was added as part of the event.
+ if (changesMade.includes('added')) {
+ let toAppend = $('');
+ toAppend.text(`Dashboard Object ${dashboardObjectId} added: "${dashboardObject.name}"`);
+ changesDiv.append(toAppend);
+ $('#dashboard-layout-change-list').append(changesDiv);
+ return;
+ }
+
+ // getting old dashboard object before event to compare it with the current one.
+ let oldDashboardObject;
+ for (let i = 0; i < oldDashboardObjects.length; i++) {
+ if (oldDashboardObjects[i].id === dashboardObjectId) {
+ oldDashboardObject = oldDashboardObjects[i];
+ break;
+ }
+ }
+
+ // checking if this dashboard object was removed as part of the event.
+ if (changesMade.includes('removed')) {
+ let toAppend = $('');
+ toAppend.text(`Dashboard Object ${dashboardObjectId} removed: "${oldDashboardObject.name}"`);
+ changesDiv.append(toAppend);
+ $('#dashboard-layout-change-list').append(changesDiv);
+ return;
+ }
+
+ // the following dashboard changes are not mutually exclusive, so we list them together.
+ let h6 = $('');
+ h6.text(`Dashboard Object ${dashboardObjectId}: "${dashboardObject.name}"`);
+ changesDiv.append(h6);
+ let ul = $('
');
+
+ // checking if the dashboard object had changes to its floating state.
+ if (changesMade.includes('is-floating-changed')) {
+ let li = $('');
+ li.text(`Floating is now ${dashboardObject.isFloating}, was ${oldDashboardObject.isFloating}`);
+ ul.append(li);
+ }
+
+ // checking if the dashbaord object had changes to its visibility.
+ if (changesMade.includes('is-visible-changed')) {
+ let li = $('');
+ li.text(`Visibility is now ${dashboardObject.isVisible}, was ${oldDashboardObject.isVisible}`);
+ ul.append(li);
+ }
+
+ // checking if the dashboard object was repositioned.
+ if (changesMade.includes('position-changed')) {
+ let li = $('');
+ let newPos = dashboardObject.position;
+ let oldPos = oldDashboardObject.position;
+ li.text(`Position is now (${newPos.x},${newPos.y}), was (${oldPos.x},${oldPos.y})`);
+ ul.append(li);
+ }
+
+ // checking if the dashboard object was resized.
+ if (changesMade.includes('size-changed')) {
+ let li = $('');
+ let newSize = dashboardObject.size;
+ let oldSize = oldDashboardObject.size;
+ li.text(`Size is now ${newSize.width}x${newSize.height}, was ${oldSize.width}x${oldSize.height}`);
+ ul.append(li);
+ }
+
+ // checking if the dashboard object was renamed.
+ if (changesMade.includes('name-changed')) {
+ let li = $('');
+ li.text(`Name is now "${dashboardObject.name}", was "${oldDashboardObject.name}"`);
+ ul.append(li);
+ }
+
+ changesDiv.append(ul);
+ $('#dashboard-layout-change-list').append(changesDiv);
+ });
+ }
+
+ // This function adds a dashboard event if there is not one already, and removes it if there is.
+ function handleEventButtonClick () {
+ const dashboard = tableau.extensions.dashboardContent.dashboard;
+ if ($('#dashboard-event-btn').text() === 'Add Dashboard Event') {
+ dashboard.addEventListener(tableau.TableauEventType.DashboardLayoutChanged, onDashboardLayoutChange);
+ $('#dashboard-event-btn').text('Remove Dashboard Event');
+ } else {
+ dashboard.removeEventListener(tableau.TableauEventType.DashboardLayoutChanged, onDashboardLayoutChange);
+ $('#dashboard-layout-change-list').empty();
+ $('#dashboard-event-btn').text('Add Dashboard Event');
+ }
+ }
+})();
diff --git a/webpack.config.js b/webpack.config.js
index c1666524..d6b90d05 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -4,6 +4,7 @@ const path = require('path');
module.exports = {
entry: {
+ dashboardLayout: './Samples-Typescript/DashboardLayout/dashboardLayout.ts',
datasources: './Samples-Typescript/DataSources/datasources.ts',
filtering: './Samples-Typescript/Filtering/filtering.ts',
formatting: './Samples-Typescript/Formatting/formatting.tsx',