@thoughtspot/ts-chart-sdk 0.0.1-alpha.2 → 0.0.1-alpha.5
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.
- package/cjs/index.d.ts +1 -0
- package/cjs/index.d.ts.map +1 -1
- package/cjs/index.js +1 -0
- package/cjs/index.js.map +1 -1
- package/cjs/main/custom-chart-context.d.ts +1 -0
- package/cjs/main/custom-chart-context.d.ts.map +1 -1
- package/cjs/main/custom-chart-context.js +8 -0
- package/cjs/main/custom-chart-context.js.map +1 -1
- package/cjs/main/custom-chart-context.spec.js +69 -0
- package/cjs/main/custom-chart-context.spec.js.map +1 -1
- package/cjs/react/mocks/custom-chart-context-mock.d.ts +3 -0
- package/cjs/react/mocks/custom-chart-context-mock.d.ts.map +1 -0
- package/cjs/react/mocks/custom-chart-context-mock.js +100 -0
- package/cjs/react/mocks/custom-chart-context-mock.js.map +1 -0
- package/cjs/react/use-custom-chart-context.d.ts +4 -0
- package/cjs/react/use-custom-chart-context.d.ts.map +1 -0
- package/cjs/react/use-custom-chart-context.js +95 -0
- package/cjs/react/use-custom-chart-context.js.map +1 -0
- package/cjs/react/use-custom-chart-context.spec.d.ts +2 -0
- package/cjs/react/use-custom-chart-context.spec.d.ts.map +1 -0
- package/cjs/react/use-custom-chart-context.spec.js +391 -0
- package/cjs/react/use-custom-chart-context.spec.js.map +1 -0
- package/cjs/react/use-custom-chart-context.util.d.ts +6 -0
- package/cjs/react/use-custom-chart-context.util.d.ts.map +1 -0
- package/cjs/react/use-custom-chart-context.util.js +60 -0
- package/cjs/react/use-custom-chart-context.util.js.map +1 -0
- package/cjs/react/use-custom-chart-types.d.ts +23 -0
- package/cjs/react/use-custom-chart-types.d.ts.map +1 -0
- package/cjs/react/use-custom-chart-types.js +3 -0
- package/cjs/react/use-custom-chart-types.js.map +1 -0
- package/cjs/types/chart-to-ts-event.types.d.ts +12 -4
- package/cjs/types/chart-to-ts-event.types.d.ts.map +1 -1
- package/cjs/types/chart-to-ts-event.types.js +2 -0
- package/cjs/types/chart-to-ts-event.types.js.map +1 -1
- package/cjs/types/visual-prop.types.d.ts +12 -0
- package/cjs/types/visual-prop.types.d.ts.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/main/custom-chart-context.d.ts +1 -0
- package/lib/main/custom-chart-context.d.ts.map +1 -1
- package/lib/main/custom-chart-context.js +8 -0
- package/lib/main/custom-chart-context.js.map +1 -1
- package/lib/main/custom-chart-context.spec.js +69 -0
- package/lib/main/custom-chart-context.spec.js.map +1 -1
- package/lib/react/mocks/custom-chart-context-mock.d.ts +3 -0
- package/lib/react/mocks/custom-chart-context-mock.d.ts.map +1 -0
- package/lib/react/mocks/custom-chart-context-mock.js +94 -0
- package/lib/react/mocks/custom-chart-context-mock.js.map +1 -0
- package/lib/react/use-custom-chart-context.d.ts +4 -0
- package/lib/react/use-custom-chart-context.d.ts.map +1 -0
- package/lib/react/use-custom-chart-context.js +68 -0
- package/lib/react/use-custom-chart-context.js.map +1 -0
- package/lib/react/use-custom-chart-context.spec.d.ts +2 -0
- package/lib/react/use-custom-chart-context.spec.d.ts.map +1 -0
- package/lib/react/use-custom-chart-context.spec.js +363 -0
- package/lib/react/use-custom-chart-context.spec.js.map +1 -0
- package/lib/react/use-custom-chart-context.util.d.ts +6 -0
- package/lib/react/use-custom-chart-context.util.d.ts.map +1 -0
- package/lib/react/use-custom-chart-context.util.js +51 -0
- package/lib/react/use-custom-chart-context.util.js.map +1 -0
- package/lib/react/use-custom-chart-types.d.ts +23 -0
- package/lib/react/use-custom-chart-types.d.ts.map +1 -0
- package/lib/react/use-custom-chart-types.js +2 -0
- package/lib/react/use-custom-chart-types.js.map +1 -0
- package/lib/types/chart-to-ts-event.types.d.ts +12 -4
- package/lib/types/chart-to-ts-event.types.d.ts.map +1 -1
- package/lib/types/chart-to-ts-event.types.js +2 -0
- package/lib/types/chart-to-ts-event.types.js.map +1 -1
- package/lib/types/visual-prop.types.d.ts +12 -0
- package/lib/types/visual-prop.types.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/index.ts +1 -0
- package/src/main/custom-chart-context.spec.ts +116 -0
- package/src/main/custom-chart-context.ts +15 -0
- package/src/react/mocks/custom-chart-context-mock.ts +114 -0
- package/src/react/use-custom-chart-context.spec.tsx +464 -0
- package/src/react/use-custom-chart-context.tsx +180 -0
- package/src/react/use-custom-chart-context.util.ts +101 -0
- package/src/react/use-custom-chart-types.ts +70 -0
- package/src/types/chart-to-ts-event.types.ts +47 -2
- package/src/types/visual-prop.types.ts +68 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { render, waitFor } from '@testing-library/react';
|
|
2
|
+
import { renderHook } from '@testing-library/react-hooks';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import * as PostMessageEventBridge from '../main/post-message-event-bridge';
|
|
5
|
+
import { mockInitializeContextPayload } from '../test/test-utils';
|
|
6
|
+
import { ChartToTSEvent } from '../types/chart-to-ts-event.types';
|
|
7
|
+
import { TSToChartEvent } from '../types/ts-to-chart-event.types';
|
|
8
|
+
import { contextChartProps } from './mocks/custom-chart-context-mock';
|
|
9
|
+
import { useChartContext } from './use-custom-chart-context';
|
|
10
|
+
|
|
11
|
+
jest.mock('../main/post-message-event-bridge');
|
|
12
|
+
|
|
13
|
+
describe('useChartContext initialization', () => {
|
|
14
|
+
let eventProcessor: any;
|
|
15
|
+
let mockInitMessage;
|
|
16
|
+
let mockPostMessageToHost;
|
|
17
|
+
const mockedChartModel = { columns: [], config: {} };
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
mockInitMessage = jest.spyOn(
|
|
20
|
+
PostMessageEventBridge,
|
|
21
|
+
'initMessageListener',
|
|
22
|
+
);
|
|
23
|
+
mockPostMessageToHost = jest.spyOn(
|
|
24
|
+
PostMessageEventBridge,
|
|
25
|
+
'postMessageToHostApp',
|
|
26
|
+
);
|
|
27
|
+
mockInitMessage.mockImplementation((fn: any) => {
|
|
28
|
+
eventProcessor = fn;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
mockPostMessageToHost.mockImplementation(() => {
|
|
32
|
+
return global.Promise.resolve();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
// Clear mock implementations after each test
|
|
37
|
+
jest.clearAllMocks();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should initialize the context only after intialize completes', async () => {
|
|
41
|
+
// Render the hook with the custom chart context props
|
|
42
|
+
const { result, waitFor } = renderHook(() =>
|
|
43
|
+
useChartContext(contextChartProps),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Assert that the context is not initialized initially
|
|
47
|
+
expect(result.current.hasInitialized).toBe(false);
|
|
48
|
+
expect(result.current.chartModel).toBeUndefined();
|
|
49
|
+
const mockPostMessage = jest.fn();
|
|
50
|
+
eventProcessor({
|
|
51
|
+
data: {
|
|
52
|
+
payload: {
|
|
53
|
+
componentId: 'COMPONENT_ID',
|
|
54
|
+
hostUrl: 'https://some.chart.app',
|
|
55
|
+
chartModel: mockedChartModel,
|
|
56
|
+
},
|
|
57
|
+
eventType: TSToChartEvent.Initialize,
|
|
58
|
+
source: 'ts-host-app',
|
|
59
|
+
},
|
|
60
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
eventProcessor({
|
|
64
|
+
data: {
|
|
65
|
+
payload: {},
|
|
66
|
+
eventType: TSToChartEvent.InitializeComplete,
|
|
67
|
+
source: 'ts-host-app',
|
|
68
|
+
},
|
|
69
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
70
|
+
});
|
|
71
|
+
await waitFor(() => {
|
|
72
|
+
expect(result.current.hasInitialized).toBe(true);
|
|
73
|
+
expect(result.current.chartModel).toEqual(mockedChartModel);
|
|
74
|
+
});
|
|
75
|
+
result.current?.destroy();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('should make sure hasInitialized to remain false when context initialization failed', async () => {
|
|
79
|
+
jest.mock('../main/custom-chart-context', () => ({
|
|
80
|
+
CustomChartContext: jest.fn().mockImplementation(() => ({
|
|
81
|
+
initialize: jest
|
|
82
|
+
.fn()
|
|
83
|
+
.mockRejectedValue(new Error('Initialization failed')),
|
|
84
|
+
emitEvent: jest.fn(),
|
|
85
|
+
on: jest.fn(),
|
|
86
|
+
getChartModel: jest.fn(),
|
|
87
|
+
renderChart: jest.fn(),
|
|
88
|
+
destroy: jest.fn(),
|
|
89
|
+
})),
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
// Render the hook with the custom chart context props
|
|
93
|
+
const { result, waitFor } = renderHook(() =>
|
|
94
|
+
useChartContext({
|
|
95
|
+
...contextChartProps,
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
98
|
+
await waitFor(() => {
|
|
99
|
+
expect(result.current.hasInitialized).toBe(false);
|
|
100
|
+
expect(result.current.chartModel).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('useChartContext emit', () => {
|
|
106
|
+
let eventProcessor: any;
|
|
107
|
+
let mockInitMessage;
|
|
108
|
+
let mockPostMessageToHost: any;
|
|
109
|
+
beforeEach(() => {
|
|
110
|
+
mockInitMessage = jest.spyOn(
|
|
111
|
+
PostMessageEventBridge,
|
|
112
|
+
'initMessageListener',
|
|
113
|
+
);
|
|
114
|
+
mockPostMessageToHost = jest.spyOn(
|
|
115
|
+
PostMessageEventBridge,
|
|
116
|
+
'postMessageToHostApp',
|
|
117
|
+
);
|
|
118
|
+
mockInitMessage.mockImplementation((fn: any) => {
|
|
119
|
+
eventProcessor = fn;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
mockPostMessageToHost.mockImplementation(() => {
|
|
123
|
+
return global.Promise.resolve();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
afterEach(() => {
|
|
127
|
+
// Clear mock implementations after each test
|
|
128
|
+
jest.clearAllMocks();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should trigger the emitter correctly when context is initialized', async () => {
|
|
132
|
+
// Render the hook with the custom chart context props
|
|
133
|
+
const { result, waitFor } = renderHook(() =>
|
|
134
|
+
useChartContext(contextChartProps),
|
|
135
|
+
);
|
|
136
|
+
const mockPostMessage = jest.fn();
|
|
137
|
+
eventProcessor({
|
|
138
|
+
data: {
|
|
139
|
+
payload: mockInitializeContextPayload,
|
|
140
|
+
eventType: TSToChartEvent.Initialize,
|
|
141
|
+
source: 'ts-host-app',
|
|
142
|
+
},
|
|
143
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
eventProcessor({
|
|
147
|
+
data: {
|
|
148
|
+
payload: {},
|
|
149
|
+
eventType: TSToChartEvent.InitializeComplete,
|
|
150
|
+
source: 'ts-host-app',
|
|
151
|
+
},
|
|
152
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
153
|
+
});
|
|
154
|
+
await waitFor(() => {
|
|
155
|
+
expect(result.current.hasInitialized).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
const contextPayload = {
|
|
158
|
+
clickedPoint: { tuple: [{ columnId: '123', value: 12 }] },
|
|
159
|
+
event: {} as any,
|
|
160
|
+
};
|
|
161
|
+
result.current.emitOpenContextMenu(contextPayload);
|
|
162
|
+
expect(mockPostMessageToHost).toHaveBeenCalledWith(
|
|
163
|
+
mockInitializeContextPayload.componentId,
|
|
164
|
+
mockInitializeContextPayload.hostUrl,
|
|
165
|
+
contextPayload,
|
|
166
|
+
ChartToTSEvent.OpenContextMenu,
|
|
167
|
+
);
|
|
168
|
+
result.current?.destroy();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('should not defined emitter when context is not initialized', async () => {
|
|
172
|
+
// Render the hook with the custom chart context props
|
|
173
|
+
const { result, waitFor } = renderHook(() =>
|
|
174
|
+
useChartContext(contextChartProps),
|
|
175
|
+
);
|
|
176
|
+
const mockPostMessage = jest.fn();
|
|
177
|
+
eventProcessor({
|
|
178
|
+
data: {
|
|
179
|
+
payload: mockInitializeContextPayload,
|
|
180
|
+
eventType: TSToChartEvent.Initialize,
|
|
181
|
+
source: 'ts-host-app',
|
|
182
|
+
},
|
|
183
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
184
|
+
});
|
|
185
|
+
await waitFor(() => {
|
|
186
|
+
expect(result.current.hasInitialized).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
const promise = result.current.emitRenderStart();
|
|
189
|
+
await expect(promise).rejects.toThrow('Context not initialized');
|
|
190
|
+
expect(mockPostMessageToHost).not.toHaveBeenCalled();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('useChartContext setOn listeners', () => {
|
|
195
|
+
let eventProcessor: any;
|
|
196
|
+
let mockInitMessage;
|
|
197
|
+
let mockPostMessageToHost;
|
|
198
|
+
const mockedChartModel = { columns: [], config: {} };
|
|
199
|
+
beforeEach(() => {
|
|
200
|
+
mockInitMessage = jest.spyOn(
|
|
201
|
+
PostMessageEventBridge,
|
|
202
|
+
'initMessageListener',
|
|
203
|
+
);
|
|
204
|
+
mockPostMessageToHost = jest.spyOn(
|
|
205
|
+
PostMessageEventBridge,
|
|
206
|
+
'postMessageToHostApp',
|
|
207
|
+
);
|
|
208
|
+
mockInitMessage.mockImplementation((fn: any) => {
|
|
209
|
+
eventProcessor = fn;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
mockPostMessageToHost.mockImplementation(() => {
|
|
213
|
+
return global.Promise.resolve();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
afterEach(() => {
|
|
217
|
+
// Clear mock implementations after each test
|
|
218
|
+
jest.clearAllMocks();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('should trigger the setOnEvent correctly when context is initialized', async () => {
|
|
222
|
+
// Render the hook with the custom chart context props
|
|
223
|
+
const { result, waitFor } = renderHook(() =>
|
|
224
|
+
useChartContext(contextChartProps),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// Assert that the context is not initialized initially
|
|
228
|
+
expect(result.current.hasInitialized).toBe(false);
|
|
229
|
+
expect(result.current.chartModel).toBeUndefined();
|
|
230
|
+
const mockPostMessage = jest.fn();
|
|
231
|
+
eventProcessor({
|
|
232
|
+
data: {
|
|
233
|
+
payload: {
|
|
234
|
+
componentId: 'COMPONENT_ID',
|
|
235
|
+
hostUrl: 'https://some.chart.app',
|
|
236
|
+
chartModel: mockedChartModel,
|
|
237
|
+
},
|
|
238
|
+
eventType: TSToChartEvent.Initialize,
|
|
239
|
+
source: 'ts-host-app',
|
|
240
|
+
},
|
|
241
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
eventProcessor({
|
|
245
|
+
data: {
|
|
246
|
+
payload: {},
|
|
247
|
+
eventType: TSToChartEvent.InitializeComplete,
|
|
248
|
+
source: 'ts-host-app',
|
|
249
|
+
},
|
|
250
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
251
|
+
});
|
|
252
|
+
await waitFor(() => {
|
|
253
|
+
expect(result.current.hasInitialized).toBe(true);
|
|
254
|
+
expect(result.current.chartModel).toEqual(mockedChartModel);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
eventProcessor({
|
|
258
|
+
data: {
|
|
259
|
+
payload: { chartModel: {} },
|
|
260
|
+
eventType: TSToChartEvent.ChartModelUpdate,
|
|
261
|
+
source: 'ts-host-app',
|
|
262
|
+
},
|
|
263
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
264
|
+
});
|
|
265
|
+
await waitFor(() => {
|
|
266
|
+
expect(mockPostMessage).toHaveBeenCalledWith({
|
|
267
|
+
triggerRenderChart: true,
|
|
268
|
+
});
|
|
269
|
+
expect(result.current.chartModel).toEqual({});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
eventProcessor({
|
|
273
|
+
data: {
|
|
274
|
+
payload: { visualProps: { color: 'red' } },
|
|
275
|
+
eventType: TSToChartEvent.VisualPropsUpdate,
|
|
276
|
+
source: 'ts-host-app',
|
|
277
|
+
},
|
|
278
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
279
|
+
});
|
|
280
|
+
expect(mockPostMessage).toHaveBeenCalledWith({
|
|
281
|
+
triggerRenderChart: true,
|
|
282
|
+
});
|
|
283
|
+
await waitFor(() => {
|
|
284
|
+
expect(mockPostMessage).toHaveBeenCalledWith({
|
|
285
|
+
triggerRenderChart: true,
|
|
286
|
+
});
|
|
287
|
+
expect(result.current.chartModel?.visualProps).toEqual({
|
|
288
|
+
color: 'red',
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
result.current?.destroy();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test('should not defined setOnEvent when context is not initialized', async () => {
|
|
295
|
+
// Render the hook with the custom chart context props
|
|
296
|
+
const { result, waitFor } = renderHook(() =>
|
|
297
|
+
useChartContext(contextChartProps),
|
|
298
|
+
);
|
|
299
|
+
const mockPostMessage = jest.fn();
|
|
300
|
+
eventProcessor({
|
|
301
|
+
data: {
|
|
302
|
+
payload: mockInitializeContextPayload,
|
|
303
|
+
eventType: TSToChartEvent.Initialize,
|
|
304
|
+
source: 'ts-host-app',
|
|
305
|
+
},
|
|
306
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
307
|
+
});
|
|
308
|
+
await waitFor(() => {
|
|
309
|
+
expect(result.current.hasInitialized).toBe(false);
|
|
310
|
+
});
|
|
311
|
+
const promise = result.current.setOnVisualPropsUpdate((payload) => {
|
|
312
|
+
//
|
|
313
|
+
});
|
|
314
|
+
await expect(promise).rejects.toThrow('Context not initialized');
|
|
315
|
+
const offPromise = result.current.setOffVisualPropsUpdate();
|
|
316
|
+
await expect(offPromise).rejects.toThrow('Context not initialized');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('useChartContext on React Wrapper component', () => {
|
|
321
|
+
let eventProcessor: any;
|
|
322
|
+
let mockInitMessage;
|
|
323
|
+
let mockPostMessageToHost;
|
|
324
|
+
const mockedChartModel = { columns: [], config: {} };
|
|
325
|
+
beforeEach(() => {
|
|
326
|
+
mockInitMessage = jest.spyOn(
|
|
327
|
+
PostMessageEventBridge,
|
|
328
|
+
'initMessageListener',
|
|
329
|
+
);
|
|
330
|
+
mockPostMessageToHost = jest.spyOn(
|
|
331
|
+
PostMessageEventBridge,
|
|
332
|
+
'postMessageToHostApp',
|
|
333
|
+
);
|
|
334
|
+
mockInitMessage.mockImplementation((fn: any) => {
|
|
335
|
+
eventProcessor = fn;
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
mockPostMessageToHost.mockImplementation(() => {
|
|
339
|
+
return global.Promise.resolve();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
afterEach(() => {
|
|
343
|
+
// Clear mock implementations after each test
|
|
344
|
+
jest.clearAllMocks();
|
|
345
|
+
});
|
|
346
|
+
test('TSChartContext renders children and should not increase counter for useEffect on chartModel if visualProps is updated', async () => {
|
|
347
|
+
const CustomChartComponent = () => {
|
|
348
|
+
const [counter, setCounter] = React.useState(0);
|
|
349
|
+
const { TSChartContext, chartModel, hasInitialized } =
|
|
350
|
+
useChartContext(contextChartProps);
|
|
351
|
+
React.useEffect(() => {
|
|
352
|
+
setCounter((prevCount) => prevCount + 1);
|
|
353
|
+
}, [chartModel]);
|
|
354
|
+
return (
|
|
355
|
+
<TSChartContext>
|
|
356
|
+
<div data-testid="child-element">
|
|
357
|
+
Child Element counter: {counter}
|
|
358
|
+
{(chartModel?.visualProps as any)?.color ?? ''}
|
|
359
|
+
</div>
|
|
360
|
+
</TSChartContext>
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const { getByTestId, rerender } = render(<CustomChartComponent />);
|
|
365
|
+
|
|
366
|
+
const mockPostMessage = jest.fn();
|
|
367
|
+
eventProcessor({
|
|
368
|
+
data: {
|
|
369
|
+
payload: {
|
|
370
|
+
componentId: 'COMPONENT_ID',
|
|
371
|
+
hostUrl: 'https://some.chart.app',
|
|
372
|
+
chartModel: mockedChartModel,
|
|
373
|
+
},
|
|
374
|
+
eventType: TSToChartEvent.Initialize,
|
|
375
|
+
source: 'ts-host-app',
|
|
376
|
+
},
|
|
377
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
378
|
+
});
|
|
379
|
+
rerender(<CustomChartComponent />);
|
|
380
|
+
|
|
381
|
+
// Check if the child element is rendered
|
|
382
|
+
await waitFor(() => {
|
|
383
|
+
expect(getByTestId('child-element').textContent).toBe(
|
|
384
|
+
'Child Element counter: 1',
|
|
385
|
+
);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
eventProcessor({
|
|
389
|
+
data: {
|
|
390
|
+
payload: { visualProps: { color: 'red' } },
|
|
391
|
+
eventType: TSToChartEvent.VisualPropsUpdate,
|
|
392
|
+
source: 'ts-host-app',
|
|
393
|
+
},
|
|
394
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Re-render the component with a new key
|
|
398
|
+
// Check if the child element is still in the document after re-render
|
|
399
|
+
const updatedChildElement = getByTestId('child-element');
|
|
400
|
+
await waitFor(() => {
|
|
401
|
+
expect(updatedChildElement.textContent).toBe(
|
|
402
|
+
'Child Element counter: 1red',
|
|
403
|
+
);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('TSChartContext renders children should increase counter for useEffect on chartModel.visualProps if visualProps is updated', async () => {
|
|
408
|
+
const CustomChartComponent = () => {
|
|
409
|
+
const [counter, setCounter] = React.useState(0);
|
|
410
|
+
const { TSChartContext, chartModel, hasInitialized } =
|
|
411
|
+
useChartContext(contextChartProps);
|
|
412
|
+
React.useEffect(() => {
|
|
413
|
+
setCounter((prevCount) => prevCount + 1);
|
|
414
|
+
}, [chartModel?.visualProps]);
|
|
415
|
+
return (
|
|
416
|
+
<TSChartContext>
|
|
417
|
+
<div data-testid="child-element">
|
|
418
|
+
Child Element counter: {counter}
|
|
419
|
+
{(chartModel?.visualProps as any)?.color ?? ''}
|
|
420
|
+
</div>
|
|
421
|
+
</TSChartContext>
|
|
422
|
+
);
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const { getByTestId, rerender } = render(<CustomChartComponent />);
|
|
426
|
+
|
|
427
|
+
// Check if the child element is rendered
|
|
428
|
+
const childElement = getByTestId('child-element');
|
|
429
|
+
await waitFor(() => {
|
|
430
|
+
expect(childElement.textContent).toBe('Child Element counter: 1');
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// Re-render the component with a new key
|
|
434
|
+
const mockPostMessage = jest.fn();
|
|
435
|
+
eventProcessor({
|
|
436
|
+
data: {
|
|
437
|
+
payload: {
|
|
438
|
+
componentId: 'COMPONENT_ID',
|
|
439
|
+
hostUrl: 'https://some.chart.app',
|
|
440
|
+
chartModel: mockedChartModel,
|
|
441
|
+
},
|
|
442
|
+
eventType: TSToChartEvent.Initialize,
|
|
443
|
+
source: 'ts-host-app',
|
|
444
|
+
},
|
|
445
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
446
|
+
});
|
|
447
|
+
eventProcessor({
|
|
448
|
+
data: {
|
|
449
|
+
payload: { visualProps: { color: 'red' } },
|
|
450
|
+
eventType: TSToChartEvent.VisualPropsUpdate,
|
|
451
|
+
source: 'ts-host-app',
|
|
452
|
+
},
|
|
453
|
+
ports: [{ postMessage: mockPostMessage }],
|
|
454
|
+
});
|
|
455
|
+
rerender(<CustomChartComponent />);
|
|
456
|
+
// Check if the child element is still in the document after re-render
|
|
457
|
+
const updatedChildElement = getByTestId('child-element');
|
|
458
|
+
await waitFor(() => {
|
|
459
|
+
expect(updatedChildElement.textContent).toBe(
|
|
460
|
+
'Child Element counter: 2red',
|
|
461
|
+
);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Custom Chart Context Hook for React App
|
|
3
|
+
* @fileoverview
|
|
4
|
+
* @author Divyam Lamiyan <divyam.lamiyan@thoughtspot.com>
|
|
5
|
+
*
|
|
6
|
+
* Copyright: ThoughtSpot Inc. 2023
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import _ from 'lodash';
|
|
10
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
11
|
+
import {
|
|
12
|
+
CustomChartContext,
|
|
13
|
+
CustomChartContextProps,
|
|
14
|
+
} from '../main/custom-chart-context';
|
|
15
|
+
import { ChartModel } from '../types/common.types';
|
|
16
|
+
import {
|
|
17
|
+
ChartModelUpdateEventPayload,
|
|
18
|
+
DataUpdateEventPayload,
|
|
19
|
+
VisualPropsUpdateEventPayload,
|
|
20
|
+
} from '../types/ts-to-chart-event.types';
|
|
21
|
+
import {
|
|
22
|
+
emitter,
|
|
23
|
+
eventListener,
|
|
24
|
+
eventOffListener,
|
|
25
|
+
} from './use-custom-chart-context.util';
|
|
26
|
+
import {
|
|
27
|
+
ChartContextProps,
|
|
28
|
+
TSChartContextProps,
|
|
29
|
+
} from './use-custom-chart-types';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A custom hook to manage the Chart Context state and provide necessary
|
|
33
|
+
* chart-related functionality.
|
|
34
|
+
* @param {CustomChartContextProps} props - The custom chart context properties
|
|
35
|
+
* to initialize the chart context.
|
|
36
|
+
* @returns {ChartContextProps} The chart context values, including initialized state,
|
|
37
|
+
* chart model, emitter, event listener, and TSChartContext.
|
|
38
|
+
*/
|
|
39
|
+
export const useChartContext = (
|
|
40
|
+
props: Omit<CustomChartContextProps, 'renderChart'>,
|
|
41
|
+
): ChartContextProps => {
|
|
42
|
+
/**
|
|
43
|
+
* State to keep track of whether the context has been initialized.
|
|
44
|
+
* @type {boolean}
|
|
45
|
+
*/
|
|
46
|
+
const [hasInitialized, setHasInitialized] = useState(false);
|
|
47
|
+
/**
|
|
48
|
+
* State to manage the key used to re-render the chart.
|
|
49
|
+
* @type {number}
|
|
50
|
+
*/
|
|
51
|
+
const [key, setKey] = useState(0);
|
|
52
|
+
/**
|
|
53
|
+
* State to hold the custom chart context.
|
|
54
|
+
* @type {CustomChartContext}
|
|
55
|
+
*/
|
|
56
|
+
const [ctx, setContextState] = useState<CustomChartContext>(
|
|
57
|
+
{} as CustomChartContext,
|
|
58
|
+
);
|
|
59
|
+
/**
|
|
60
|
+
* State to manage the chart model.
|
|
61
|
+
* @type {ChartModel}
|
|
62
|
+
*/
|
|
63
|
+
const [chartModel, setChartModel] = useState<ChartModel>();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Retrieves the chart context values.
|
|
67
|
+
* @param {CustomChartContext} ctx - The custom chart context.
|
|
68
|
+
* @returns {ChartContextProps} The chart context values,
|
|
69
|
+
* including initialized state, chart model, emitter,
|
|
70
|
+
* event listener, and TSChartContext.
|
|
71
|
+
*/
|
|
72
|
+
const getChartContextValues = useCallback(
|
|
73
|
+
(ctx: CustomChartContext): ChartContextProps => {
|
|
74
|
+
return {
|
|
75
|
+
hasInitialized,
|
|
76
|
+
chartModel,
|
|
77
|
+
destroy: () => ctx?.destroy(),
|
|
78
|
+
...emitter(ctx),
|
|
79
|
+
...eventListener(ctx),
|
|
80
|
+
...eventOffListener(ctx),
|
|
81
|
+
TSChartContext: ({ children }: TSChartContextProps) => {
|
|
82
|
+
return (
|
|
83
|
+
<React.Fragment key={key}>{children}</React.Fragment>
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
[chartModel, key, hasInitialized, ctx],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Register all external event listeners and update chart model once called
|
|
93
|
+
*/
|
|
94
|
+
const setupEventListeners = useCallback((context: CustomChartContext) => {
|
|
95
|
+
const commonUpdateHandler = (
|
|
96
|
+
payload:
|
|
97
|
+
| ChartModelUpdateEventPayload
|
|
98
|
+
| VisualPropsUpdateEventPayload
|
|
99
|
+
| DataUpdateEventPayload,
|
|
100
|
+
) => {
|
|
101
|
+
setChartModel(context.getChartModel());
|
|
102
|
+
return {
|
|
103
|
+
triggerRenderChart: true,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Register all external event listeners here
|
|
108
|
+
getChartContextValues(context).setOnChartModelUpdate(
|
|
109
|
+
commonUpdateHandler,
|
|
110
|
+
);
|
|
111
|
+
getChartContextValues(context).setOnVisualPropsUpdate(
|
|
112
|
+
commonUpdateHandler,
|
|
113
|
+
);
|
|
114
|
+
getChartContextValues(context).setOnDataUpdate(commonUpdateHandler);
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Register all external event off listeners
|
|
119
|
+
*/
|
|
120
|
+
const setupEventOffListeners = useCallback(
|
|
121
|
+
(context: CustomChartContext) => {
|
|
122
|
+
// Register all external event off listeners here
|
|
123
|
+
getChartContextValues(context).setOffChartModelUpdate();
|
|
124
|
+
getChartContextValues(context).setOffDataUpdate();
|
|
125
|
+
getChartContextValues(context).setOffVisualPropsUpdate();
|
|
126
|
+
},
|
|
127
|
+
[],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Initializes the chart context provider.
|
|
132
|
+
* @param {CustomChartContext} context - The custom chart context to be initialized.
|
|
133
|
+
* @returns {Promise<boolean>} A promise that resolves to
|
|
134
|
+
* `true` after successful initialization.
|
|
135
|
+
*/
|
|
136
|
+
const initializeContext = async (context: CustomChartContext) => {
|
|
137
|
+
return context
|
|
138
|
+
.initialize()
|
|
139
|
+
.then(() => {
|
|
140
|
+
setHasInitialized(true);
|
|
141
|
+
setContextState(context);
|
|
142
|
+
setChartModel(context.getChartModel());
|
|
143
|
+
return true;
|
|
144
|
+
})
|
|
145
|
+
.catch((e) => {
|
|
146
|
+
console.log('Error in context initialization', e);
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Renders the chart by updating the `key` state to force re-rendering.
|
|
152
|
+
* @param {CustomChartContext} ctx - The custom chart context.
|
|
153
|
+
* @returns {Promise<void>} A promise that resolves after the
|
|
154
|
+
* chart has been rendered.
|
|
155
|
+
*/
|
|
156
|
+
const renderChart = useCallback(
|
|
157
|
+
(ctx: CustomChartContext) => {
|
|
158
|
+
setKey((prevKey: number) => prevKey + 1);
|
|
159
|
+
return Promise.resolve();
|
|
160
|
+
},
|
|
161
|
+
[key],
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
// Create a new custom chart context with the provided props and
|
|
166
|
+
// renderChart function.
|
|
167
|
+
const context = new CustomChartContext({
|
|
168
|
+
...props,
|
|
169
|
+
renderChart,
|
|
170
|
+
});
|
|
171
|
+
// Initialize the chart context provider.
|
|
172
|
+
initializeContext(context);
|
|
173
|
+
setupEventListeners(context);
|
|
174
|
+
return () => {
|
|
175
|
+
setupEventOffListeners(context);
|
|
176
|
+
};
|
|
177
|
+
}, []);
|
|
178
|
+
|
|
179
|
+
return getChartContextValues(ctx);
|
|
180
|
+
};
|