@thoughtspot/ts-chart-sdk 0.0.1-alpha.0 → 0.0.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +782 -0
  2. package/cjs/index.d.ts +8 -0
  3. package/cjs/index.d.ts.map +1 -0
  4. package/cjs/index.js +24 -0
  5. package/cjs/index.js.map +1 -0
  6. package/cjs/main/custom-chart-context.d.ts +39 -0
  7. package/cjs/main/custom-chart-context.d.ts.map +1 -0
  8. package/cjs/main/custom-chart-context.js +211 -0
  9. package/cjs/main/custom-chart-context.js.map +1 -0
  10. package/cjs/main/custom-chart-context.spec.d.ts +2 -0
  11. package/cjs/main/custom-chart-context.spec.d.ts.map +1 -0
  12. package/cjs/main/custom-chart-context.spec.js +379 -0
  13. package/cjs/main/custom-chart-context.spec.js.map +1 -0
  14. package/cjs/main/post-message-event-bridge.d.ts +6 -0
  15. package/cjs/main/post-message-event-bridge.d.ts.map +1 -0
  16. package/cjs/main/post-message-event-bridge.js +46 -0
  17. package/cjs/main/post-message-event-bridge.js.map +1 -0
  18. package/cjs/main/post-message-event-bridge.spec.d.ts +2 -0
  19. package/cjs/main/post-message-event-bridge.spec.d.ts.map +1 -0
  20. package/cjs/main/post-message-event-bridge.spec.js +122 -0
  21. package/cjs/main/post-message-event-bridge.spec.js.map +1 -0
  22. package/cjs/test/test-utils.d.ts +7 -0
  23. package/cjs/test/test-utils.d.ts.map +1 -0
  24. package/cjs/test/test-utils.js +14 -0
  25. package/cjs/test/test-utils.js.map +1 -0
  26. package/cjs/types/answer-column.types.d.ts +69 -0
  27. package/cjs/types/answer-column.types.d.ts.map +1 -0
  28. package/cjs/types/answer-column.types.js +59 -0
  29. package/cjs/types/answer-column.types.js.map +1 -0
  30. package/cjs/types/chart-to-ts-event.types.d.ts +48 -0
  31. package/cjs/types/chart-to-ts-event.types.d.ts.map +1 -0
  32. package/cjs/types/chart-to-ts-event.types.js +19 -0
  33. package/cjs/types/chart-to-ts-event.types.js.map +1 -0
  34. package/cjs/types/common.types.d.ts +50 -0
  35. package/cjs/types/common.types.d.ts.map +1 -0
  36. package/cjs/types/common.types.js +3 -0
  37. package/cjs/types/common.types.js.map +1 -0
  38. package/cjs/types/configurator.types.d.ts +15 -0
  39. package/cjs/types/configurator.types.d.ts.map +1 -0
  40. package/cjs/types/configurator.types.js +3 -0
  41. package/cjs/types/configurator.types.js.map +1 -0
  42. package/cjs/types/ts-to-chart-event.types.d.ts +69 -0
  43. package/cjs/types/ts-to-chart-event.types.d.ts.map +1 -0
  44. package/cjs/types/ts-to-chart-event.types.js +16 -0
  45. package/cjs/types/ts-to-chart-event.types.js.map +1 -0
  46. package/cjs/types/visual-prop.types.d.ts +47 -0
  47. package/cjs/types/visual-prop.types.d.ts.map +1 -0
  48. package/cjs/types/visual-prop.types.js +3 -0
  49. package/cjs/types/visual-prop.types.js.map +1 -0
  50. package/lib/main/custom-chart-context.d.ts +2 -1
  51. package/lib/main/custom-chart-context.d.ts.map +1 -1
  52. package/lib/main/custom-chart-context.js +11 -7
  53. package/lib/main/custom-chart-context.js.map +1 -1
  54. package/lib/main/custom-chart-context.spec.js +64 -25
  55. package/lib/main/custom-chart-context.spec.js.map +1 -1
  56. package/lib/types/chart-to-ts-event.types.d.ts +8 -8
  57. package/lib/types/chart-to-ts-event.types.d.ts.map +1 -1
  58. package/lib/types/chart-to-ts-event.types.js.map +1 -1
  59. package/lib/types/common.types.d.ts +7 -8
  60. package/lib/types/common.types.d.ts.map +1 -1
  61. package/lib/types/ts-to-chart-event.types.d.ts +2 -0
  62. package/lib/types/ts-to-chart-event.types.d.ts.map +1 -1
  63. package/lib/types/ts-to-chart-event.types.js +1 -0
  64. package/lib/types/ts-to-chart-event.types.js.map +1 -1
  65. package/package.json +2 -2
  66. package/src/main/custom-chart-context.spec.ts +87 -26
  67. package/src/main/custom-chart-context.ts +26 -16
  68. package/src/main/post-message-event-bridge.ts +2 -2
  69. package/src/types/answer-column.types.ts +5 -5
  70. package/src/types/chart-to-ts-event.types.ts +18 -28
  71. package/src/types/common.types.ts +22 -25
  72. package/src/types/configurator.types.ts +8 -8
  73. package/src/types/ts-to-chart-event.types.ts +10 -4
  74. package/src/types/visual-prop.types.ts +11 -11
