@finos/legend-query-builder 4.9.4 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. package/lib/__lib__/QueryBuilderTesting.d.ts +3 -1
  2. package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderTesting.js +4 -1
  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 +36 -33
  7. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  8. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts +1 -0
  9. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts.map +1 -1
  10. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js +13 -1
  11. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js.map +1 -1
  12. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.d.ts +2 -2
  13. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.d.ts.map +1 -1
  14. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js +42 -24
  15. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js.map +1 -1
  16. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  17. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +1 -1
  18. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  19. package/lib/index.css +16 -0
  20. package/lib/package.json +1 -1
  21. package/lib/stores/QueryBuilderConstantsState.d.ts +5 -1
  22. package/lib/stores/QueryBuilderConstantsState.d.ts.map +1 -1
  23. package/lib/stores/QueryBuilderConstantsState.js +14 -1
  24. package/lib/stores/QueryBuilderConstantsState.js.map +1 -1
  25. package/lib/stores/QueryBuilderStateHashUtils.d.ts +2 -0
  26. package/lib/stores/QueryBuilderStateHashUtils.d.ts.map +1 -1
  27. package/lib/stores/QueryBuilderStateHashUtils.js +2 -0
  28. package/lib/stores/QueryBuilderStateHashUtils.js.map +1 -1
  29. package/lib/stores/QueryBuilderValueSpecificationBuilder.d.ts.map +1 -1
  30. package/lib/stores/QueryBuilderValueSpecificationBuilder.js +3 -23
  31. package/lib/stores/QueryBuilderValueSpecificationBuilder.js.map +1 -1
  32. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterOperator.js +1 -1
  33. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterOperator.js.map +1 -1
  34. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts +31 -5
  35. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts.map +1 -1
  36. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js +125 -30
  37. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js.map +1 -1
  38. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.d.ts.map +1 -1
  39. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.js +21 -8
  40. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.js.map +1 -1
  41. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.d.ts +3 -1
  42. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.d.ts.map +1 -1
  43. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.js +20 -17
  44. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.js.map +1 -1
  45. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Contain.d.ts.map +1 -1
  46. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Contain.js +4 -6
  47. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Contain.js.map +1 -1
  48. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_EndWith.d.ts.map +1 -1
  49. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_EndWith.js +4 -6
  50. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_EndWith.js.map +1 -1
  51. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Equal.d.ts +1 -1
  52. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Equal.d.ts.map +1 -1
  53. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Equal.js +10 -10
  54. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Equal.js.map +1 -1
  55. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThan.d.ts.map +1 -1
  56. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThan.js +10 -10
  57. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThan.js.map +1 -1
  58. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThanEqual.d.ts.map +1 -1
  59. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThanEqual.js +5 -5
  60. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThanEqual.js.map +1 -1
  61. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.d.ts +1 -1
  62. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.d.ts.map +1 -1
  63. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js +32 -27
  64. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js.map +1 -1
  65. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.d.ts.map +1 -1
  66. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js +10 -5
  67. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js.map +1 -1
  68. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThan.d.ts.map +1 -1
  69. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThan.js +10 -10
  70. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThan.js.map +1 -1
  71. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThanEqual.d.ts.map +1 -1
  72. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThanEqual.js +5 -5
  73. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThanEqual.js.map +1 -1
  74. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_StartWith.d.ts.map +1 -1
  75. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_StartWith.js +4 -6
  76. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_StartWith.js.map +1 -1
  77. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.d.ts.map +1 -1
  78. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js +17 -3
  79. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js.map +1 -1
  80. package/package.json +4 -4
  81. package/src/__lib__/QueryBuilderTesting.ts +4 -1
  82. package/src/components/QueryBuilderResultPanel.tsx +64 -59
  83. package/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx +25 -1
  84. package/src/components/fetch-structure/QueryBuilderPostFilterPanel.tsx +102 -49
  85. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +6 -2
  86. package/src/stores/QueryBuilderConstantsState.ts +30 -0
  87. package/src/stores/QueryBuilderStateHashUtils.ts +2 -0
  88. package/src/stores/QueryBuilderValueSpecificationBuilder.ts +4 -50
  89. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterOperator.ts +1 -1
  90. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts +180 -34
  91. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.ts +38 -9
  92. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.ts +36 -20
  93. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Contain.ts +5 -6
  94. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_EndWith.ts +6 -6
  95. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Equal.ts +12 -13
  96. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThan.ts +10 -9
  97. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_GreaterThanEqual.ts +5 -4
  98. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.ts +48 -43
  99. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.ts +12 -4
  100. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThan.ts +10 -9
  101. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_LessThanEqual.ts +5 -4
  102. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_StartWith.ts +6 -6
  103. package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.ts +26 -4
