@finos/legend-query-builder 4.0.9 → 4.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  2. package/lib/components/QueryBuilderResultPanel.js +208 -67
  3. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  4. package/lib/index.css +2 -2
  5. package/lib/index.css.map +1 -1
  6. package/lib/package.json +4 -4
  7. package/lib/stores/QueryBuilderResultState.d.ts +19 -0
  8. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  9. package/lib/stores/QueryBuilderResultState.js +48 -1
  10. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  11. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts.map +1 -1
  12. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js.map +1 -1
  13. package/lib/stores/shared/LambdaParameterState.d.ts.map +1 -1
  14. package/lib/stores/shared/LambdaParameterState.js +1 -1
  15. package/lib/stores/shared/LambdaParameterState.js.map +1 -1
  16. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +5 -2
  17. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
  18. package/lib/stores/shared/ValueSpecificationEditorHelper.js +30 -40
  19. package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
  20. package/package.json +12 -12
  21. package/src/components/QueryBuilderResultPanel.tsx +357 -91
  22. package/src/stores/QueryBuilderResultState.ts +82 -0
  23. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts +1 -0
  24. package/src/stores/shared/LambdaParameterState.ts +1 -0
  25. package/src/stores/shared/ValueSpecificationEditorHelper.ts +80 -57
@@ -68,7 +68,7 @@ import {
68
68
  prettyDuration,
69
69
  filterByType,
70
70
  } from '@finos/legend-shared';
