@finos/legend-query-builder 4.14.8 → 4.14.9

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 (31) hide show
  1. package/lib/components/QueryBuilderPropertyExpressionEditor.d.ts +2 -0
  2. package/lib/components/QueryBuilderPropertyExpressionEditor.d.ts.map +1 -1
  3. package/lib/components/QueryBuilderPropertyExpressionEditor.js +6 -3
  4. package/lib/components/QueryBuilderPropertyExpressionEditor.js.map +1 -1
  5. package/lib/components/explorer/QueryBuilderExplorerPanel.d.ts.map +1 -1
  6. package/lib/components/explorer/QueryBuilderExplorerPanel.js +12 -9
  7. package/lib/components/explorer/QueryBuilderExplorerPanel.js.map +1 -1
  8. package/lib/components/explorer/QueryBuilderPropertySearchPanel.d.ts.map +1 -1
  9. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js +6 -5
  10. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js.map +1 -1
  11. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  12. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +19 -5
  13. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  14. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.d.ts +3 -0
  15. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.d.ts.map +1 -1
  16. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js +3 -3
  17. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js.map +1 -1
  18. package/lib/index.css +2 -2
  19. package/lib/index.css.map +1 -1
  20. package/lib/package.json +1 -1
  21. package/lib/stores/explorer/QueryBuilderExplorerState.d.ts +5 -0
  22. package/lib/stores/explorer/QueryBuilderExplorerState.d.ts.map +1 -1
  23. package/lib/stores/explorer/QueryBuilderExplorerState.js +32 -0
  24. package/lib/stores/explorer/QueryBuilderExplorerState.js.map +1 -1
  25. package/package.json +5 -5
  26. package/src/components/QueryBuilderPropertyExpressionEditor.tsx +16 -2
  27. package/src/components/explorer/QueryBuilderExplorerPanel.tsx +18 -8
  28. package/src/components/explorer/QueryBuilderPropertySearchPanel.tsx +8 -5
  29. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +65 -24
  30. package/src/components/shared/QueryBuilderPropertyInfoTooltip.tsx +29 -2
  31. package/src/stores/explorer/QueryBuilderExplorerState.ts +35 -0