@@ -80,6 +80,7 @@ import {
80
80
  type QueryBuilderPostFilterTreeNodeData,
81
81
  PostFilterConditionState,
82
82
  QueryBuilderPostFilterTreeConditionNodeData,
83
+ PostFilterValueSpecConditionValueState,
83
84
  } from '../stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js';
84
85
  import {
85
86
  QueryBuilderPostFilterOperator_Equal,
@@ -171,13 +172,13 @@ const QueryBuilderGridResultContextMenu = observer(
171
172
  .filter(
172
173
  (v) =>
173
174
  v instanceof QueryBuilderPostFilterTreeConditionNodeData &&
174
- v.condition.columnState instanceof
175
+ v.condition.leftConditionValue instanceof
175
176
  QueryBuilderProjectionColumnState,
176
177
  )
177
178
  .filter(
178
179
  (n) =>
179
180
  (n as QueryBuilderPostFilterTreeConditionNodeData).condition
180
- .columnState.columnName ===
181
+ .leftConditionValue.columnName ===
181
182
  (projectionColumnName ?? projectionColumnState?.columnName) &&
182
183
  operators
183
184
  .map((op) => op.getLabel())
@@ -231,7 +232,6 @@ const QueryBuilderGridResultContextMenu = observer(
231
232
  postFilterConditionState = new PostFilterConditionState(
232
233
  postFilterState,
233
234
  possibleProjectionColumnState,
234
- undefined,
235
235
  operator,
236
236
  );
237
237
 
@@ -249,7 +249,9 @@ const QueryBuilderGridResultContextMenu = observer(
249
249
  postFilterConditionState,
250
250
  );
251
251
 
252
- postFilterConditionState.setValue(defaultFilterConditionValue);
252
+ postFilterConditionState.buildFromValueSpec(
253
+ defaultFilterConditionValue,
254
+ );
253
255
  updateFilterConditionValue(
254
256
  defaultFilterConditionValue as InstanceValue,
255
257
  cellData,
@@ -293,62 +295,65 @@ const QueryBuilderGridResultContextMenu = observer(
293
295
  existingPostFilterNode as QueryBuilderPostFilterTreeConditionNodeData
294
296
  ).condition;
295
297
 
296
- if (conditionState.operator.getLabel() === operator.getLabel()) {
297
- const doesValueAlreadyExist =
298
- conditionState.value instanceof InstanceValue &&
299
- (conditionState.value instanceof EnumValueInstanceValue
300
- ? conditionState.value.values.map((ef) => ef.value.name)
301
- : conditionState.value.values
302
- ).includes(cellData.value);
303
-
304
- if (!doesValueAlreadyExist) {
305
- const currentValueSpecificaton = conditionState.value;
306
- const newValueSpecification =
307
- conditionState.operator.getDefaultFilterConditionValue(
308
- conditionState,
298
+ const rightSide = conditionState.rightConditionValue;
299
+ if (rightSide instanceof PostFilterValueSpecConditionValueState) {
300
+ if (conditionState.operator.getLabel() === operator.getLabel()) {
301
+ const doesValueAlreadyExist =
302
+ rightSide.value instanceof InstanceValue &&
303
+ (rightSide.value instanceof EnumValueInstanceValue
304
+ ? rightSide.value.values.map((ef) => ef.value.name)
305
+ : rightSide.value.values
306
+ ).includes(cellData.value);
307
+
308
+ if (!doesValueAlreadyExist) {
309
+ const currentValueSpecificaton = rightSide.value;
310
+ const newValueSpecification =
311
+ conditionState.operator.getDefaultFilterConditionValue(
312
+ conditionState,
313
+ );
314
+ updateFilterConditionValue(
315
+ newValueSpecification as InstanceValue,
316
+ cellData,
309
317
  );
310
- updateFilterConditionValue(
311
- newValueSpecification as InstanceValue,
312
- cellData,
313
- );
314
- conditionState.changeOperator(
315
- isFilterBy ? postFilterInOperator : postFilterNotInOperator,
316
- );
317
- instanceValue_setValues(
318
- conditionState.value as InstanceValue,
319
- [currentValueSpecificaton, newValueSpecification],
320
- tdsState.queryBuilderState.observerContext,
321
- );
322
- }
323
- } else {
324
- const doesValueAlreadyExist =
325
- conditionState.value instanceof InstanceValue &&
326
- conditionState.value.values
327
- .filter((v) => v instanceof InstanceValue)
328
- .map((v) =>
329
- v instanceof EnumValueInstanceValue
330
- ? v.values.map((ef) => ef.value.name)
331
- : (v as InstanceValue).values,
332
- )
333
- .flat()
334
- .includes(cellData.value ?? data?.value);
335
-
336
- if (!doesValueAlreadyExist) {
337
- const newValueSpecification = (
338
- isFilterBy ? postFilterEqualOperator : postFilterNotEqualOperator
339
- ).getDefaultFilterConditionValue(conditionState);
340
- updateFilterConditionValue(
341
- newValueSpecification as InstanceValue,
342
- cellData,
343
- );
344
- instanceValue_setValues(
345
- conditionState.value as InstanceValue,
346
- [
347
- ...(conditionState.value as InstanceValue).values,
348
- newValueSpecification,
349
- ],
350
- tdsState.queryBuilderState.observerContext,
351
- );
318
+ conditionState.changeOperator(
319
+ isFilterBy ? postFilterInOperator : postFilterNotInOperator,
320
+ );
321
+ instanceValue_setValues(
322
+ rightSide.value as InstanceValue,
323
+ [currentValueSpecificaton, newValueSpecification],
324
+ tdsState.queryBuilderState.observerContext,
325
+ );
326
+ }
327
+ } else {
328
+ const doesValueAlreadyExist =
329
+ rightSide.value instanceof InstanceValue &&
330
+ rightSide.value.values
331
+ .filter((v) => v instanceof InstanceValue)
332
+ .map((v) =>
333
+ v instanceof EnumValueInstanceValue
334
+ ? v.values.map((ef) => ef.value.name)
335
+ : (v as InstanceValue).values,
336
+ )
337
+ .flat()
338
+ .includes(cellData.value ?? data?.value);
339
+
340
+ if (!doesValueAlreadyExist) {
341
+ const newValueSpecification = (
342
+ isFilterBy ? postFilterEqualOperator : postFilterNotEqualOperator
343
+ ).getDefaultFilterConditionValue(conditionState);
344
+ updateFilterConditionValue(
345
+ newValueSpecification as InstanceValue,
346
+ cellData,
347
+ );
348
+ instanceValue_setValues(
349
+ rightSide.value as InstanceValue,
350
+ [
351
+ ...(rightSide.value as InstanceValue).values,
352
+ newValueSpecification,
353
+ ],
354
+ tdsState.queryBuilderState.observerContext,
355
+ );
356
+ }
352
357
  }
353
358
  }
354
359
  };
@@ -14,7 +14,14 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { type RenderResult, render, waitFor } from '@testing-library/react';
17
+ import {
18
+ type RenderResult,
19
+ render,
20
+ waitFor,
21
+ fireEvent,
22
+ findByText,
23
+ getByText,
24
+ } from '@testing-library/react';
18
25
  import { LogService } from '@finos/legend-shared';
19
26
  import { createSpy } from '@finos/legend-shared/test';
20
27
  import {
@@ -43,6 +50,23 @@ import {
43
50
  TEST__getGenericApplicationConfig,
44
51
  } from '../../stores/__test-utils__/QueryBuilderStateTestUtils.js';
45
52
 
53
+ export const dragAndDrop = async (
54
+ source: HTMLElement,
55
+ drop: HTMLElement,
56
+ panel: HTMLElement,
57
+ draggingHoverText?: string,
58
+ ): Promise<void> => {
59
+ fireEvent.dragStart(source);
60
+ fireEvent.dragEnter(drop);
61
+ fireEvent.dragOver(drop);
62
+ if (draggingHoverText) {
63
+ await findByText(panel, draggingHoverText);
64
+ fireEvent.drop(getByText(panel, draggingHoverText));
65
+ } else {
66
+ fireEvent.dragOver(drop);
67
+ }
68
+ };
69
+
46
70
  export const TEST__setUpQueryBuilder = async (
47
71
  entities: Entity[],
48
72
  lambda: RawLambda,
@@ -61,7 +61,13 @@ import {
61
61
  } from '@finos/legend-shared';
62
62
  import { flowResult } from 'mobx';
63
63
  import { observer } from 'mobx-react-lite';
64
- import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
64
+ import React, {
65
+ forwardRef,
66
+ useCallback,
67
+ useMemo,
68
+ useRef,
69
+ useState,
70
+ } from 'react';
65
71
  import { useDrop, useDrag, useDragLayer } from 'react-dnd';
66
72
  import { QueryBuilderAggregateColumnState } from '../../stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js';
67
73
  import type { QueryBuilderPostFilterOperator } from '../../stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterOperator.js';
@@ -74,6 +80,8 @@ import {
74
80
  QueryBuilderPostFilterTreeGroupNodeData,
75
81
  QueryBuilderPostFilterTreeBlankConditionNodeData,
76
82
  QUERY_BUILDER_POST_FILTER_DND_TYPE,
83
+ PostFilterValueSpecConditionValueState,
84
+ PostFilterTDSColumnValueConditionValueState,
77
85
  } from '../../stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js';
78
86
  import {
79
87
  type QueryBuilderProjectionColumnState,
@@ -203,14 +211,14 @@ const QueryBuilderPostFilterGroupConditionEditor = observer(
203
211
 
204
212
  export const QueryBuilderColumnBadge = observer(
205
213
  (props: {
206
- postFilterConditionState: PostFilterConditionState;
214
+ colState: QueryBuilderTDSColumnState;
207
215
  onColumnChange: (
208
216
  columnState: QueryBuilderProjectionColumnState,
209
217
  ) => Promise<void>;
210
218
  }) => {
211
- const { postFilterConditionState, onColumnChange } = props;
219
+ const { colState, onColumnChange } = props;
212
220
  const applicationStore = useApplicationStore();
213
- const type = postFilterConditionState.columnState.getColumnType();
221
+ const type = colState.getColumnType();
214
222
  const handleDrop = useCallback(
215
223
  (item: QueryBuilderProjectionColumnDragSource): Promise<void> =>
216
224
  onColumnChange(item.columnState),
@@ -261,12 +269,12 @@ export const QueryBuilderColumnBadge = observer(
261
269
  )}
262
270
  <div
263
271
  className="query-builder-column-badge__property"
264
- title={`${postFilterConditionState.columnName}`}
272
+ title={`${colState.columnName}`}
265
273
  >
266
- {postFilterConditionState.columnName}
274
+ {colState.columnName}
267
275
  </div>
268
276
  <QueryBuilderColumnInfoTooltip
269
- columnState={postFilterConditionState.columnState}
277
+ columnState={colState}
270
278
  placement="bottom-end"
271
279
  >
272
280
  <div className="query-builder-column-badge__property__info">
@@ -291,13 +299,15 @@ const QueryBuilderPostFilterConditionEditor = observer(
291
299
  const applicationStore = useApplicationStore();
292
300
  const changeOperator = (val: QueryBuilderPostFilterOperator) => (): void =>
293
301
  node.condition.changeOperator(val);
302
+ const rightConditionValue = node.condition.rightConditionValue;
294
303
  const changeColumn = async (
295
304
  columnState: QueryBuilderProjectionColumnState,
296
305
  ): Promise<void> => {
297
306
  const currentColState =
298
- node.condition.columnState instanceof QueryBuilderAggregateColumnState
299
- ? node.condition.columnState.projectionColumnState
300
- : node.condition.columnState;
307
+ node.condition.leftConditionValue instanceof
308
+ QueryBuilderAggregateColumnState
309
+ ? node.condition.leftConditionValue.projectionColumnState
310
+ : node.condition.leftConditionValue;
301
311
  if (currentColState !== columnState) {
302
312
  await flowResult(node.condition.changeColumn(columnState));
303
313
  }
@@ -306,12 +316,13 @@ const QueryBuilderPostFilterConditionEditor = observer(
306
316
  const handleDrop = useCallback(
307
317
  (item: QueryBuilderVariableDragSource): void => {
308
318
  const parameterType = item.variable.genericType?.value.rawType;
309
- const conditionValueType = node.condition.columnState.getColumnType();
319
+ const conditionValueType =
320
+ node.condition.leftConditionValue.getColumnType();
310
321
  if (
311
322
  conditionValueType &&
312
323
  isTypeCompatibleForAssignment(parameterType, conditionValueType)
313
324
  ) {
314
- node.condition.setValue(item.variable);
325
+ node.condition.buildFromValueSpec(item.variable);
315
326
  } else {
316
327
  applicationStore.notificationService.notifyWarning(
317
328
  `Incompatible parameter type ${parameterType?.name}. ${parameterType?.name} is not compatible with type ${conditionValueType?.name}.`,
@@ -339,7 +350,7 @@ const QueryBuilderPostFilterConditionEditor = observer(
339
350
  [handleDrop],
340
351
  );
341
352
  const resetNode = (): void => {
342
- node.condition.setValue(
353
+ node.condition.buildFromValueSpec(
343
354
  node.condition.operator.getDefaultFilterConditionValue(node.condition),
344
355
  );
345
356
  };
@@ -355,7 +366,7 @@ const QueryBuilderPostFilterConditionEditor = observer(
355
366
  node.condition.typeaheadSearchState.complete();
356
367
  };
357
368
  const changeValueSpecification = (val: ValueSpecification): void => {
358
- node.condition.setValue(val);
369
+ node.condition.buildFromValueSpec(val);
359
370
  };
360
371
  const selectorConfig = {
361
372
  values: node.condition.typeaheadSearchResults,
@@ -371,6 +382,70 @@ const QueryBuilderPostFilterConditionEditor = observer(
371
382
  monitor.getItemType() === QUERY_BUILDER_WINDOW_COLUMN_DND_TYPE),
372
383
  }));
373
384
 
385
+ const renderRightVal = (): React.ReactNode => {
386
+ if (
387
+ rightConditionValue instanceof PostFilterValueSpecConditionValueState &&
388
+ rightConditionValue.value
389
+ ) {
390
+ return (
391
+ <div
392
+ ref={dropConnector}
393
+ className="query-builder-post-filter-tree__condition-node__value"
394
+ >
395
+ <PanelEntryDropZonePlaceholder
396
+ isDragOver={isFilterValueDragOver}
397
+ label="Change Filter Value"
398
+ >
399
+ <BasicValueSpecificationEditor
400
+ valueSpecification={rightConditionValue.value}
401
+ setValueSpecification={changeValueSpecification}
402
+ graph={graph}
403
+ obseverContext={queryBuilderState.observerContext}
404
+ typeCheckOption={{
405
+ expectedType: guaranteeNonNullable(
406
+ node.condition.leftConditionValue.getColumnType(),
407
+ ),
408
+ }}
409
+ resetValue={resetNode}
410
+ selectorConfig={selectorConfig}
411
+ isConstant={queryBuilderState.constantState.isValueSpecConstant(
412
+ rightConditionValue.value,
413
+ )}
414
+ />
415
+ </PanelEntryDropZonePlaceholder>
416
+ </div>
417
+ );
418
+ } else if (
419
+ rightConditionValue instanceof
420
+ PostFilterTDSColumnValueConditionValueState
421
+ ) {
422
+ const changeRightCol = async (
423
+ columnState: QueryBuilderProjectionColumnState,
424
+ ): Promise<void> => {
425
+ rightConditionValue.changeCol(columnState);
426
+ };
427
+ return (
428
+ <div
429
+ ref={dropConnector}
430
+ className="query-builder-post-filter-tree__condition-node__value"
431
+ >
432
+ <PanelEntryDropZonePlaceholder
433
+ isDragOver={isFilterValueDragOver}
434
+ label="Change Filter Value"
435
+ >
436
+ <div className="query-builder-post-filter-tree__condition-node__property">
437
+ <QueryBuilderColumnBadge
438
+ colState={rightConditionValue.tdsColumn}
439
+ onColumnChange={changeRightCol}
440
+ />
441
+ </div>
442
+ </PanelEntryDropZonePlaceholder>
443
+ </div>
444
+ );
445
+ }
446
+ return null;
447
+ };
448
+
374
449
  return (
375
450
  <div className="query-builder-post-filter-tree__node__label__content dnd__entry__container">
376
451
  <PanelEntryDropZonePlaceholder
@@ -381,7 +456,7 @@ const QueryBuilderPostFilterConditionEditor = observer(
381
456
  <div className="query-builder-post-filter-tree__condition-node">
382
457
  <div className="query-builder-post-filter-tree__condition-node__property">
383
458
  <QueryBuilderColumnBadge
384
- postFilterConditionState={node.condition}
459
+ colState={node.condition.leftConditionValue}
385
460
  onColumnChange={changeColumn}
386
461
  />
387
462
  </div>
@@ -414,34 +489,7 @@ const QueryBuilderPostFilterConditionEditor = observer(
414
489
  <CaretDownIcon />
415
490
  </div>
416
491
  </DropdownMenu>
417
- {node.condition.value && (
418
- <div
419
- ref={dropConnector}
420
- className="query-builder-post-filter-tree__condition-node__value"
421
- >
422
- <PanelEntryDropZonePlaceholder
423
- isDragOver={isFilterValueDragOver}
424
- label="Change Filter Value"
425
- >
426
- <BasicValueSpecificationEditor
427
- valueSpecification={node.condition.value}
428
- setValueSpecification={changeValueSpecification}
429
- graph={graph}
430
- obseverContext={queryBuilderState.observerContext}
431
- typeCheckOption={{
432
- expectedType: guaranteeNonNullable(
433
- node.condition.columnState.getColumnType(),
434
- ),
435
- }}
436
- resetValue={resetNode}
437
- selectorConfig={selectorConfig}
438
- isConstant={queryBuilderState.constantState.isValueSpecConstant(
439
- node.condition.value,
440
- )}
441
- />
442
- </PanelEntryDropZonePlaceholder>
443
- </div>
444
- )}
492
+ {renderRightVal()}
445
493
  </div>
446
494
  </PanelEntryDropZonePlaceholder>
447
495
  </div>
@@ -538,9 +586,8 @@ const QueryBuilderPostFilterTreeNodeContainer = observer(
538
586
  postFilterState,
539
587
  columnState,
540
588
  undefined,
541
- undefined,
542
589
  );
543
- conditionState.setValue(
590
+ conditionState.buildFromValueSpec(
544
591
  conditionState.operator.getDefaultFilterConditionValue(
545
592
  conditionState,
546
593
  ),
@@ -648,6 +695,9 @@ const QueryBuilderPostFilterTreeNodeContainer = observer(
648
695
  >
649
696
  <div
650
697
  ref={ref}
698
+ data-testid={
699
+ QUERY_BUILDER_TEST_ID.QUERY_BUILDER_POST_FILTER_TREE_NODE_CONTENT
700
+ }
651
701
  className={clsx(
652
702
  'tree-view__node__container query-builder-post-filter-tree__node__container',
653
703
  {
@@ -679,6 +729,10 @@ const QueryBuilderPostFilterTreeNodeContainer = observer(
679
729
  <div
680
730
  className={clsx(
681
731
  'tree-view__node__label query-builder-post-filter-tree__node__label',
732
+ {
733
+ 'query-builder-post-filter-tree__node__label--expandable':
734
+ isExpandable,
735
+ },
682
736
  )}
683
737
  >
684
738
  {node instanceof QueryBuilderPostFilterTreeGroupNodeData && (
@@ -894,9 +948,8 @@ const QueryBuilderPostFilterPanelContent = observer(
894
948
  postFilterState,
895
949
  aggregateColumnState ?? columnState,
896
950
  undefined,
897
- undefined,
898
951
  );
899
- postFilterConditionState.setValue(
952
+ postFilterConditionState.buildFromValueSpec(
900
953
  postFilterConditionState.operator.getDefaultFilterConditionValue(
901
954
  postFilterConditionState,
902
955
  ),
@@ -959,7 +1012,7 @@ const QueryBuilderPostFilterPanelContent = observer(
959
1012
  <div className="panel__header__actions">
960
1013
  <DropdownMenu
961
1014
  className="panel__header__action"
962
- title="Show Filter Options Menu..."
1015
+ title="Show Post-Filter Options Menu..."
963
1016
  content={
964
1017
  <MenuContent>
965
1018
  <MenuContentItem onClick={createCondition}>
@@ -1091,7 +1144,7 @@ export const QueryBuilderPostFilterPanel = observer(
1091
1144
 
1092
1145
  return (
1093
1146
  <div
1094
- data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_POST_FILTER}
1147
+ data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_POST_FILTER_PANEL}
1095
1148
  className="panel"
1096
1149
  >
1097
1150
  {fetchStructureImplementation instanceof QueryBuilderTDSState && (
@@ -688,7 +688,6 @@ const QueryBuilderProjectionColumnEditor = observer(
688
688
  }),
689
689
  [handleDrop],
690
690
  );
691
-
692
691
  return (
693
692
  <PanelDnDEntry
694
693
  ref={ref}
@@ -732,7 +731,12 @@ const QueryBuilderProjectionColumnEditor = observer(
732
731
  onOpen={onContextMenuOpen}
733
732
  onClose={onContextMenuClose}
734
733
  >
735
- <div className="query-builder__projection__column__container">
734
+ <div
735
+ data-testid={
736
+ QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TDS_PROJECTION_COLUMN
737
+ }
738
+ className="query-builder__projection__column__container"
739
+ >
736
740
  <PanelEntryDragHandle
737
741
  isDragging={false}
738
742
  dragSourceConnector={dragHandleRef}
@@ -24,6 +24,12 @@ import {
24
24
  buildSourceInformationSourceId,
25
25
  ParserError,
26
26
  GRAPH_MANAGER_EVENT,
27
+ SimpleFunctionExpression,
28
+ PrimitiveInstanceValue,
29
+ PrimitiveType,
30
+ extractElementNameFromPath,
31
+ SUPPORTED_FUNCTIONS,
32
+ INTERNAL__UnknownValueSpecification,
27
33
  } from '@finos/legend-graph';
28
34
  import {
29
35
  type Hashable,
@@ -70,6 +76,22 @@ export abstract class QueryBuilderConstantExpressionState implements Hashable {
70
76
  this.variable.name,
71
77
  ]);
72
78
  }
79
+
80
+ buildLetExpression(): SimpleFunctionExpression {
81
+ const leftSide = new PrimitiveInstanceValue(
82
+ GenericTypeExplicitReference.create(
83
+ new GenericType(PrimitiveType.STRING),
84
+ ),
85
+ );
86
+ leftSide.values = [this.variable.name];
87
+ const letFunc = new SimpleFunctionExpression(
88
+ extractElementNameFromPath(SUPPORTED_FUNCTIONS.LET),
89
+ );
90
+ letFunc.parametersValues = [leftSide, this.buildLetAssignmentValue()];
91
+ return letFunc;
92
+ }
93
+
94
+ abstract buildLetAssignmentValue(): ValueSpecification;
73
95
  }
74
96
 
75
97
  export class QueryBuilderSimpleConstantExpressionState
@@ -141,6 +163,10 @@ export class QueryBuilderSimpleConstantExpressionState
141
163
  }
142
164
  }
143
165
 
166
+ override buildLetAssignmentValue(): ValueSpecification {
167
+ return this.value;
168
+ }
169
+
144
170
  override get hashCode(): string {
145
171
  return hashArray([
146
172
  QUERY_BUILDER_STATE_HASH_STRUCTURE.CONSTANT_EXPRESSION_STATE,
@@ -272,6 +298,10 @@ export class QueryBuilderCalculatedConstantExpressionState
272
298
  setValue(val: PlainObject): void {
273
299
  this.value = val;
274
300
  }
301
+
302
+ override buildLetAssignmentValue(): ValueSpecification {
303
+ return new INTERNAL__UnknownValueSpecification(this.value);
304
+ }
275
305
  }
276
306
 
277
307
  export class QueryBuilderConstantsState implements Hashable {
@@ -45,6 +45,8 @@ export enum QUERY_BUILDER_STATE_HASH_STRUCTURE {
45
45
  POST_FILTER_TREE_CONDIITION_NODE_DATA = 'POST_FILTER_TREE_CONDITION_NODE_DATA',
46
46
  POST_FILTER_TREE_BLANK_CONDITION_NODE_DATA = 'POST_FILTER_TREE_BLANK_CONDITION_NODE_DATA',
47
47
  POST_FILTER_CONDITION_STATE = 'POST_FILTER_CONDITION_STATE',
48
+ POST_FILTER_CONDITION_RIGHT_VALUE = 'POST_FILTER_CONDITION_RIGHT_VALUE',
49
+ POST_FILTER_CONDITION_RIGHT_VALUE_SPEC = 'POST_FILTER_CONDITION_RIGHT_VALUE_SPEC',
48
50
  POST_FILTER_OPERATOR_CONTAIN = 'POST_FILTER_OPERATOR_CONTAIN',
49
51
  POST_FILTER_OPERATOR_NOT_CONTAIN = 'POST_FILTER_OPERATOR_NOT_CONTAIN',
50
52
  POST_FILTER_OPERATOR_END_WITH = 'POST_FILTER_OPERATOR_END_WITH',
@@ -14,14 +14,9 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {
18
- UnsupportedOperationError,
19
- guaranteeNonNullable,
20
- guaranteeType,
21
- } from '@finos/legend-shared';
17
+ import { guaranteeNonNullable, guaranteeType } from '@finos/legend-shared';
22
18
  import {
23
19
  type Class,
24
- type ValueSpecification,
25
20
  Multiplicity,
26
21
  getMilestoneTemporalStereotype,
27
22
  extractElementNameFromPath,
@@ -33,11 +28,8 @@ import {
33
28
  GenericTypeExplicitReference,
34
29
  LambdaFunction,
35
30
  SimpleFunctionExpression,
36
- PrimitiveInstanceValue,
37
- PrimitiveType,
38
31
  SUPPORTED_FUNCTIONS,
39
32
  RuntimePointer,
40
- INTERNAL__UnknownValueSpecification,
41
33
  } from '@finos/legend-graph';
42
34
  import type { QueryBuilderState } from './QueryBuilderState.js';
43
35
  import { buildFilterExpression } from './filter/QueryBuilderFilterValueSpecificationBuilder.js';
@@ -46,11 +38,6 @@ import type { QueryBuilderFetchStructureState } from './fetch-structure/QueryBui
46
38
  import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../graph/QueryBuilderMetaModelConst.js';
47
39
  import { buildWatermarkExpression } from './watermark/QueryBuilderWatermarkValueSpecificationBuilder.js';
48
40
  import { buildExecutionQueryFromLambdaFunction } from './shared/LambdaParameterState.js';
49
- import {
50
- QueryBuilderSimpleConstantExpressionState,
51
- type QueryBuilderConstantExpressionState,
52
- QueryBuilderCalculatedConstantExpressionState,
53
- } from './QueryBuilderConstantsState.js';
54
41
  import {
55
42
  QueryBuilderEmbeddedFromExecutionContextState,
56
43
  type QueryBuilderExecutionContextState,
@@ -90,40 +77,6 @@ const buildGetAllVersionsFunction = (
90
77
  return _func;
91
78
  };
92
79
 
93
- const buildLetExpression = (
94
- constantExpressionState: QueryBuilderConstantExpressionState,
95
- ): SimpleFunctionExpression => {
96
- const varName = constantExpressionState.variable.name;
97
- const leftSide = new PrimitiveInstanceValue(
98
- GenericTypeExplicitReference.create(new GenericType(PrimitiveType.STRING)),
99
- );
100
- leftSide.values = [varName];
101
- const letFunc = new SimpleFunctionExpression(
102
- extractElementNameFromPath(SUPPORTED_FUNCTIONS.LET),
103
- );
104
- let value: ValueSpecification;
105
- if (
106
- constantExpressionState instanceof QueryBuilderSimpleConstantExpressionState
107
- ) {
108
- value = constantExpressionState.value;
109
- } else if (
110
- constantExpressionState instanceof
111
- QueryBuilderCalculatedConstantExpressionState
112
- ) {
113
- value = new INTERNAL__UnknownValueSpecification(
114
- constantExpressionState.value,
115
- );
116
- } else {
117
- throw new UnsupportedOperationError(
118
- `Can't build let() expression: unsupported constant state`,
119
- constantExpressionState,
120
- );
121
- }
122
-
123
- letFunc.parametersValues = [leftSide, value];
124
- return letFunc;
125
- };
126
-
127
80
  const buildExecutionContextState = (
128
81
  executionState: QueryBuilderExecutionContextState,
129
82
  lambdaFunction: LambdaFunction,
@@ -237,8 +190,9 @@ export const buildLambdaFunction = (
237
190
 
238
191
  // build variable expressions
239
192
  if (queryBuilderState.constantState.constants.length) {
240
- const letExpressions =
241
- queryBuilderState.constantState.constants.map(buildLetExpression);
193
+ const letExpressions = queryBuilderState.constantState.constants.map((e) =>
194
+ e.buildLetExpression(),
195
+ );
242
196
  lambdaFunction.expressionSequence = [
243
197
  ...letExpressions,
244
198
  ...lambdaFunction.expressionSequence,
@@ -48,7 +48,7 @@ export abstract class QueryBuilderPostFilterOperator implements Hashable {
48
48
  isCompatibleWithPostFilterColumn(
49
49
  postFilterState: PostFilterConditionState,
50
50
  ): boolean {
51
- const columnType = postFilterState.columnState.getColumnType();
51
+ const columnType = postFilterState.leftConditionValue.getColumnType();
52
52
  if (columnType) {
53
53
  return this.isCompatibleWithType(columnType);
54
54
  }