71
- import { type MutableRefObject, forwardRef, useRef, useState } from 'react';
71
+ import { forwardRef, useState } from 'react';
72
72
  import {
73
73
  QueryBuilderDerivationProjectionColumnState,
74
74
  QueryBuilderProjectionColumnState,
@@ -96,13 +96,22 @@ import { PARAMETER_SUBMIT_ACTION } from '../stores/shared/LambdaParameterState.j
96
96
  import { QUERY_BUILDER_TEST_ID } from '../__lib__/QueryBuilderTesting.js';
97
97
  import {
98
98
  DataGrid,
99
- type DataGridCellMouseOverEvent,
99
+ type DataGridCellRendererParams,
100
100
  } from '@finos/legend-lego/data-grid';
101
101
  import {
102
102
  CODE_EDITOR_LANGUAGE,
103
103
  CodeEditor,
104
104
  } from '@finos/legend-lego/code-editor';
105
105
  import { ExecutionPlanViewer } from './execution-plan/ExecutionPlanViewer.js';
106
+ import type {
107
+ QueryBuilderTDSResultCellCoordinate,
108
+ QueryBuilderResultState,
109
+ QueryBuilderTDSResultCellData,
110
+ } from '../stores/QueryBuilderResultState.js';
111
+ import {
112
+ QueryBuilderPostFilterOperator_IsEmpty,
113
+ QueryBuilderPostFilterOperator_IsNotEmpty,
114
+ } from '../stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js';
106
115
 
107
116
  export const tryToFormatSql = (sql: string): string => {
108
117
  try {
@@ -117,7 +126,7 @@ const QueryBuilderGridResultContextMenu = observer(
117
126
  forwardRef<
118
127
  HTMLDivElement,
119
128
  {
120
- event: MutableRefObject<DataGridCellMouseOverEvent | null>;
129
+ event: QueryBuilderTDSResultCellData | null;
121
130
  tdsState: QueryBuilderTDSState;
122
131
  }
123
132
  >(function QueryBuilderResultContextMenu(props, ref) {
@@ -125,20 +134,26 @@ const QueryBuilderGridResultContextMenu = observer(
125
134
  const applicationStore = useApplicationStore();
126
135
  const postFilterEqualOperator = new QueryBuilderPostFilterOperator_Equal();
127
136
  const postFilterInOperator = new QueryBuilderPostFilterOperator_In();
137
+ const postFilterEmptyOperator =
138
+ new QueryBuilderPostFilterOperator_IsEmpty();
139
+ const postFilterNotEmptyOperator =
140
+ new QueryBuilderPostFilterOperator_IsNotEmpty();
141
+
128
142
  const postFilterNotEqualOperator =
129
143
  new QueryBuilderPostFilterOperator_NotEqual();
130
144
  const postFilterNotInOperator = new QueryBuilderPostFilterOperator_NotIn();
131
145
  const postFilterState = tdsState.postFilterState;
132
- const projectionColumnState = tdsState.tdsColumns
133
- .filter((c) => c.columnName === event.current?.column.getColId())
146
+
147
+ const projectionColumnState = tdsState.projectionColumns
148
+ .filter((c) => c.columnName === event?.columnName)
134
149
  .concat(
135
150
  tdsState.aggregationState.columns
136
- .filter((c) => c.columnName === event.current?.column.getColId())
151
+ .filter((c) => c.columnName === event?.columnName)
137
152
  .map((ag) => ag.projectionColumnState),
138
153
  )[0];
139
-
140
154
  const getExistingPostFilterNode = (
141
155
  operators: QueryBuilderPostFilterOperator[],
156
+ projectionColumnName: string | undefined,
142
157
  ): QueryBuilderPostFilterTreeNodeData | undefined =>
143
158
  Array.from(postFilterState.nodes.values())
144
159
  .filter(
@@ -150,7 +165,8 @@ const QueryBuilderGridResultContextMenu = observer(
150
165
  .filter(
151
166
  (n) =>
152
167
  (n as QueryBuilderPostFilterTreeConditionNodeData).condition
153
- .columnState.columnName === projectionColumnState?.columnName &&
168
+ .columnState.columnName ===
169
+ (projectionColumnName ?? projectionColumnState?.columnName) &&
154
170
  operators
155
171
  .map((op) => op.getLabel())
156
172
  .includes(
@@ -162,8 +178,9 @@ const QueryBuilderGridResultContextMenu = observer(
162
178
 
163
179
  const updateFilterConditionValue = (
164
180
  conditionValue: InstanceValue,
181
+ cellData: QueryBuilderTDSResultCellData,
165
182
  ): void => {
166
- if (event.current?.value !== null) {
183
+ if (cellData.value) {
167
184
  instanceValue_setValue(
168
185
  conditionValue,
169
186
  conditionValue instanceof EnumValueInstanceValue
@@ -172,10 +189,10 @@ const QueryBuilderGridResultContextMenu = observer(
172
189
  (
173
190
  conditionValue.genericType?.ownerReference
174
191
  .value as Enumeration
175
- ).values.filter((v) => v.name === event.current?.value)[0],
192
+ ).values.filter((v) => v.name === cellData.value)[0],
176
193
  ),
177
194
  )
178
- : event.current?.value,
195
+ : cellData.value,
179
196
  0,
180
197
  tdsState.queryBuilderState.observerContext,
181
198
  );
@@ -184,15 +201,28 @@ const QueryBuilderGridResultContextMenu = observer(
184
201
 
185
202
  const generateNewPostFilterConditionNodeData = async (
186
203
  operator: QueryBuilderPostFilterOperator,
204
+ cellData: QueryBuilderTDSResultCellData,
187
205
  ): Promise<void> => {
188
- if (projectionColumnState) {
189
- try {
190
- const postFilterConditionState = new PostFilterConditionState(
206
+ let postFilterConditionState: PostFilterConditionState;
207
+ try {
208
+ const possibleProjectionColumnState = cellData.columnName
209
+ ? tdsState.projectionColumns
210
+ .filter((c) => c.columnName === cellData.columnName)
211
+ .concat(
212
+ tdsState.aggregationState.columns
213
+ .filter((c) => c.columnName === cellData.columnName)
214
+ .map((ag) => ag.projectionColumnState),
215
+ )[0]
216
+ : projectionColumnState;
217
+
218
+ if (possibleProjectionColumnState) {
219
+ postFilterConditionState = new PostFilterConditionState(
191
220
  postFilterState,
192
- projectionColumnState,
221
+ possibleProjectionColumnState,
193
222
  undefined,
194
223
  operator,
195
224
  );
225
+
196
226
  if (
197
227
  projectionColumnState instanceof
198
228
  QueryBuilderDerivationProjectionColumnState
@@ -201,13 +231,16 @@ const QueryBuilderGridResultContextMenu = observer(
201
231
  projectionColumnState.fetchDerivationLambdaReturnType(),
202
232
  );
203
233
  }
234
+
204
235
  const defaultFilterConditionValue =
205
236
  postFilterConditionState.operator.getDefaultFilterConditionValue(
206
237
  postFilterConditionState,
207
238
  );
239
+
208
240
  postFilterConditionState.setValue(defaultFilterConditionValue);
209
241
  updateFilterConditionValue(
210
242
  defaultFilterConditionValue as InstanceValue,
243
+ cellData,
211
244
  );
212
245
  postFilterState.addNodeFromNode(
213
246
  new QueryBuilderPostFilterTreeConditionNodeData(
@@ -216,41 +249,56 @@ const QueryBuilderGridResultContextMenu = observer(
216
249
  ),
217
250
  undefined,
218
251
  );
219
- } catch (error) {
220
- assertErrorThrown(error);
221
- applicationStore.notificationService.notifyWarning(error.message);
222
- return;
223
252
  }
253
+ } catch (error) {
254
+ assertErrorThrown(error);
255
+ applicationStore.notificationService.notifyWarning(error.message);
256
+ return;
224
257
  }
225
258
  };
226
259
 
227
260
  const updateExistingPostFilterConditionNodeData = (
228
261
  existingPostFilterNode: QueryBuilderPostFilterTreeNodeData,
229
262
  isFilterBy: boolean,
263
+ cellData: QueryBuilderTDSResultCellData,
264
+ operator: QueryBuilderPostFilterOperator,
230
265
  ): void => {
266
+ if (
267
+ operator === postFilterEmptyOperator ||
268
+ operator === postFilterNotEmptyOperator
269
+ ) {
270
+ const conditionState = (
271
+ existingPostFilterNode as QueryBuilderPostFilterTreeConditionNodeData
272
+ ).condition;
273
+ if (conditionState.operator.getLabel() !== operator.getLabel()) {
274
+ conditionState.changeOperator(
275
+ isFilterBy ? postFilterEmptyOperator : postFilterNotEmptyOperator,
276
+ );
277
+ }
278
+ return;
279
+ }
231
280
  const conditionState = (
232
281
  existingPostFilterNode as QueryBuilderPostFilterTreeConditionNodeData
233
282
  ).condition;
234
- if (
235
- conditionState.operator.getLabel() ===
236
- (isFilterBy
237
- ? postFilterEqualOperator
238
- : postFilterNotEqualOperator
239
- ).getLabel()
240
- ) {
283
+
284
+ if (conditionState.operator.getLabel() === operator.getLabel()) {
241
285
  const doesValueAlreadyExist =
242
286
  conditionState.value instanceof InstanceValue &&
243
287
  (conditionState.value instanceof EnumValueInstanceValue
244
288
  ? conditionState.value.values.map((ef) => ef.value.name)
245
289
  : conditionState.value.values
246
- ).includes(event.current?.value);
290
+ ).includes(cellData.value);
291
+
247
292
  if (!doesValueAlreadyExist) {
248
293
  const currentValueSpecificaton = conditionState.value;
249
294
  const newValueSpecification =
250
295
  conditionState.operator.getDefaultFilterConditionValue(
251
296
  conditionState,
252
297
  );
253
- updateFilterConditionValue(newValueSpecification as InstanceValue);
298
+ updateFilterConditionValue(
299
+ newValueSpecification as InstanceValue,
300
+ cellData,
301
+ );
254
302
  conditionState.changeOperator(
255
303
  isFilterBy ? postFilterInOperator : postFilterNotInOperator,
256
304
  );
@@ -271,12 +319,16 @@ const QueryBuilderGridResultContextMenu = observer(
271
319
  : (v as InstanceValue).values,
272
320
  )
273
321
  .flat()
274
- .includes(event.current?.value);
322
+ .includes(cellData.value ?? event?.value);
323
+
275
324
  if (!doesValueAlreadyExist) {
276
325
  const newValueSpecification = (
277
326
  isFilterBy ? postFilterEqualOperator : postFilterNotEqualOperator
278
327
  ).getDefaultFilterConditionValue(conditionState);
279
- updateFilterConditionValue(newValueSpecification as InstanceValue);
328
+ updateFilterConditionValue(
329
+ newValueSpecification as InstanceValue,
330
+ cellData,
331
+ );
280
332
  instanceValue_setValues(
281
333
  conditionState.value as InstanceValue,
282
334
  [
@@ -289,32 +341,76 @@ const QueryBuilderGridResultContextMenu = observer(
289
341
  }
290
342
  };
291
343
 
292
- const filterByOrOut = (isFilterBy: boolean): void => {
344
+ const getFilterOperator = (
345
+ isFilterBy: boolean,
346
+ cellData: QueryBuilderTDSResultCellData,
347
+ ): QueryBuilderPostFilterOperator => {
348
+ if (isFilterBy === true) {
349
+ if (cellData.value === null) {
350
+ return postFilterEmptyOperator;
351
+ } else {
352
+ return postFilterEqualOperator;
353
+ }
354
+ } else {
355
+ if (cellData.value === null) {
356
+ return postFilterNotEmptyOperator;
357
+ } else {
358
+ return postFilterNotEqualOperator;
359
+ }
360
+ }
361
+ };
362
+
363
+ const filterByOrOutValue = (
364
+ isFilterBy: boolean,
365
+ cellData: QueryBuilderTDSResultCellData,
366
+ ): void => {
293
367
  tdsState.setShowPostFilterPanel(true);
368
+
369
+ const operator = getFilterOperator(isFilterBy, cellData);
370
+
294
371
  const existingPostFilterNode = getExistingPostFilterNode(
295
- isFilterBy
372
+ cellData.value === null
373
+ ? [postFilterEmptyOperator, postFilterNotEmptyOperator]
374
+ : isFilterBy
296
375
  ? [postFilterEqualOperator, postFilterInOperator]
297
376
  : [postFilterNotEqualOperator, postFilterNotInOperator],
377
+ cellData.columnName,
298
378
  );
379
+
299
380
  existingPostFilterNode === undefined
300
- ? generateNewPostFilterConditionNodeData(
301
- isFilterBy ? postFilterEqualOperator : postFilterNotEqualOperator,
302
- ).catch(applicationStore.alertUnhandledError)
381
+ ? generateNewPostFilterConditionNodeData(operator, cellData).catch(
382
+ applicationStore.alertUnhandledError,
383
+ )
303
384
  : updateExistingPostFilterConditionNodeData(
304
385
  existingPostFilterNode,
305
386
  isFilterBy,
387
+ cellData,
388
+ operator,
306
389
  );
307
390
  };
308
391
 
392
+ const filterByOrOutValues = (isFilterBy: boolean): void => {
393
+ tdsState.queryBuilderState.resultState.selectedCells.forEach(
394
+ (cellData) => {
395
+ filterByOrOutValue(isFilterBy, cellData);
396
+ },
397
+ );
398
+ };
399
+
309
400
  const handleCopyCellValue = applicationStore.guardUnhandledError(() =>
310
401
  applicationStore.clipboardService.copyTextToClipboard(
311
- event.current?.value,
402
+ event?.value?.toString() ?? '',
312
403
  ),
313
404
  );
314
405
 
315
406
  const handleCopyRowValue = applicationStore.guardUnhandledError(() =>
316
407
  applicationStore.clipboardService.copyTextToClipboard(
317
- Object.values(event.current?.data).toString(),
408
+ tdsState.queryBuilderState.resultState
409
+ .findRowFromRowIndex(
410
+ tdsState.queryBuilderState.resultState.selectedCells[0]?.coordinates
411
+ .rowIndex ?? 0,
412
+ )
413
+ .toString(),
318
414
  ),
319
415
  );
320
416
 
@@ -323,7 +419,7 @@ const QueryBuilderGridResultContextMenu = observer(
323
419
  <MenuContentItem
324
420
  disabled={!projectionColumnState}
325
421
  onClick={(): void => {
326
- filterByOrOut(true);
422
+ filterByOrOutValues(true);
327
423
  }}
328
424
  >
329
425
  Filter By
@@ -331,7 +427,7 @@ const QueryBuilderGridResultContextMenu = observer(
331
427
  <MenuContentItem
332
428
  disabled={!projectionColumnState}
333
429
  onClick={(): void => {
334
- filterByOrOut(false);
430
+ filterByOrOutValues(false);
335
431
  }}
336
432
  >
337
433
  Filter Out
@@ -348,18 +444,201 @@ const QueryBuilderGridResultContextMenu = observer(
348
444
  }),
349
445
  );
350
446
 
447
+ type IQueryRendererParamsWithGridType = DataGridCellRendererParams & {
448
+ resultState: QueryBuilderResultState;
449
+ tdsExecutionResult: TDSExecutionResult;
450
+ };
451
+
452
+ const QueryResultCellRenderer = observer(
453
+ (params: IQueryRendererParamsWithGridType) => {
454
+ const resultState = params.resultState;
455
+ const tdsExecutionResult = params.tdsExecutionResult;
456
+
457
+ const cellValue = params.value as string;
458
+ const columnName = params.column?.getColId() ?? '';
459
+
460
+ const findCoordinatesFromResultValue = (
461
+ colId: string,
462
+ rowNumber: number,
463
+ ): QueryBuilderTDSResultCellCoordinate => {
464
+ const colIndex = tdsExecutionResult.result.columns.findIndex(
465
+ (col) => col === colId,
466
+ );
467
+
468
+ return { rowIndex: rowNumber, colIndex: colIndex };
469
+ };
470
+
471
+ const currentCellCoordinates = findCoordinatesFromResultValue(
472
+ columnName,
473
+ params.rowIndex,
474
+ );
475
+
476
+ const cellInFilteredResults = resultState.selectedCells.some(
477
+ (result) =>
478
+ result.coordinates.colIndex === currentCellCoordinates.colIndex &&
479
+ result.coordinates.rowIndex === currentCellCoordinates.rowIndex,
480
+ );
481
+
482
+ const mouseDown: React.MouseEventHandler = (event) => {
483
+ event.preventDefault();
484
+
485
+ if (event.shiftKey) {
486
+ const coordinates = findCoordinatesFromResultValue(
487
+ columnName,
488
+ params.rowIndex,
489
+ );
490
+ const actualValue = resultState.findResultValueFromCoordinates([
491
+ coordinates.rowIndex,
492
+ coordinates.colIndex,
493
+ ]);
494
+ resultState.addCellData({
495
+ value: actualValue,
496
+ columnName: columnName,
497
+ coordinates: coordinates,
498
+ });
499
+ return;
500
+ }
501
+
502
+ if (event.button === 0) {
503
+ resultState.setIsSelectingCells(true);
504
+ resultState.setSelectedCells([]);
505
+ const coordinates = findCoordinatesFromResultValue(
506
+ columnName,
507
+ params.rowIndex,
508
+ );
509
+ const actualValue = resultState.findResultValueFromCoordinates([
510
+ coordinates.rowIndex,
511
+ coordinates.colIndex,
512
+ ]);
513
+
514
+ const rowNode = params.api.getRowNode(params.rowIndex.toString());
515
+
516
+ if (rowNode) {
517
+ params.api.refreshCells({
518
+ force: true,
519
+ columns: [columnName],
520
+ rowNodes: [rowNode],
521
+ });
522
+ }
523
+ resultState.setSelectedCells([
524
+ {
525
+ value: actualValue,
526
+ columnName: columnName,
527
+ coordinates: coordinates,
528
+ },
529
+ ]);
530
+ }
531
+ };
532
+ const mouseUp: React.MouseEventHandler = (event) => {
533
+ resultState.setIsSelectingCells(false);
534
+ };
535
+
536
+ const mouseOver: React.MouseEventHandler = (event) => {
537
+ if (resultState.isSelectingCells) {
538
+ if (resultState.selectedCells.length < 1) {
539
+ return;
540
+ }
541
+ const results = resultState.selectedCells[0];
542
+ if (!results) {
543
+ return;
544
+ }
545
+
546
+ const firstCorner = results.coordinates;
547
+ const secondCorner = findCoordinatesFromResultValue(
548
+ columnName,
549
+ params.rowIndex,
550
+ );
551
+
552
+ resultState.setSelectedCells([results]);
553
+
554
+ const minRow = Math.min(firstCorner.rowIndex, secondCorner.rowIndex);
555
+ const minCol = Math.min(firstCorner.colIndex, secondCorner.colIndex);
556
+ const maxRow = Math.max(firstCorner.rowIndex, secondCorner.rowIndex);
557
+ const maxCol = Math.max(firstCorner.colIndex, secondCorner.colIndex);
558
+
559
+ for (let x = minRow; x <= maxRow; x++) {
560
+ for (let y = minCol; y <= maxCol; y++) {
561
+ const actualValue = resultState.findResultValueFromCoordinates([
562
+ x,
563
+ y,
564
+ ]);
565
+
566
+ const valueAndColumnId = {
567
+ value: actualValue,
568
+ columnName: resultState.findColumnFromCoordinates(y),
569
+ coordinates: {
570
+ rowIndex: x,
571
+ colIndex: y,
572
+ },
573
+ } as QueryBuilderTDSResultCellData;
574
+
575
+ if (
576
+ !resultState.selectedCells.find(
577
+ (result) =>
578
+ result.coordinates.colIndex === y &&
579
+ result.coordinates.rowIndex === x,
580
+ )
581
+ ) {
582
+ resultState.addCellData(valueAndColumnId);
583
+ }
584
+ }
585
+ }
586
+ }
587
+
588
+ resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
589
+ };
590
+
591
+ const fetchStructureImplementation =
592
+ resultState.queryBuilderState.fetchStructureState.implementation;
593
+
594
+ return (
595
+ <ContextMenu
596
+ content={
597
+ // NOTE: we only support this functionality for grid result with a projection fetch-structure
598
+ fetchStructureImplementation instanceof QueryBuilderTDSState ? (
599
+ <QueryBuilderGridResultContextMenu
600
+ event={resultState.mousedOverCell}
601
+ tdsState={fetchStructureImplementation}
602
+ />
603
+ ) : null
604
+ }
605
+ disabled={
606
+ !(
607
+ resultState.queryBuilderState.fetchStructureState
608
+ .implementation instanceof QueryBuilderTDSState
609
+ ) ||
610
+ !resultState.queryBuilderState.isQuerySupported ||
611
+ !resultState.mousedOverCell
612
+ }
613
+ menuProps={{ elevation: 7 }}
614
+ key={params.value as string}
615
+ className={clsx('ag-theme-balham-dark query-builder__result__tds-grid')}
616
+ >
617
+ <div
618
+ className={clsx('query-builder__result__values__table__cell', {
619
+ 'query-builder__result__values__table__cell--active':
620
+ cellInFilteredResults,
621
+ })}
622
+ onMouseDown={(event) => mouseDown(event)}
623
+ onMouseUp={(event) => mouseUp(event)}
624
+ onMouseOver={(event) => mouseOver(event)}
625
+ >
626
+ <span>{cellValue}</span>
627
+ </div>
628
+ </ContextMenu>
629
+ );
630
+ },
631
+ );
632
+
351
633
  const QueryBuilderGridResult = observer(
352
634
  (props: {
353
635
  executionResult: TDSExecutionResult;
354
636
  queryBuilderState: QueryBuilderState;
355
637
  }) => {
356
638
  const { executionResult, queryBuilderState } = props;
357
- const fetchStructureImplementation =
358
- queryBuilderState.fetchStructureState.implementation;
359
- const cellDoubleClickedEvent = useRef<DataGridCellMouseOverEvent | null>(
360
- null,
361
- );
362
- const columns = executionResult.result.columns;
639
+
640
+ const resultState = queryBuilderState.resultState;
641
+
363
642
  const rowData = executionResult.result.rows.map((_row, rowIdx) => {
364
643
  const row: PlainObject = {};
365
644
  const cols = executionResult.result.columns;
@@ -369,57 +648,42 @@ const QueryBuilderGridResult = observer(
369
648
  // See https://github.com/finos/legend-studio/issues/1008
370
649
  row[cols[colIdx] as string] = isBoolean(value) ? String(value) : value;
371
650
  });
651
+
372
652
  row.rowNumber = rowIdx;
373
653
  return row;
374
654
  });
375
655
 
376
656
  return (
377
- <ContextMenu
378
- content={
379
- // NOTE: we only support this functionality for grid result with a projection fetch-structure
380
- fetchStructureImplementation instanceof QueryBuilderTDSState ? (
381
- <QueryBuilderGridResultContextMenu
382
- event={cellDoubleClickedEvent}
383
- tdsState={fetchStructureImplementation}
384
- />
385
- ) : null
386
- }
387
- disabled={
388
- !(fetchStructureImplementation instanceof QueryBuilderTDSState) ||
389
- !queryBuilderState.isQuerySupported ||
390
- !cellDoubleClickedEvent
391
- }
392
- menuProps={{ elevation: 7 }}
393
- key={executionResult._UUID}
394
- className={clsx('ag-theme-balham-dark query-builder__result__tds-grid')}
395
- >
396
- <DataGrid
397
- rowData={rowData}
398
- gridOptions={{
399
- suppressScrollOnNewData: true,
400
- getRowId: function (data) {
401
- return data.data.rowNumber as string;
402
- },
403
- }}
404
- // NOTE: we use onCellMouseOver as a bit of a workaround
405
- // since we use the context menu so we want the user to be
406
- // able to right click any cell and have the context menu
407
- // options use the data belonging to the row that they are
408
- // in. hence why we set the cell every time we mouse over
409
- // rather than making user click multiple times.
410
- onCellMouseOver={(event): void => {
411
- cellDoubleClickedEvent.current = event;
412
- }}
413
- suppressFieldDotNotation={true}
414
- columnDefs={columns.map((colName) => ({
415
- minWidth: 50,
416
- sortable: true,
417
- resizable: true,
418
- field: colName,
419
- flex: 1,
420
- }))}
421
- />
422
- </ContextMenu>
657
+ <div className="query-builder__result__values__table">
658
+ <div
659
+ className={clsx(
660
+ 'ag-theme-balham-dark query-builder__result__tds-grid',
661
+ )}
662
+ >
663
+ <DataGrid
664
+ rowData={rowData}
665
+ gridOptions={{
666
+ suppressScrollOnNewData: true,
667
+ getRowId: function (data) {
668
+ return data.data.rowNumber as string;
669
+ },
670
+ }}
671
+ suppressFieldDotNotation={true}
672
+ columnDefs={executionResult.result.columns.map((colName) => ({
673
+ minWidth: 50,
674
+ sortable: true,
675
+ resizable: true,
676
+ field: colName,
677
+ flex: 1,
678
+ cellRenderer: QueryResultCellRenderer,
679
+ cellRendererParams: {
680
+ resultState: resultState,
681
+ tdsExecutionResult: executionResult,
682
+ },
683
+ }))}
684
+ />
685
+ </div>
686
+ </div>
423
687
  );
424
688
  },
425
689
  );
@@ -531,6 +795,7 @@ export const QueryBuilderResultPanel = observer(
531
795
  !queryBuilderState.isQuerySupported || isSupportedQueryValid;
532
796
 
533
797
  const runQuery = (): void => {
798
+ resultState.setSelectedCells([]);
534
799
  resultState.pressedRunQuery.inProgress();
535
800
  if (queryParametersState.parameterStates.length) {
536
801
  queryParametersState.parameterValuesEditorState.open(
@@ -564,6 +829,7 @@ export const QueryBuilderResultPanel = observer(
564
829
  val === '' ? 0 : parseInt(val, 10),
565
830
  );
566
831
  };
832
+
567
833
  const allowSettingPreviewLimit = queryBuilderState.isQuerySupported;
568
834
 
569
835
  const copyExpression = (value: string): void => {