@finos/legend-query-builder 4.14.8 → 4.14.9

Sign up to get free protection for your applications and to get access to all the features.
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