@finos/legend-query-builder 4.0.9 → 4.0.10

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 (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 => {