@finos/legend-query-builder 4.7.1 → 4.7.3

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 (48) hide show
  1. package/lib/__lib__/QueryBuilderTesting.d.ts +1 -0
  2. package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderTesting.js +1 -0
  4. package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
  5. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  6. package/lib/components/QueryBuilderResultPanel.js +14 -23
  7. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  8. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  9. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +5 -4
  10. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  11. package/lib/graph-manager/QueryBuilderConst.d.ts +1 -0
  12. package/lib/graph-manager/QueryBuilderConst.d.ts.map +1 -1
  13. package/lib/graph-manager/QueryBuilderConst.js +1 -0
  14. package/lib/graph-manager/QueryBuilderConst.js.map +1 -1
  15. package/lib/index.css +17 -1
  16. package/lib/index.css.map +1 -1
  17. package/lib/package.json +9 -9
  18. package/lib/stores/QueryBuilderResultState.d.ts +9 -9
  19. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  20. package/lib/stores/QueryBuilderResultState.js +20 -20
  21. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  22. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.d.ts +1 -0
  23. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.d.ts.map +1 -1
  24. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js +6 -0
  25. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js.map +1 -1
  26. package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.d.ts.map +1 -1
  27. package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.js +2 -0
  28. package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.js.map +1 -1
  29. package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.d.ts.map +1 -1
  30. package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.js +2 -0
  31. package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.js.map +1 -1
  32. package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts.map +1 -1
  33. package/lib/stores/milestoning/QueryBuilderMilestoningState.js +0 -2
  34. package/lib/stores/milestoning/QueryBuilderMilestoningState.js.map +1 -1
  35. package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.d.ts.map +1 -1
  36. package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.js +2 -0
  37. package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.js.map +1 -1
  38. package/package.json +17 -17
  39. package/src/__lib__/QueryBuilderTesting.ts +1 -0
  40. package/src/components/QueryBuilderResultPanel.tsx +34 -46
  41. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +86 -28
  42. package/src/graph-manager/QueryBuilderConst.ts +2 -0
  43. package/src/stores/QueryBuilderResultState.ts +20 -22
  44. package/src/stores/fetch-structure/tds/QueryResultSetModifierState.ts +7 -0
  45. package/src/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.ts +2 -0
  46. package/src/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.ts +2 -0
  47. package/src/stores/milestoning/QueryBuilderMilestoningState.ts +0 -2
  48. package/src/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.ts +2 -0
@@ -39,7 +39,6 @@ import {
39
39
  PanelDivider,
40
40
  } from '@finos/legend-art';
41
41
  import { format as formatSQL } from 'sql-formatter';
42
-
43
42
  import { observer } from 'mobx-react-lite';
44
43
  import { flowResult } from 'mobx';
45
44
  import type { QueryBuilderState } from '../stores/QueryBuilderState.js';
@@ -99,6 +98,7 @@ import { QUERY_BUILDER_TEST_ID } from '../__lib__/QueryBuilderTesting.js';
99
98
  import {
100
99
  DataGrid,
101
100
  type DataGridCellRendererParams,
101
+ type DataGridColumnDefinition,
102
102
  } from '@finos/legend-lego/data-grid';