@@ -144,8 +144,9 @@ const QueryBuilderProjectionColumnContextMenu = observer(
144
144
  const QueryBuilderSimpleProjectionColumnEditor = observer(
145
145
  (props: {
146
146
  projectionColumnState: QueryBuilderSimpleProjectionColumnState;
147
+ setIsEditingColumnName: (isEditing: boolean) => void;
147
148
  }) => {
148
- const { projectionColumnState } = props;
149
+ const { projectionColumnState, setIsEditingColumnName } = props;
149
150
  const onPropertyExpressionChange = (
150
151
  node: QueryBuilderExplorerTreePropertyNodeData,
151
152
  ): void =>
@@ -158,10 +159,12 @@ const QueryBuilderSimpleProjectionColumnEditor = observer(
158
159
  return (
159
160
  <div className="query-builder__projection__column__value__property">
160
161
  <QueryBuilderPropertyExpressionBadge
162
+ columnName={projectionColumnState.columnName}
161
163
  propertyExpressionState={
162
164
  projectionColumnState.propertyExpressionState
163
165
  }
164
166
  onPropertyExpressionChange={onPropertyExpressionChange}
167
+ setIsEditingColumnName={setIsEditingColumnName}
165
168
  />
166
169
  </div>
167
170
  );
@@ -378,6 +381,13 @@ const QueryBuilderProjectionColumnEditor = observer(
378
381
  ) => projectionColumnState.setColumnName(event.target.value);
379
382
  const isDuplicatedColumnName =
380
383
  projectionColumnState.tdsState.isDuplicateColumn(projectionColumnState);
384
+ const [isEditingColumnName, setIsEditingColumnName] = useState(false);
385
+ const columnNameInputRef = useRef<HTMLInputElement>(null);
386
+ useEffect(() => {
387
+ if (isEditingColumnName) {
388
+ columnNameInputRef.current?.focus();
389
+ }
390
+ }, [isEditingColumnName, columnNameInputRef]);
381
391
 
382
392
  // aggregation
383
393
  const aggregateColumnState = tdsState.aggregationState.columns.find(
@@ -742,29 +752,60 @@ const QueryBuilderProjectionColumnEditor = observer(
742
752
  dragSourceConnector={dragHandleRef}
743
753
  className="query-builder__projection__column__drag-handle__container"
744
754
  />
745
- <div className="query-builder__projection__column__name">
746
- <InputWithInlineValidation
747
- className="query-builder__projection__column__name__input input-group__input"
748
- spellCheck={false}
749
- value={projectionColumnState.columnName}
750
- onChange={changeColumnName}
751
- error={isDuplicatedColumnName ? 'Duplicated column' : undefined}
752
- />
753
- </div>
754
- <div className="query-builder__projection__column__value">
755
- {projectionColumnState instanceof
756
- QueryBuilderSimpleProjectionColumnState && (
757
- <QueryBuilderSimpleProjectionColumnEditor
758
- projectionColumnState={projectionColumnState}
759
- />
760
- )}
761
- {projectionColumnState instanceof
762
- QueryBuilderDerivationProjectionColumnState && (
763
- <QueryBuilderDerivationProjectionColumnEditor
764
- projectionColumnState={projectionColumnState}
765
- />
766
- )}
767
- </div>
755
+ {projectionColumnState instanceof
756
+ QueryBuilderSimpleProjectionColumnState &&
757
+ (isEditingColumnName ? (
758
+ <div className="query-builder__projection__column__name">
759
+ <InputWithInlineValidation
760
+ className="query-builder__projection__column__name__input input-group__input"
761
+ spellCheck={false}
762
+ value={projectionColumnState.columnName}
763
+ onChange={changeColumnName}
764
+ onKeyDown={(
765
+ event: React.KeyboardEvent<HTMLInputElement>,
766
+ ) => {
767
+ if (event.key === 'Enter') {
768
+ setIsEditingColumnName(false);
769
+ }
770
+ }}
771
+ onBlur={() => {
772
+ setIsEditingColumnName(false);
773
+ }}
774
+ error={
775
+ isDuplicatedColumnName ? 'Duplicated column' : undefined
776
+ }
777
+ ref={columnNameInputRef}
778
+ />
779
+ </div>
780
+ ) : (
781
+ <div className="query-builder__projection__column__value">
782
+ <QueryBuilderSimpleProjectionColumnEditor
783
+ projectionColumnState={projectionColumnState}
784
+ setIsEditingColumnName={setIsEditingColumnName}
785
+ />
786
+ </div>
787
+ ))}
788
+ {projectionColumnState instanceof
789
+ QueryBuilderDerivationProjectionColumnState && (
790
+ <>
791
+ <div className="query-builder__projection__column__name query-builder__projection__column__name__derivation">
792
+ <InputWithInlineValidation
793
+ className="query-builder__projection__column__name__input input-group__input"
794
+ spellCheck={false}
795
+ value={projectionColumnState.columnName}
796
+ onChange={changeColumnName}
797
+ error={
798
+ isDuplicatedColumnName ? 'Duplicated column' : undefined
799
+ }
800
+ />
801
+ </div>
802
+ <div className="query-builder__projection__column__value">
803
+ <QueryBuilderDerivationProjectionColumnEditor
804
+ projectionColumnState={projectionColumnState}
805
+ />
806
+ </div>
807
+ </>
808
+ )}
768
809
  <div className="query-builder__projection__column__aggregate">
769
810
  <div className="query-builder__projection__column__aggregate__operator">
770
811
  {aggregateColumnState && (
@@ -14,7 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { type TooltipPlacement, Tooltip } from '@finos/legend-art';
17
+ import {
18
+ ShareBoxIcon,
19
+ type TooltipPlacement,
20
+ Tooltip,
21
+ } from '@finos/legend-art';
18
22
  import {
19
23
  type AbstractProperty,
20
24
  type Type,
@@ -25,6 +29,7 @@ import {
25
29
  PURE_DOC_TAG,
26
30
  } from '@finos/legend-graph';
27
31
  import { useState } from 'react';
32
+ import type { QueryBuilderExplorerState } from '../../stores/explorer/QueryBuilderExplorerState.js';
28
33
 
29
34
  export const QueryBuilderTaggedValueInfoTooltip: React.FC<{
30
35
  taggedValues: TaggedValue[];
@@ -112,14 +117,25 @@ export const QueryBuilderTaggedValueInfoTooltip: React.FC<{
112
117
  };
113
118
 
114
119
  export const QueryBuilderPropertyInfoTooltip: React.FC<{
120
+ title: string;
115
121
  property: AbstractProperty;
116
122
  path: string;
117
123
  isMapped: boolean;
118
124
  children: React.ReactElement;
119
125
  placement?: TooltipPlacement | undefined;
120
126
  type?: Type | undefined;
127
+ explorerState?: QueryBuilderExplorerState | undefined;
121
128
  }> = (props) => {
122
- const { property, path, isMapped, children, placement, type } = props;
129
+ const {
130
+ title,
131
+ property,
132
+ path,
133
+ isMapped,
134
+ children,
135
+ placement,
136
+ type,
137
+ explorerState,
138
+ } = props;
123
139
 
124
140
  return (
125
141
  <Tooltip
@@ -138,6 +154,7 @@ export const QueryBuilderPropertyInfoTooltip: React.FC<{
138
154
  }}
139
155
  title={
140
156
  <div className="query-builder__tooltip__content">
157
+ <div className="query-builder__tooltip__header">{title}</div>
141
158
  <div className="query-builder__tooltip__item">
142
159
  <div className="query-builder__tooltip__item__label">Type</div>
143
160
  <div className="query-builder__tooltip__item__value">
@@ -147,6 +164,16 @@ export const QueryBuilderPropertyInfoTooltip: React.FC<{
147
164
  <div className="query-builder__tooltip__item">
148
165
  <div className="query-builder__tooltip__item__label">Path</div>
149
166
  <div className="query-builder__tooltip__item__value">{path}</div>
167
+ {explorerState && (
168
+ <div className="query-builder__tooltip__item__action">
169
+ <button
170
+ onClick={() => explorerState.highlightTreeNode(path)}
171
+ title="Show in tree"
172
+ >
173
+ <ShareBoxIcon color="white" />
174
+ </button>
175
+ </div>
176
+ )}
150
177
  </div>
151
178
  <div className="query-builder__tooltip__item">
152
179
  <div className="query-builder__tooltip__item__label">
@@ -72,6 +72,7 @@ import { QueryBuilderPropertySearchState } from './QueryBuilderPropertySearchSta
72
72
  import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js';
73
73
  import { propertyExpression_setFunc } from '../shared/ValueSpecificationModifierHelper.js';
74
74
  import { QueryBuilderTelemetryHelper } from '../../__lib__/QueryBuilderTelemetryHelper.js';
75
+ import { createRef } from 'react';
75
76
 
76
77
  export enum QUERY_BUILDER_EXPLORER_TREE_DND_TYPE {
77
78
  ROOT = 'ROOT',
@@ -97,6 +98,7 @@ export interface QueryBuilderExplorerTreeDragSource {
97
98
  export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
98
99
  isSelected?: boolean | undefined;
99
100
  isOpen?: boolean | undefined;
101
+ isHighlighting?: boolean | undefined;
100
102
  id: string;
101
103
  label: string;
102
104
  dndText: string;
@@ -104,6 +106,7 @@ export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
104
106
  isPartOfDerivedPropertyBranch: boolean;
105
107
  type: Type;
106
108
  mappingData: QueryBuilderExplorerTreeNodeMappingData;
109
+ elementRef: React.RefObject<HTMLDivElement>;
107
110
 
108
111
  constructor(
109
112
  id: string,
@@ -114,7 +117,9 @@ export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
114
117
  mappingData: QueryBuilderExplorerTreeNodeMappingData,
115
118
  ) {
116
119
  makeObservable(this, {
120
+ isHighlighting: observable,
117
121
  isSelected: observable,
122
+ setIsHighlighting: action,
118
123
  setIsSelected: action,
119
124
  });
120
125
 
@@ -124,11 +129,16 @@ export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
124
129
  this.isPartOfDerivedPropertyBranch = isPartOfDerivedPropertyBranch;
125
130
  this.type = type;
126
131
  this.mappingData = mappingData;
132
+ this.elementRef = createRef();
127
133
  }
128
134
 
129
135
  setIsSelected(val: boolean | undefined): void {
130
136
  this.isSelected = val;
131
137
  }
138
+
139
+ setIsHighlighting(val: boolean | undefined): void {
140
+ this.isHighlighting = val;
141
+ }
132
142
  }
133
143
 
134
144
  export type QueryBuilderExplorerTreeNodeMappingData = {
@@ -663,6 +673,7 @@ export class QueryBuilderExplorerState {
663
673
  setHumanizePropertyName: action,
664
674
  setShowUnmappedProperties: action,
665
675
  setHighlightUsedProperties: action,
676
+ highlightTreeNode: action,
666
677
  analyzeMappingModelCoverage: flow,
667
678
  previewData: flow,
668
679
  });
@@ -717,6 +728,30 @@ export class QueryBuilderExplorerState {
717
728
  );
718
729
  }
719
730
 
731
+ highlightTreeNode(key: string): void {
732
+ const nodeToHighlight = this.treeData?.nodes.get(key);
733
+ if (nodeToHighlight instanceof QueryBuilderExplorerTreePropertyNodeData) {
734
+ let nodeToOpen: QueryBuilderExplorerTreeNodeData | null =
735
+ this.treeData?.nodes.get(nodeToHighlight.parentId) ?? null;
736
+ while (nodeToOpen !== null) {
737
+ if (!nodeToOpen.isOpen) {
738
+ nodeToOpen.isOpen = true;
739
+ }
740
+ nodeToOpen =
741
+ nodeToOpen instanceof QueryBuilderExplorerTreePropertyNodeData
742
+ ? this.treeData?.nodes.get(nodeToOpen.parentId) ?? null
743
+ : null;
744
+ }
745
+ this.refreshTree();
746
+ nodeToHighlight.setIsHighlighting(true);
747
+ // scrollIntoView must be called in a setTimeout because it must happen after
748
+ // the tree nodes are recursively opened and the tree is refreshed.
749
+ setTimeout(() => {
750
+ nodeToHighlight.elementRef.current?.scrollIntoView();
751
+ }, 0);
752
+ }
753
+ }
754
+
720
755
  *analyzeMappingModelCoverage(): GeneratorFn<void> {
721
756
  // We will only refetch if the analysis result's mapping has changed.
722
757
  // This makes the assumption that the mapping has not been edited, which is a valid assumption since query is not for editing mappings