Skip to content

Commit 0d8a023

Browse files
committed
finish highlighting, relationship management, add file
1 parent 4c1863f commit 0d8a023

File tree

7 files changed

+400
-35
lines changed

7 files changed

+400
-35
lines changed

packages/compass-data-modeling/src/components/diagram-editor.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import ExportDiagramModal from './export-diagram-modal';
4141
import { DATA_MODELING_DRAWER_ID } from './drawer/diagram-editor-side-panel';
4242
import {
4343
collectionToDiagramNode,
44-
getSelectedFields,
44+
getHighlightedFields,
4545
relationshipToDiagramEdge,
4646
} from '../utils/nodes-and-edges';
4747

@@ -114,7 +114,13 @@ const DiagramContent: React.FunctionComponent<{
114114
onFieldSelect: (namespace: string, fieldPath: FieldPath) => void;
115115
onDiagramBackgroundClicked: () => void;
116116
selectedItems: SelectedItems;
117-
onCreateNewRelationship: (source: string, target: string) => void;
117+
onCreateNewRelationship: ({
118+
localNamespace,
119+
foreignNamespace,
120+
}: {
121+
localNamespace: string;
122+
foreignNamespace: string;
123+
}) => void;
118124
onRelationshipDrawn: () => void;
119125
}> = ({
120126
diagramLabel,
@@ -151,7 +157,7 @@ const DiagramContent: React.FunctionComponent<{
151157
}, [model?.relationships, selectedItems]);
152158

153159
const nodes = useMemo<NodeProps[]>(() => {
154-
const selectedFields = getSelectedFields(
160+
const highlightedFields = getHighlightedFields(
155161
selectedItems,
156162
model?.relationships
157163
);
@@ -161,7 +167,11 @@ const DiagramContent: React.FunctionComponent<{
161167
selectedItems.type === 'collection' &&
162168
selectedItems.id === coll.ns;
163169
return collectionToDiagramNode(coll, {
164-
selectedFields,
170+
highlightedFields,
171+
selectedField:
172+
selectedItems?.type === 'field' && selectedItems.namespace === coll.ns
173+
? selectedItems.fieldPath
174+
: undefined,
165175
selected,
166176
isInRelationshipDrawingMode,
167177
});
@@ -188,7 +198,10 @@ const DiagramContent: React.FunctionComponent<{
188198

189199
const handleNodesConnect = useCallback(
190200
(source: string, target: string) => {
191-
onCreateNewRelationship(source, target);
201+
onCreateNewRelationship({
202+
localNamespace: source,
203+
foreignNamespace: target,
204+
});
192205
onRelationshipDrawn();
193206
},
194207
[onRelationshipDrawn, onCreateNewRelationship]
@@ -210,7 +223,6 @@ const DiagramContent: React.FunctionComponent<{
210223
// dragging
211224
nodeDragThreshold={5}
212225
onNodeClick={(_evt, node) => {
213-
console.log('onNodeClick', node);
214226
if (node.type !== 'collection') {
215227
return;
216228
}

packages/compass-data-modeling/src/components/drawer/collection-drawer-content.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ type CollectionDrawerContentProps = {
2525
namespaces: string[];
2626
note?: string;
2727
relationships: Relationship[];
28-
onCreateNewRelationshipClick: (namespace: string) => void;
28+
onCreateNewRelationshipClick: ({
29+
localNamespace,
30+
}: {
31+
localNamespace: string;
32+
}) => void;
2933
onEditRelationshipClick: (rId: string) => void;
3034
onDeleteRelationshipClick: (rId: string) => void;
3135
onNoteChange: (namespace: string, note: string) => void;
@@ -121,9 +125,10 @@ const CollectionDrawerContent: React.FunctionComponent<
121125

122126
<RelationshipsSection
123127
relationships={relationships}
128+
emptyMessage="This collection does not have any relationships yet."
124129
getRelationshipLabel={getDefaultRelationshipName}
125130
onCreateNewRelationshipClick={() => {
126-
onCreateNewRelationshipClick(namespace);
131+
onCreateNewRelationshipClick({ localNamespace: namespace });
127132
}}
128133
onEditRelationshipClick={onEditRelationshipClick}
129134
onDeleteRelationshipClick={onDeleteRelationshipClick}

packages/compass-data-modeling/src/components/drawer/diagram-editor-side-panel.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useMemo } from 'react';
22
import { connect } from 'react-redux';
3+
import toNS from 'mongodb-ns';
34
import type { DataModelingState } from '../../store/reducer';
45
import {
56
css,
@@ -41,6 +42,8 @@ type DiagramEditorSidePanelProps = {
4142
onDeleteField: (ns: string, fieldPath: FieldPath) => void;
4243
};
4344

45+
const getCollection = (namespace: string) => toNS(namespace).collection;
46+
4447
function DiagramEditorSidePanel({
4548
selectedItems,
4649
onDeleteCollection,
@@ -183,7 +186,7 @@ export default connect(
183186
return {
184187
selectedItems: {
185188
...selected,
186-
label: selected.id,
189+
label: getCollection(selected.id),
187190
},
188191
};
189192
}
@@ -224,7 +227,9 @@ export default connect(
224227
return {
225228
selectedItems: {
226229
...selected,
227-
label: selected.fieldPath.join('.'),
230+
label: `${getCollection(
231+
selected.namespace
232+
)}.${selected.fieldPath.join('.')}`,
228233
},
229234
};
230235
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import React, { useMemo } from 'react';
2+
import { connect } from 'react-redux';
3+
import type {
4+
FieldPath,
5+
FieldSchema,
6+
Relationship,
7+
} from '../../services/data-model-storage';
8+
import { TextInput } from '@mongodb-js/compass-components';
9+
import toNS from 'mongodb-ns';
10+
import {
11+
createNewRelationship,
12+
deleteRelationship,
13+
selectCurrentModelFromState,
14+
selectRelationship,
15+
} from '../../store/diagram';
16+
import type { DataModelingState } from '../../store/reducer';
17+
import {
18+
DMDrawerSection,
19+
DMFormFieldContainer,
20+
} from './drawer-section-components';
21+
import { useChangeOnBlur } from './use-change-on-blur';
22+
import { RelationshipsSection } from './relationships-section';
23+
24+
type FieldDrawerContentProps = {
25+
namespace: string;
26+
fieldPath: FieldPath;
27+
fieldPaths: FieldPath[];
28+
jsonSchema: FieldSchema;
29+
relationships: Relationship[];
30+
onCreateNewRelationshipClick: ({
31+
localNamespace,
32+
localFields,
33+
}: {
34+
localNamespace: string;
35+
localFields: FieldPath;
36+
}) => void;
37+
onEditRelationshipClick: (rId: string) => void;
38+
onDeleteRelationshipClick: (rId: string) => void;
39+
onRenameField: (
40+
namespace: string,
41+
fromFieldPath: FieldPath,
42+
toFieldPath: FieldPath
43+
) => void;
44+
onChangeFieldType: (
45+
namespace: string,
46+
fieldPath: FieldPath,
47+
fromBsonType: string,
48+
toBsonType: string
49+
) => void;
50+
};
51+
52+
export function getIsFieldNameValid(
53+
currentFieldPath: FieldPath,
54+
existingFields: FieldPath[],
55+
newName: string
56+
): {
57+
isValid: boolean;
58+
errorMessage?: string;
59+
} {
60+
const trimmedName = newName.trim();
61+
if (!trimmedName.length) {
62+
return {
63+
isValid: false,
64+
errorMessage: 'Field name cannot be empty.',
65+
};
66+
}
67+
68+
const fieldsNamesWithoutCurrent = existingFields
69+
.filter(
70+
(fieldPath) =>
71+
JSON.stringify(fieldPath) !== JSON.stringify(currentFieldPath)
72+
)
73+
.map((fieldPath) => fieldPath[fieldPath.length - 1]);
74+
75+
const isDuplicate = fieldsNamesWithoutCurrent.some(
76+
(fieldName) => fieldName === trimmedName
77+
);
78+
79+
return {
80+
isValid: !isDuplicate,
81+
errorMessage: isDuplicate ? 'Field already exists.' : undefined,
82+
};
83+
}
84+
85+
const FieldDrawerContent: React.FunctionComponent<FieldDrawerContentProps> = ({
86+
namespace,
87+
fieldPath,
88+
fieldPaths,
89+
jsonSchema,
90+
relationships,
91+
onCreateNewRelationshipClick,
92+
onEditRelationshipClick,
93+
onDeleteRelationshipClick,
94+
onRenameField,
95+
onChangeFieldType,
96+
}) => {
97+
const { value: fieldName, ...nameInputProps } = useChangeOnBlur(
98+
fieldPath[fieldPath.length - 1],
99+
(fieldName) => {
100+
const trimmedName = fieldName.trim();
101+
if (trimmedName === fieldName) {
102+
return;
103+
}
104+
if (!isFieldNameValid) {
105+
return;
106+
}
107+
onRenameField(namespace, fieldPath, [
108+
...fieldPath.slice(0, fieldPath.length - 1),
109+
trimmedName,
110+
]);
111+
}
112+
);
113+
114+
const { isValid: isFieldNameValid, errorMessage: fieldNameEditErrorMessage } =
115+
useMemo(
116+
() => getIsFieldNameValid(fieldPath, fieldPaths, fieldName),
117+
[fieldPath, fieldPaths, fieldName]
118+
);
119+
120+
return (
121+
<>
122+
<DMDrawerSection label="Field properties">
123+
<DMFormFieldContainer>
124+
<TextInput
125+
label="Field name"
126+
data-testid="data-model-collection-drawer-name-input"
127+
sizeVariant="small"
128+
value={fieldName}
129+
{...nameInputProps}
130+
state={isFieldNameValid ? undefined : 'error'}
131+
errorMessage={fieldNameEditErrorMessage}
132+
/>
133+
</DMFormFieldContainer>
134+
</DMDrawerSection>
135+
136+
<RelationshipsSection
137+
relationships={relationships}
138+
emptyMessage="This field does not have any relationships yet."
139+
getRelationshipLabel={([local, foreign]) => {
140+
const labelField =
141+
local.ns === namespace &&
142+
JSON.stringify(local.fields) === JSON.stringify(fieldPath)
143+
? foreign
144+
: local;
145+
return [
146+
labelField.ns ? toNS(labelField.ns).collection : '',
147+
labelField.fields?.join('.'),
148+
].join('.');
149+
}}
150+
onCreateNewRelationshipClick={() => {
151+
onCreateNewRelationshipClick({
152+
localNamespace: namespace,
153+
localFields: fieldPath,
154+
});
155+
}}
156+
onEditRelationshipClick={onEditRelationshipClick}
157+
onDeleteRelationshipClick={onDeleteRelationshipClick}
158+
/>
159+
</>
160+
);
161+
};
162+
163+
export default connect(
164+
(
165+
state: DataModelingState,
166+
ownProps: { namespace: string; fieldPath: FieldPath }
167+
) => {
168+
const model = selectCurrentModelFromState(state);
169+
return {
170+
jsonSchema: {}, // TODO get field schema
171+
fieldPaths: [], // TODO get field paths
172+
relationships: model.relationships.filter((r) => {
173+
const [local, foreign] = r.relationship;
174+
return (
175+
(local.ns === ownProps.namespace &&
176+
JSON.stringify(local.fields) ===
177+
JSON.stringify(ownProps.fieldPath)) ||
178+
(foreign.ns === ownProps.namespace &&
179+
JSON.stringify(foreign.fields) ===
180+
JSON.stringify(ownProps.fieldPath))
181+
);
182+
}),
183+
};
184+
},
185+
{
186+
onCreateNewRelationshipClick: createNewRelationship,
187+
onEditRelationshipClick: selectRelationship,
188+
onDeleteRelationshipClick: deleteRelationship,
189+
onRenameField: () => {}, // TODO: renameField,
190+
onChangeFieldType: () => {}, // TODO: updateFieldSchema,
191+
}
192+
)(FieldDrawerContent);

0 commit comments

Comments
 (0)