103
103
  import {
104
104
  CODE_EDITOR_LANGUAGE,
@@ -115,6 +115,7 @@ import {
115
115
  QueryBuilderPostFilterOperator_IsNotEmpty,
116
116
  } from '../stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js';
117
117
  import { QueryUsageViewer } from './QueryUsageViewer.js';
118
+ import { DEFAULT_LOCALE } from '../graph-manager/QueryBuilderConst.js';
118
119
 
119
120
  export const tryToFormatSql = (sql: string): string => {
120
121
  try {
@@ -479,8 +480,15 @@ const QueryResultCellRenderer = observer(
479
480
  (params: IQueryRendererParamsWithGridType) => {
480
481
  const resultState = params.resultState;
481
482
  const tdsExecutionResult = params.tdsExecutionResult;
483
+ const fetchStructureImplementation =
484
+ resultState.queryBuilderState.fetchStructureState.implementation;
482
485
 
483
486
  const cellValue = params.value as string;
487
+ const formattedCellValue = !isNaN(Number(cellValue))
488
+ ? Intl.NumberFormat(DEFAULT_LOCALE, { maximumFractionDigits: 4 }).format(
489
+ Number(cellValue),
490
+ )
491
+ : cellValue;
484
492
  const columnName = params.column?.getColId() ?? '';
485
493
 
486
494
  const findCoordinatesFromResultValue = (
@@ -498,7 +506,6 @@ const QueryResultCellRenderer = observer(
498
506
  columnName,
499
507
  params.rowIndex,
500
508
  );
501
-
502
509
  const cellInFilteredResults = resultState.selectedCells.some(
503
510
  (result) =>
504
511
  result.coordinates.colIndex === currentCellCoordinates.colIndex &&
@@ -564,7 +571,7 @@ const QueryResultCellRenderer = observer(
564
571
  coordinates.rowIndex,
565
572
  coordinates.colIndex,
566
573
  ]);
567
- resultState.addCellData({
574
+ resultState.addSelectedCell({
568
575
  value: actualValue,
569
576
  columnName: columnName,
570
577
  coordinates: coordinates,
@@ -583,9 +590,6 @@ const QueryResultCellRenderer = observer(
583
590
  coordinates.rowIndex,
584
591
  coordinates.colIndex,
585
592
  ]);
586
-
587
- const rowNode = params.api.getRowNode(params.rowIndex.toString());
588
-
589
593
  resultState.setSelectedCells([
590
594
  {
591
595
  value: actualValue,
@@ -593,15 +597,6 @@ const QueryResultCellRenderer = observer(
593
597
  coordinates: coordinates,
594
598
  },
595
599
  ]);
596
-
597
- if (rowNode) {
598
- params.api.refreshCells({
599
- force: true,
600
- columns: [columnName],
601
- rowNodes: [rowNode],
602
- });
603
- }
604
-
605
600
  resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
606
601
  }
607
602
 
@@ -616,7 +611,6 @@ const QueryResultCellRenderer = observer(
616
611
  coordinates.rowIndex,
617
612
  coordinates.colIndex,
618
613
  ]);
619
-
620
614
  resultState.setSelectedCells([
621
615
  {
622
616
  value: actualValue,
@@ -624,19 +618,11 @@ const QueryResultCellRenderer = observer(
624
618
  coordinates: coordinates,
625
619
  },
626
620
  ]);
627
- const rowNode = params.api.getRowNode(params.rowIndex.toString());
628
-
629
- if (rowNode) {
630
- params.api.refreshCells({
631
- force: true,
632
- columns: [columnName],
633
- rowNodes: [rowNode],
634
- });
635
- }
636
621
  resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
637
622
  }
638
623
  }
639
624
  };
625
+
640
626
  const mouseUp: React.MouseEventHandler = (event) => {
641
627
  resultState.setIsSelectingCells(false);
642
628
  };
@@ -684,7 +670,7 @@ const QueryResultCellRenderer = observer(
684
670
  result.coordinates.rowIndex === x,
685
671
  )
686
672
  ) {
687
- resultState.addCellData(valueAndColumnId);
673
+ resultState.addSelectedCell(valueAndColumnId);
688
674
  }
689
675
  }
690
676
  }
@@ -693,9 +679,6 @@ const QueryResultCellRenderer = observer(
693
679
  resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
694
680
  };
695
681
 
696
- const fetchStructureImplementation =
697
- resultState.queryBuilderState.fetchStructureState.implementation;
698
-
699
682
  return (
700
683
  <ContextMenu
701
684
  content={
@@ -716,7 +699,6 @@ const QueryResultCellRenderer = observer(
716
699
  !resultState.mousedOverCell
717
700
  }
718
701
  menuProps={{ elevation: 7 }}
719
- key={params.value as string}
720
702
  className={clsx('ag-theme-balham-dark query-builder__result__tds-grid')}
721
703
  >
722
704
  <div
@@ -733,7 +715,7 @@ const QueryResultCellRenderer = observer(
733
715
  {cellValue}
734
716
  </a>
735
717
  ) : (
736
- <span>{cellValue}</span>
718
+ <span>{formattedCellValue}</span>
737
719
  )}
738
720
  </div>
739
721
  </ContextMenu>
@@ -775,23 +757,29 @@ const QueryBuilderGridResult = observer(
775
757
  rowData={rowData}
776
758
  gridOptions={{
777
759
  suppressScrollOnNewData: true,
778
- getRowId: function (data) {
779
- return data.data.rowNumber as string;
780
- },
760
+ getRowId: (data) => data.data.rowNumber,
761
+ }}
762
+ // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
763
+ // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
764
+ onRowDataUpdated={(params) => {
765
+ params.api.refreshCells({ force: true });
781
766
  }}
782
767
  suppressFieldDotNotation={true}
783
- columnDefs={executionResult.result.columns.map((colName) => ({
784
- minWidth: 50,
785
- sortable: true,
786
- resizable: true,
787
- field: colName,
788
- flex: 1,
789
- cellRenderer: QueryResultCellRenderer,
790
- cellRendererParams: {
791
- resultState: resultState,
792
- tdsExecutionResult: executionResult,
793
- },
794
- }))}
768
+ columnDefs={executionResult.result.columns.map(
769
+ (colName) =>
770
+ ({
771
+ minWidth: 50,
772
+ sortable: true,
773
+ resizable: true,
774
+ field: colName,
775
+ flex: 1,
776
+ cellRenderer: QueryResultCellRenderer,
777
+ cellRendererParams: {
778
+ resultState: resultState,
779
+ tdsExecutionResult: executionResult,
780
+ },
781
+ }) as DataGridColumnDefinition,
782
+ )}
795
783
  />
796
784
  </div>
797
785
  </div>
@@ -30,7 +30,6 @@ import {
30
30
  PanelDropZone,
31
31
  DragPreviewLayer,
32
32
  useDragPreviewLayer,
33
- OptionsIcon,
34
33
  PlusIcon,
35
34
  PanelContent,
36
35
  TrashIcon,
@@ -41,6 +40,7 @@ import {
41
40
  CustomSelectorInput,
42
41
  PanelEntryDropZonePlaceholder,
43
42
  FunctionIcon,
43
+ CogIcon,
44
44
  } from '@finos/legend-art';
45
45
  import {
46
46
  type QueryBuilderExplorerTreeDragSource,
@@ -952,6 +952,7 @@ export const QueryBuilderTDSPanel = observer(
952
952
  const clearAllProjectionColumns = (): void => {
953
953
  tdsState.checkBeforeClearingColumns(() => {
954
954
  tdsState.removeAllColumns();
955
+ tdsState.resultSetModifierState.reset();
955
956
  });
956
957
  };
957
958
 
@@ -1061,35 +1062,92 @@ export const QueryBuilderTDSPanel = observer(
1061
1062
  return (
1062
1063
  <PanelContent>
1063
1064
  <div className="query-builder__projection__toolbar">
1064
- <button
1065
- className="panel__header__action"
1066
- onClick={openResultSetModifierEditor}
1067
- tabIndex={-1}
1068
- title="Configure result set modifiers..."
1069
- >
1070
- <OptionsIcon className="query-builder__icon query-builder__icon__query-option" />
1071
- </button>
1072
- <button
1073
- className="panel__header__action"
1074
- disabled={isEmpty}
1075
- onClick={clearAllProjectionColumns}
1076
- tabIndex={-1}
1077
- title={
1078
- isEmpty
1079
- ? 'No projection columns to clear'
1080
- : 'Clear all projection columns'
1065
+ <div
1066
+ className="query-builder__projection__result-modifier-prompt"
1067
+ data-testid={
1068
+ QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TDS_RESULT_MODIFIER_PROMPT
1081
1069
  }
1082
1070
  >
1083
- <TrashIcon className="query-builder__icon query-builder__icon__query-option--small" />
1084
- </button>
1085
- <button
1086
- className="panel__header__action"
1087
- onClick={addNewBlankDerivation}
1088
- tabIndex={-1}
1089
- title="Add a new derivation"
1090
- >
1091
- <PlusIcon />
1092
- </button>
1071
+ <div className="query-builder__projection__result-modifier-prompt__header">
1072
+ <button
1073
+ className="query-builder__projection__result-modifier-prompt__header__label"
1074
+ onClick={openResultSetModifierEditor}
1075
+ title="Configure result set modifiers..."
1076
+ >
1077
+ <CogIcon className="query-builder__projection__result-modifier-prompt__header__label__icon" />
1078
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1079
+ Query Options
1080
+ </div>
1081
+ </button>
1082
+ </div>
1083
+ {tdsState.resultSetModifierState.limit && (
1084
+ <div className="query-builder__projection__result-modifier-prompt__group">
1085
+ <div className="query-builder__projection__result-modifier-prompt__group__label">
1086
+ Max Rows
1087
+ </div>
1088
+ <div
1089
+ className="query-builder__projection__result-modifier-prompt__group__content"
1090
+ onClick={openResultSetModifierEditor}
1091
+ >
1092
+ {tdsState.resultSetModifierState.limit}
1093
+ </div>
1094
+ </div>
1095
+ )}
1096
+ {tdsState.resultSetModifierState.distinct && (
1097
+ <div className="query-builder__projection__result-modifier-prompt__group">
1098
+ <div className="query-builder__projection__result-modifier-prompt__group__label">
1099
+ Eliminate Duplicate Rows
1100
+ </div>
1101
+ <div
1102
+ className="query-builder__projection__result-modifier-prompt__group__content"
1103
+ onClick={openResultSetModifierEditor}
1104
+ >
1105
+ Yes
1106
+ </div>
1107
+ </div>
1108
+ )}
1109
+ {tdsState.resultSetModifierState.sortColumns.length > 0 && (
1110
+ <div className="query-builder__projection__result-modifier-prompt__group">
1111
+ <div className="query-builder__projection__result-modifier-prompt__group__label">
1112
+ Sort
1113
+ </div>
1114
+ {tdsState.resultSetModifierState.sortColumns.map(
1115
+ (columnState) => (
1116
+ <div
1117
+ className="query-builder__projection__result-modifier-prompt__group__content"
1118
+ key={columnState.columnState.uuid}
1119
+ onClick={openResultSetModifierEditor}
1120
+ >
1121
+ {`${columnState.columnState.columnName} ${columnState.sortType}`}
1122
+ </div>
1123
+ ),
1124
+ )}
1125
+ </div>
1126
+ )}
1127
+ </div>
1128
+ <div className="query-builder__projection__toolbar__actions">
1129
+ <button
1130
+ className="panel__header__action"
1131
+ disabled={isEmpty}
1132
+ onClick={clearAllProjectionColumns}
1133
+ tabIndex={-1}
1134
+ title={
1135
+ isEmpty
1136
+ ? 'No projection columns to clear'
1137
+ : 'Clear all projection columns'
1138
+ }
1139
+ >
1140
+ <TrashIcon className="query-builder__icon query-builder__icon__query-option--small" />
1141
+ </button>
1142
+ <button
1143
+ className="panel__header__action"
1144
+ onClick={addNewBlankDerivation}
1145
+ tabIndex={-1}
1146
+ title="Add a new derivation"
1147
+ >
1148
+ <PlusIcon />
1149
+ </button>
1150
+ </div>
1093
1151
  </div>
1094
1152
  <div className="query-builder__projection__content">
1095
1153
  <PanelDropZone
@@ -18,3 +18,5 @@ export enum QUERY_BUILDER_CALENDAR_TYPE {
18
18
  NY = 'NY',
19
19
  LDN = 'LDN',
20
20
  }
21
+
22
+ export const DEFAULT_LOCALE = 'en-US';
@@ -98,13 +98,13 @@ export class QueryBuilderResultState {
98
98
  mousedOverCell: observable,
99
99
  isRunningQuery: observable,
100
100
  isSelectingCells: observable,
101
- setIsSelectingCells: action,
102
101
  isQueryUsageViewerOpened: observable,
102
+ setIsSelectingCells: action,
103
103
  setIsRunningQuery: action,
104
104
  setExecutionResult: action,
105
105
  setExecutionDuration: action,
106
106
  setPreviewLimit: action,
107
- addCellData: action,
107
+ addSelectedCell: action,
108
108
  setSelectedCells: action,
109
109
  setMouseOverCell: action,
110
110
  setQueryRunPromise: action,
@@ -124,43 +124,41 @@ export class QueryBuilderResultState {
124
124
  );
125
125
  }
126
126
 
127
- setIsSelectingCells = (val: boolean): void => {
127
+ setIsSelectingCells(val: boolean): void {
128
128
  this.isSelectingCells = val;
129
- };
129
+ }
130
130
 
131
- setIsRunningQuery = (val: boolean): void => {
131
+ setIsRunningQuery(val: boolean): void {
132
132
  this.isRunningQuery = val;
133
- };
133
+ }
134
134
 
135
- setExecutionResult = (val: ExecutionResult | undefined): void => {
135
+ setExecutionResult(val: ExecutionResult | undefined): void {
136
136
  this.executionResult = val;
137
- };
137
+ }
138
138
 
139
- setExecutionDuration = (val: number | undefined): void => {
139
+ setExecutionDuration(val: number | undefined): void {
140
140
  this.executionDuration = val;
141
- };
141
+ }
142
142
 
143
- setPreviewLimit = (val: number): void => {
143
+ setPreviewLimit(val: number): void {
144
144
  this.previewLimit = Math.max(1, val);
145
- };
145
+ }
146
146
 
147
- addCellData = (val: QueryBuilderTDSResultCellData): void => {
147
+ addSelectedCell(val: QueryBuilderTDSResultCellData): void {
148
148
  this.selectedCells.push(val);
149
- };
149
+ }
150
150
 
151
- setSelectedCells = (val: QueryBuilderTDSResultCellData[]): void => {
151
+ setSelectedCells(val: QueryBuilderTDSResultCellData[]): void {
152
152
  this.selectedCells = val;
153
- };
153
+ }
154
154
 
155
- setMouseOverCell = (val: QueryBuilderTDSResultCellData | null): void => {
155
+ setMouseOverCell(val: QueryBuilderTDSResultCellData | null): void {
156
156
  this.mousedOverCell = val;
157
- };
157
+ }
158
158
 
159
- setQueryRunPromise = (
160
- promise: Promise<ExecutionResult> | undefined,
161
- ): void => {
159
+ setQueryRunPromise(promise: Promise<ExecutionResult> | undefined): void {
162
160
  this.queryRunPromise = promise;
163
- };
161
+ }
164
162
 
165
163
  setIsQueryUsageViewerOpened(val: boolean): void {
166
164
  this.isQueryUsageViewerOpened = val;
@@ -82,6 +82,7 @@ export class QueryResultSetModifierState implements Hashable {
82
82
  deleteSortColumn: action,
83
83
  addSortColumn: action,
84
84
  updateSortColumns: action,
85
+ reset: action,
85
86
  hashCode: computed,
86
87
  });
87
88
 
@@ -114,6 +115,12 @@ export class QueryResultSetModifierState implements Hashable {
114
115
  );
115
116
  }
116
117
 
118
+ reset(): void {
119
+ this.sortColumns = [];
120
+ this.distinct = false;
121
+ this.limit = undefined;
122
+ }
123
+
117
124
  get hashCode(): string {
118
125
  return hashArray([
119
126
  QUERY_BUILDER_STATE_HASH_STRUCTURE.RESULT_SET_MODIFIER_STATE,
@@ -62,6 +62,8 @@ export class QueryBuilderBitemporalMilestoningImplementation extends QueryBuilde
62
62
  ),
63
63
  );
64
64
  }
65
+ // Show the parameter panel because we populate paramaters state with milestoning parameters
66
+ this.milestoningState.queryBuilderState.setShowParametersPanel(true);
65
67
  }
66
68
 
67
69
  processGetAllParamaters(parameterValues: ValueSpecification[]): void {
@@ -43,6 +43,8 @@ export class QueryBuilderBusinessTemporalMilestoningImplementation extends Query
43
43
  ),
44
44
  );
45
45
  }
46
+ // Show the parameter panel because we populate paramaters state with milestoning parameters
47
+ this.milestoningState.queryBuilderState.setShowParametersPanel(true);
46
48
  }
47
49
 
48
50
  processGetAllParamaters(parameterValues: ValueSpecification[]): void {
@@ -140,8 +140,6 @@ export class QueryBuilderMilestoningState implements Hashable {
140
140
  this.setProcessingDate(undefined);
141
141
  if (stereotype) {
142
142
  this.initializeQueryMilestoningParameters(stereotype);
143
- // Show the parameter panel because we populate paramaters state with milestoning parameters
144
- this.queryBuilderState.setShowParametersPanel(true);
145
143
  }
146
144
  }
147
145
  }
@@ -42,6 +42,8 @@ export class QueryBuilderProcessingTemporalMilestoningImplementation extends Que
42
42
  ),
43
43
  );
44
44
  }
45
+ // Show the parameter panel because we populate paramaters state with milestoning parameters
46
+ this.milestoningState.queryBuilderState.setShowParametersPanel(true);
45
47
  }
46
48
 
47
49
  processGetAllParamaters(parameterValues: ValueSpecification[]): void {