package/README.md ADDED
@@ -0,0 +1,782 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/thoughtspot/visual-embed-sdk/main/static/doc-images/images/TS-Logo-black-no-bg.svg" width=120 align="center" alt="ThoughtSpot" />
3
+ </p>
4
+
5
+ <br/>
6
+
7
+ # ThoughtSpot Charts SDK <br/>
8
+
9
+ ThoughtSpot Charts SDK allows developers to integrate custom charts into ThoughtSpot. Developers can create custom charts in Javascript using charting libraries such as HighCharts and upload them to ThoughtSpot.
10
+
11
+
12
+ # Get started
13
+ This tutorial demonstrates how to create a Gantt chart using HighCharts.
14
+ <insert links and fiddle links above>
15
+ * [Highchart demo link](https://www.highcharts.com/demo/gantt/progress-indicator)
16
+ * [JSFiddle link](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/gantt/demo/progress-indicator)
17
+
18
+ ## Prerequisites
19
+ Before you begin, check for the following requirements:
20
+
21
+ * Access to a ThoughtSpot Cloud application instance
22
+ * A Development Environment (IDE) for building custom charts
23
+ * Working knowledge of JavaScript or Typescript
24
+ * Familiarity with charting libraries such as Highcharts
25
+ * Knowledge of the chart type
26
+
27
+ ## Set up your environment
28
+ To create and test the application, this tutorial uses a Vite project setup.
29
+
30
+ ### Create a new Vite project
31
+
32
+ 1. Open a terminal window and run the following commands:
33
+
34
+ ```bash
35
+ md gantt
36
+ cd gantt
37
+ ```
38
+
39
+ 2. Create a Vite project.
40
+ ```bash
41
+ $ npm create vite@latest
42
+ ```
43
+ 3. Configure the project name and development framework for your chart application. In this tutorial, we will use the Vanilla framework with TypeScript.
44
+
45
+ ```bash
46
+ ✔ Project name: … gantt demo
47
+ ✔ Package name: … gantt-demo
48
+ ✔ Select a framework: › Vanilla
49
+ ✔ Select a variant: › TypeScript
50
+ ```
51
+ 4. Initialize your application.
52
+
53
+ ```bash
54
+ npm install
55
+ npm run dev
56
+ ```
57
+
58
+ 5. Go to the localhost link and check if the following page shows up.
59
+
60
+ ![initial-setup](assets/images/initial-setup.png)
61
+
62
+ ### Install Highcharts and lodash
63
+
64
+ ```bash
65
+ npm install --save highcharts lodash
66
+ ```
67
+
68
+ ### Install the SDK
69
+
70
+ ```bash
71
+ npm install --save @thoughtspot/ts-chart-sdk
72
+ ```
73
+
74
+ ## Render a chart on your local environment
75
+
76
+ Render a chart in the application created from the preceding steps.
77
+
78
+ This tutorial uses the [chart from the Highcharts library](https://www.highcharts.com/demo/gantt/progress-indicator) to create a custom Gantt chart.
79
+
80
+ ### Implement the chart code
81
+
82
+ This tutorial uses the implementation code of the Gantt chart from the [Highcharts site](https://www.highcharts.com/demo/gantt/progress-indicator).
83
+
84
+ To implement the chart code in your application, complete these steps:
85
+
86
+ 1. To copy the implementation code from the Highcharts library, select a chart type and click **Copy JS Code**.
87
+ 2. Paste this code into the `main.ts` file in your `src` folder.
88
+ 3. Import the dependencies for Highcharts and Highcharts Gantt module into your application code as shown in this example:
89
+
90
+ ```bash
91
+ import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
92
+ import 'highcharts/es-modules/masters/modules/gantt.src';
93
+ ```
94
+ Note the order of import.
95
+
96
+ 4. Replace the content of `index.html` with the following snippet:
97
+
98
+ Note that we have replaced the div id `app` with `container` because the code copied from Highcharts points to `container`.
99
+
100
+ ```bash
101
+ <!DOCTYPE html>
102
+ <html lang="en">
103
+ <head>
104
+ <meta charset="UTF-8" />
105
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
106
+ <title>Gantt Chart Demo</title>
107
+ </head>
108
+ <body>
109
+ <div id="container"></div>
110
+ <script type="module" src="/src/main.ts"></script>
111
+ </body>
112
+ </html>
113
+ ```
114
+ 5. Refresh the application URL and check if the chart imported into your application code is rendered:
115
+ ![demo-chart-render](assets/images/demo-chart-render.png)
116
+ 6. Delete the unwanted files from your project folder
117
+ The folder structure in your project at this point includes many files as shown in this figure.
118
+
119
+ ![folder-structure](assets/images/folder-structure.png)
120
+
121
+ You can remove the following files.
122
+
123
+ * `/public/vite.svg`
124
+ * `/src/counter.ts`
125
+ * `/src/typescript.svg`
126
+
127
+ ## Integrate ThoughtSpot Chart SDK
128
+
129
+ The chart imported into your application has static data. To add ThoughtSpot capabilities such as drill, you need to fetch data from your ThoughtSpot application. To add ThoughtSpot capabilities and data to the chart code in your application, integrate ThoughtSpot Chart SDK and complete these steps:
130
+
131
+ 1. [Initialize the Chart Context](#initialize-the-chart-context)
132
+ 2. [Create a data model from input data](#Create-a-data-model-from-input-data)
133
+ 3. [Plug data into the Highcharts datasets](#plug-data-into-the-highcharts-datasets)
134
+
135
+ ### Initialize the Chart Context
136
+
137
+ Chart Context is the main context object that helps in orchestrating ThoughtSpot APIs to render charts. It also acts as a core central point of all interactions on the charts.
138
+
139
+ To initialize the chart context, call `getChartContext`:
140
+
141
+ ```jsx
142
+ const init = async () => {
143
+ const ctx = await getChartContext({
144
+ getDefaultChartConfig: (chartModel: ChartModel): ChartConfig[] => {
145
+ const columns = chartModel.columns;
146
+
147
+ // Here we assume that the columns are always coming in the
148
+ // following order.
149
+ // [Project Name, Task, Start Date, End Date, Completion]
150
+
151
+ // TBD: do basic validation here to ensure that the chart is renderable
152
+ if (columns.length < 4) {
153
+ // not possible to plot a chart
154
+ return [];
155
+ }
156
+
157
+ const chartConfig: ChartConfig = {
158
+ key: 'default',
159
+ dimensions: [
160
+ {
161
+ key: 'project-name',
162
+ columns: [columns[0]],
163
+ },
164
+ {
165
+ key: 'task',
166
+ columns: [columns[1]],
167
+ },
168
+ {
169
+ key: 'start-date',
170
+ columns: [columns[2]],
171
+ },
172
+ {
173
+ key: 'end-date',
174
+ columns: [columns[3]],
175
+ },
176
+ {
177
+ key: 'completion',
178
+ columns: columns[4] ? [columns[4]] : [],
179
+ },
180
+ ],
181
+ };
182
+ return [chartConfig];
183
+ },
184
+ getQueriesFromChartConfig: (
185
+ chartConfig: ChartConfig[],
186
+ ): Array<Query> => {
187
+ // map all the columns in the config to the query array
188
+ return chartConfig.map(
189
+ (config: ChartConfig): Query =>
190
+ _.reduce(
191
+ config.dimensions,
192
+ (acc: Query, dimension) => ({
193
+ queryColumns: [
194
+ ...acc.queryColumns,
195
+ ...dimension.columns,
196
+ ],
197
+ }),
198
+ {
199
+ queryColumns: [],
200
+ } as Query,
201
+ ),
202
+ );
203
+ },
204
+ renderChart: (context) => {},
205
+ });
206
+ };
207
+
208
+ init();
209
+ ```
210
+ > NOTE:
211
+ >For more information about the chart context component, refer to the following documentation resources:
212
+ > * [https://ts-chart-sdk-docs.vercel.app/types/CustomChartContextProps.html](https://ts-chart-sdk-docs.vercel.app/types/CustomChartContextProps.html)
213
+ > * [https://github.com/thoughtspot/ts-chart-sdk/blob/main/src/main/custom-chart-context.ts#L40](https://github.com/thoughtspot/ts-chart-sdk/blob/main/src/main/custom-chart-context.ts#L40)
214
+
215
+ The custom chart context component must include the following mandatory properties to function:
216
+
217
+ * [`getDefaultChartConfig (Doc)`](#getDefaultChartConfig-(Doc))
218
+ * [`getQueriesFromChartConfig (Doc)`](#getQueriesFromChartConfig-(Doc))
219
+ * [`renderChart (Doc)`](#getQueriesFromChartConfig-(Doc))
220
+
221
+ #### getDefaultChartConfig (Doc)
222
+
223
+ This function takes in a [ChartModel](https://ts-chart-sdk-docs.vercel.app/interfaces/ChartModel.html) object and returns a well-formed point configuration definition.
224
+
225
+ The point for the Gantt chart used in this tutorial looks like this:
226
+
227
+ ```bash
228
+ // Project 1 - Project Name
229
+ {
230
+ name: 'Start prototype', // Task
231
+ start: Date.UTC(2014, 10, 18), // Start Date
232
+ end: Date.UTC(2014, 10, 25), // End Date
233
+ completed: {
234
+ amount: 0.25, // Completion
235
+ },
236
+ }
237
+ ```
238
+ The above data can be represented as a table in CSV format as shown here:
239
+
240
+ ```bash
241
+ Project Name, Task, Start Date, End Date, Completion
242
+ "Project 1", "Start prototype", "2014-10-18", "2014-10-25", 0.25
243
+ ```
244
+
245
+ To create a Highcharts version of the data set, the above-mentioned headers must be presented as columns from ThoughtSpot. The query on the ThoughtSpot Answer page should have all the above columns to plot a Gantt chart.
246
+
247
+ Ensure that the `getDefaultChartConfig` method is included in chartContext to define the configuration of the columns that are required to map the dataset into the chart. We assume that the order of the column is maintained in the chartModel.
248
+
249
+ To render the chart, the default configuration is required.
250
+
251
+ ```jsx
252
+ getDefaultChartConfig: (chartModel: ChartModel): ChartConfig[] => {
253
+ const columns = chartModel.columns;
254
+
255
+ // Here we assume that the columns are always coming in the
256
+ // following order.
257
+ // [Project Name, Task, Start Date, End Date, Completion]
258
+
259
+ // TBD: do basic validation here to ensure that the chart is renderable
260
+ if (columns.length < 4) {
261
+ // not possible to plot a chart
262
+ return [];
263
+ }
264
+
265
+ const chartConfig: ChartConfig = {
266
+ key: 'default',
267
+ dimensions: [
268
+ {
269
+ key: 'project-name',
270
+ columns: [columns[0]],
271
+ },
272
+ {
273
+ key: 'task',
274
+ columns: [columns[1]],
275
+ },
276
+ {
277
+ key: 'start-date',
278
+ columns: [columns[2]],
279
+ },
280
+ {
281
+ key: 'end-date',
282
+ columns: [columns[3]],
283
+ },
284
+ {
285
+ key: 'completion',
286
+ columns: columns[4] ? [columns[4]] : [],
287
+ },
288
+ ],
289
+ };
290
+ return [chartConfig];
291
+ },
292
+ ```
293
+
294
+ <!--
295
+ > There is a better way to be able to configure the dimensions and columns in dimensions. But that is relatively a more advanced topic and not required for this demo. You can use the link below to look into the details of the same.
296
+ >
297
+
298
+ [TBD: Link to the Custom Chart Configuration Definition]
299
+ -->
300
+
301
+ #### getQueriesFromChartConfig (Doc)
302
+
303
+ This method defines the data query that is required to fetch the data from ThoughtSpot to render the chart. For most use cases, you do not require the data outside of the columns listed in your chart.
304
+
305
+ This example maps all the columns in the configuration as an array of columns in the arguments.
306
+
307
+ ```jsx
308
+ getQueriesFromChartConfig: (
309
+ chartConfig: ChartConfig[],
310
+ ): Array<Query> => {
311
+ // map all the columns in the config to the query array
312
+ return chartConfig.map(
313
+ (config: ChartConfig): Query =>
314
+ _.reduce(
315
+ config.dimensions,
316
+ (acc: Query, dimension) => ({
317
+ queryColumns: [
318
+ ...acc.queryColumns,
319
+ ...dimension.columns,
320
+ ],
321
+ }),
322
+ {
323
+ queryColumns: [],
324
+ } as Query,
325
+ ),
326
+ );
327
+ },
328
+ ```
329
+
330
+ #### renderChart (Doc)
331
+
332
+ This `renderChart (Doc)` function is required to render the chart implemented in your code. This function ensures that every time `chartContext` tries to re-render the chart due to the changes in data or chart model, the chart rendered in your application is updated.
333
+
334
+ > **Note**:
335
+ You can control render and re-render by implementing more granular control for the updates on data, visual props, or chart model.
336
+ >
337
+ But we haven't yet implemented this. So let's proceed and implement this.
338
+
339
+ #### Implement the renderChart function
340
+
341
+ To implement renderChart, complete the following steps:
342
+
343
+ 1. Create a function and move all the Highchart code inside the `renderChart` function.
344
+
345
+ ```jsx
346
+ const renderChart = (ctx) => {
347
+ // THE CHART
348
+ Highcharts.ganttChart('container', {
349
+ //....
350
+ } as any);
351
+ return Promise.resolve();
352
+ };
353
+ ```
354
+
355
+ 2. Implement the `renderChart` function in `getChartContext` as shown in this example:
356
+
357
+ ```jsx
358
+ ...
359
+ renderChart: (context) => renderChart(context),
360
+ ...
361
+ ```
362
+ At this point, you will notice that the chart is gone on the link.
363
+
364
+ 3. Wait for the chart to connect to the TS application.
365
+ 4. Open the console log and check if you see the following logs:
366
+
367
+ ```jsx
368
+ [vite] connecting...
369
+ [vite] connected.
370
+ Chart Context: initialization start
371
+ ```
372
+ 5. Run your application and test the chart implementation in the Playground.
373
+
374
+ #### Run the chart in the Playground
375
+
376
+ To run the chart and test your implementation, you need a Playground.
377
+
378
+ 1. Open the following link to test your implementation with predefined data sets:
379
+
380
+ [https://ts-chart-playground.vercel.app/](https://ts-chart-playground.vercel.app/)
381
+
382
+ > **NOTE**
383
+ >
384
+ > You can check out the playground code on the GitHub repository to your local environment and modify the data set to test your charts effectively.
385
+
386
+ 2. Add the following details as shown in the following example:
387
+
388
+ * App Url : `<your localhost url with port>`
389
+ * Chart Model: `Gantt - 3 Attribute - 2 Date - 1 Measure`
390
+
391
+ ![Playground](assets/images/playground.png)
392
+
393
+ In the next step, you need to bring your data into the chart.
394
+
395
+ ## Create a Data Model from input data
396
+
397
+ The data model is unique to every chart. It defines how each point will be plotted on the chart.
398
+
399
+ For the Gantt chart, this tutorial uses the following format for data points:
400
+
401
+ ```jsx
402
+ {
403
+ name: 'Start prototype', // Task
404
+ start: Date.UTC(2014, 10, 18), // Start Date
405
+ end: Date.UTC(2014, 10, 25), // End Date
406
+ completed: {
407
+ amount: 0.25, // Completion
408
+ },
409
+ }
410
+ ```
411
+ However, the data retrieved from the chart model includes an array of column types as shown in this example:
412
+
413
+ ```jsx
414
+ // inside chart model
415
+ {
416
+ ...
417
+ "data": [
418
+ {
419
+ "data": [
420
+ {
421
+ "columnId": "79344559-4c71-45c6-be33-450316eab54d",
422
+ "columnDataType": "CHAR",
423
+ "dataValue": [
424
+ "Project 1",
425
+ "Project 1",
426
+ "Project 1",
427
+ "Project 1",
428
+ "Project 2",
429
+ "Project 2",
430
+ "Project 2",
431
+ "Project 2"
432
+ ]
433
+ },
434
+ {
435
+ "columnId": "bce26c20-1335-4357-9d3f-b15a6a27237d",
436
+ "columnDataType": "CHAR",
437
+ "dataValue": [
438
+ "Start prototype",
439
+ "Test prototype",
440
+ "Develop",
441
+ "Run acceptance test",
442
+ "Start prototype",
443
+ "Test prototype",
444
+ "Develop",
445
+ "Run acceptance test"
446
+ ]
447
+ },
448
+ {
449
+ "columnId": "cdd0329d-9ae6-41d2-b036-aa565eb18bc9",
450
+ "columnDataType": "CHAR",
451
+ "dataValue": [
452
+ 1413570600000,
453
+ 1414348200000,
454
+ 1413743400000,
455
+ 1414002600000,
456
+ 1413570600000,
457
+ 1414348200000,
458
+ 1413743400000,
459
+ 1414002600000
460
+ ]
461
+ },
462
+ {
463
+ "columnId": "59a5893c-f487-46f6-ab08-8cd7672c283d",
464
+ "columnDataType": "CHAR",
465
+ "dataValue": [
466
+ 1414175400000,
467
+ 1414521000000,
468
+ 1414175400000,
469
+ 1414261800000,
470
+ 1414175400000,
471
+ 1414521000000,
472
+ 1414175400000,
473
+ 1414261800000
474
+ ]
475
+ },
476
+ {
477
+ "columnId": "69a5893c-f487-46f6-ab08-8cd7672c283d",
478
+ "columnDataType": "FLOAT",
479
+ "dataValue": [
480
+ 0.25,
481
+ null,
482
+ null,
483
+ null,
484
+ 0.4,
485
+ null,
486
+ null,
487
+ null
488
+ ]
489
+ },
490
+ {
491
+ "columnId": "79a5893c-f487-46f6-ab08-8cd7672c283d",
492
+ "columnDataType": "CHAR",
493
+ "dataValue": [
494
+ null,
495
+ "Develop",
496
+ "Start prototype",
497
+ "Test prototype",
498
+ null,
499
+ "Develop",
500
+ "Start prototype",
501
+ "Test prototype"
502
+ ]
503
+ }
504
+ ]
505
+ }
506
+ ],
507
+ ...
508
+ }
509
+ ```
510
+
511
+ You can transform the above structure to the point and series format that Highcharts can understand and interpret. Use the following code snippet to create a data model and plug data values into the chart.
512
+
513
+ ```jsx
514
+ const getDataModel = (chartModel: any) => {
515
+ const dataArr = chartModel.data[0].data;
516
+
517
+ // create point from data
518
+ const points = dataArr[0].dataValue.map((_val: string, idx: number) => {
519
+ return {
520
+ id: `${dataArr[0].dataValue[idx]} ${dataArr[1].dataValue[idx]}`,
521
+ parent: dataArr[0].dataValue[idx],
522
+ name: dataArr[1].dataValue[idx],
523
+ start: new Date(dataArr[2].dataValue[idx]).getTime(),
524
+ end: new Date(dataArr[3].dataValue[idx]).getTime(),
525
+ completed: {
526
+ amount: dataArr[4].dataValue[idx],
527
+ },
528
+ dependency: `${dataArr[0].dataValue[idx]} ${dataArr[5].dataValue[idx]}`,
529
+ };
530
+ });
531
+
532
+ // create projects from points & data
533
+ const projects = _.uniq(dataArr[0].dataValue);
534
+ const dataSeries = projects.map((project) => {
535
+ const filteredPoints = points.filter(
536
+ (point: any) => point.parent === project,
537
+ );
538
+ return {
539
+ name: project,
540
+ data: [
541
+ ...filteredPoints,
542
+ {
543
+ id: project,
544
+ name: project,
545
+ },
546
+ ],
547
+ };
548
+ });
549
+
550
+ // get max and min date
551
+ const maxDate = _.max([...dataArr[2].dataValue, ...dataArr[2].dataValue]);
552
+ const minDate = _.min([...dataArr[2].dataValue, ...dataArr[2].dataValue]);
553
+
554
+ return {
555
+ dataSeries,
556
+ maxDate,
557
+ minDate,
558
+ };
559
+ };
560
+ ```
561
+
562
+ ## Plug data into the Highcharts datasets
563
+
564
+ Use the data model created from the above function and plug the values into the Highchart configuration to render the chart.
565
+
566
+ 1. Create a data model object.
567
+ In your `renderChart` code, add the following line:
568
+
569
+ ```jsx
570
+ const dataModel = getDataModel(chartModel);
571
+ ```
572
+ 2. Replace X Axis min and max values.
573
+
574
+ ```jsx
575
+ xAxis: {
576
+ min: dataModel.minDate,
577
+ max: dataModel.maxDate,
578
+ },
579
+ ```
580
+ 3. Replace the series.
581
+
582
+ ```jsx
583
+ series: dataModel.dataSeries,
584
+ ```
585
+
586
+ The chart implemented in your code now shows up on the playground with the data values you just plugged in.
587
+
588
+ The following example shows the entire chart code for the Gantt chart implementation described in this tutorial:
589
+
590
+ ```jsx
591
+ /* eslint-disable simple-import-sort/imports */
592
+ import {
593
+ ChartConfig,
594
+ ChartModel,
595
+ ColumnType,
596
+ Query,
597
+ getChartContext,
598
+ } from '@thoughtspot/ts-chart-sdk';
599
+ import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
600
+ import 'highcharts/es-modules/masters/modules/gantt.src';
601
+ import _ from 'lodash';
602
+
603
+ const getDataModel = (chartModel: any) => {
604
+ const dataArr = chartModel.data[0].data;
605
+
606
+ // create point from data
607
+ const points = dataArr[0].dataValue.map((_val: string, idx: number) => {
608
+ return {
609
+ id: `${dataArr[0].dataValue[idx]} ${dataArr[1].dataValue[idx]}`,
610
+ parent: dataArr[0].dataValue[idx],
611
+ name: dataArr[1].dataValue[idx],
612
+ start: new Date(dataArr[2].dataValue[idx]).getTime(),
613
+ end: new Date(dataArr[3].dataValue[idx]).getTime(),
614
+ completed: {
615
+ amount: dataArr[4].dataValue[idx],
616
+ },
617
+ dependency: `${dataArr[0].dataValue[idx]} ${dataArr[5].dataValue[idx]}`,
618
+ };
619
+ });
620
+
621
+ // create projects from points & data
622
+ const projects = _.uniq(dataArr[0].dataValue);
623
+ const dataSeries = projects.map((project) => {
624
+ const filteredPoints = points.filter(
625
+ (point: any) => point.parent === project,
626
+ );
627
+ return {
628
+ name: project,
629
+ data: [
630
+ ...filteredPoints,
631
+ {
632
+ id: project,
633
+ name: project,
634
+ },
635
+ ],
636
+ };
637
+ });
638
+
639
+ // get max and min date
640
+ const maxDate = _.max([...dataArr[2].dataValue, ...dataArr[2].dataValue]);
641
+ const minDate = _.min([...dataArr[2].dataValue, ...dataArr[2].dataValue]);
642
+
643
+ return {
644
+ dataSeries,
645
+ maxDate,
646
+ minDate,
647
+ };
648
+ };
649
+
650
+ const renderChart = (ctx: any) => {
651
+ const chartModel = ctx.getChartModel();
652
+ console.log('chartModel:', chartModel);
653
+ console.log('data:', chartModel.data);
654
+
655
+ const dataModel = getDataModel(chartModel);
656
+
657
+ console.log('dataModel:', dataModel);
658
+
659
+ // THE CHART
660
+ Highcharts.ganttChart('container', {
661
+ title: {
662
+ text: 'Gantt Chart with Progress Indicators',
663
+ align: 'left',
664
+ },
665
+
666
+ xAxis: {
667
+ min: dataModel.minDate,
668
+ max: dataModel.maxDate,
669
+ },
670
+
671
+ accessibility: {
672
+ point: {
673
+ descriptionFormat:
674
+ '{yCategory}. ' +
675
+ '{#if completed}Task {(multiply completed.amount 100):.1f}% completed. {/if}' +
676
+ 'Start {x:%Y-%m-%d}, end {x2:%Y-%m-%d}.',
677
+ },
678
+ },
679
+
680
+ lang: {
681
+ accessibility: {
682
+ axis: {
683
+ xAxisDescriptionPlural:
684
+ 'The chart has a two-part X axis showing time in both week numbers and days.',
685
+ },
686
+ },
687
+ },
688
+
689
+ series: dataModel.dataSeries,
690
+ } as any);
691
+ return Promise.resolve();
692
+ };
693
+
694
+ const init = async () => {
695
+ const ctx = await getChartContext({
696
+ getDefaultChartConfig: (chartModel: ChartModel): ChartConfig[] => {
697
+ const columns = chartModel.columns;
698
+
699
+ // Here we assume that the columns are always coming in the
700
+ // following order.
701
+ // [Project Name, Task, Start Date, End Date, Completion]
702
+
703
+ // TBD: do basic validation here to ensure that the chart is renderable
704
+ if (columns.length < 4) {
705
+ // not possible to plot a chart
706
+ return [];
707
+ }
708
+
709
+ const chartConfig: ChartConfig = {
710
+ key: 'default',
711
+ dimensions: [
712
+ {
713
+ key: 'project-name',
714
+ columns: [columns[0]],
715
+ },
716
+ {
717
+ key: 'task',
718
+ columns: [columns[1]],
719
+ },
720
+ {
721
+ key: 'start-date',
722
+ columns: [columns[2]],
723
+ },
724
+ {
725
+ key: 'end-date',
726
+ columns: [columns[3]],
727
+ },
728
+ {
729
+ key: 'completion',
730
+ columns: columns[4] ? [columns[4]] : [],
731
+ },
732
+ ],
733
+ };
734
+ return [chartConfig];
735
+ },
736
+ getQueriesFromChartConfig: (
737
+ chartConfig: ChartConfig[],
738
+ ): Array<Query> => {
739
+ // map all the columns in the config to the query array
740
+ return chartConfig.map(
741
+ (config: ChartConfig): Query =>
742
+ _.reduce(
743
+ config.dimensions,
744
+ (acc: Query, dimension) => ({
745
+ queryColumns: [
746
+ ...acc.queryColumns,
747
+ ...dimension.columns,
748
+ ],
749
+ }),
750
+ {
751
+ queryColumns: [],
752
+ } as Query,
753
+ ),
754
+ );
755
+ },
756
+ renderChart: (context) => renderChart(context),
757
+ });
758
+ };
759
+
760
+ init();
761
+ ```
762
+
763
+ ## Deploy your chart
764
+
765
+ If the chart creation is successful, you can host it on a server and make it available for use:
766
+
767
+ To deploy your charts, you can use Vercel, Netlify, or any server that can render an HTML page. For information, see <a href="https://vercel.com/docs/frameworks/vite" target="_blank">Vite documentation</a>.
768
+
769
+ To deploy the chart on a test domain in Vercel, install <a href="https://vercel.com/docs/cli" target="_blank">Vercel CLI</a> and run the following command:
770
+
771
+ ```jsx
772
+ vercel
773
+ ```
774
+ For more information about Vercel deployment, see <a href="https://vercel.com/docs/getting-started-with-vercel" target="_blank">Vercel documentation</a>.
775
+
776
+ ### Content Security Policy Overrides
777
+
778
+ To allow the use of Vercel application content in Thoughtspot, add the Vercel domain URL to the CSP allow-list. For more information, see the <a href="https://developers.thoughtspot.com/docs/?pageid=security-settings" target="_blank">Security settings section in ThoughtSpot documentation</a>.
779
+
780
+ <br/>
781
+
782
+ ThoughtSpot Chart SDK, © ThoughtSpot, Inc. 2023