Skip to content

Commit 0eaeb31

Browse files
committed
feat(data-modeling): node selection and relationship editing store and actions
1 parent 3a41616 commit 0eaeb31

File tree

10 files changed

+901
-67
lines changed

10 files changed

+901
-67
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import type { Relationship } from '../services/data-model-storage';
4+
import { Button, H3 } from '@mongodb-js/compass-components';
5+
import {
6+
deleteRelationship,
7+
getCurrentDiagramFromState,
8+
selectCurrentModel,
9+
} from '../store/diagram';
10+
import type { DataModelingState } from '../store/reducer';
11+
import {
12+
createNewRelationship,
13+
startRelationshipEdit,
14+
} from '../store/side-panel';
15+
import RelationshipDrawerContent from './relationship-drawer-content';
16+
17+
type CollectionDrawerContentProps = {
18+
namespace: string;
19+
relationships: Relationship[];
20+
shouldShowRelationshipEditingForm?: boolean;
21+
onCreateNewRelationshipClick: (namespace: string) => void;
22+
onEditRelationshipClick: (relationship: Relationship) => void;
23+
onDeleteRelationshipClick: (rId: string) => void;
24+
};
25+
26+
const CollectionDrawerContent: React.FunctionComponent<
27+
CollectionDrawerContentProps
28+
> = ({
29+
namespace,
30+
relationships,
31+
shouldShowRelationshipEditingForm,
32+
onCreateNewRelationshipClick,
33+
onEditRelationshipClick,
34+
onDeleteRelationshipClick,
35+
}) => {
36+
if (shouldShowRelationshipEditingForm) {
37+
return <RelationshipDrawerContent></RelationshipDrawerContent>;
38+
}
39+
40+
return (
41+
<>
42+
<H3>{namespace}</H3>
43+
<ul>
44+
{relationships.map((r) => {
45+
return (
46+
<li key={r.id}>
47+
{r.relationship[0].fields.join(', ')}&nbsp;-&gt;&nbsp;
48+
{r.relationship[1].fields.join(', ')}
49+
<Button
50+
onClick={() => {
51+
onEditRelationshipClick(r);
52+
}}
53+
>
54+
Edit
55+
</Button>
56+
<Button
57+
onClick={() => {
58+
onDeleteRelationshipClick(r.id);
59+
}}
60+
>
61+
Delete
62+
</Button>
63+
</li>
64+
);
65+
})}
66+
</ul>
67+
<Button
68+
onClick={() => {
69+
onCreateNewRelationshipClick(namespace);
70+
}}
71+
>
72+
Add relationship manually
73+
</Button>
74+
</>
75+
);
76+
};
77+
78+
export default connect(
79+
(state: DataModelingState, ownProps: { namespace: string }) => {
80+
return {
81+
relationships: selectCurrentModel(
82+
getCurrentDiagramFromState(state).edits
83+
).relationships.filter((r) => {
84+
const [local, foreign] = r.relationship;
85+
return (
86+
local.ns === ownProps.namespace || foreign.ns === ownProps.namespace
87+
);
88+
}),
89+
shouldShowRelationshipEditingForm:
90+
state.sidePanel.viewType === 'relationship-editing',
91+
};
92+
},
93+
{
94+
onCreateNewRelationshipClick: createNewRelationship,
95+
onEditRelationshipClick: startRelationshipEdit,
96+
onDeleteRelationshipClick: deleteRelationship,
97+
}
98+
)(CollectionDrawerContent);

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

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,15 @@ import {
66
Button,
77
css,
88
cx,
9-
Body,
10-
spacing,
119
palette,
1210
useDarkMode,
1311
} from '@mongodb-js/compass-components';
12+
import CollectionDrawerContent from './collection-drawer-content';
13+
import RelationshipDrawerContent from './relationship-drawer-content';
1414

1515
const containerStyles = css({
1616
width: '400px',
1717
height: '100%',
18-
19-
display: 'flex',
20-
flexDirection: 'column',
21-
alignItems: 'center',
22-
justifyContent: 'center',
23-
gap: spacing[400],
2418
borderLeft: `1px solid ${palette.gray.light2}`,
2519
});
2620

@@ -29,21 +23,35 @@ const darkModeContainerStyles = css({
2923
});
3024

3125
type DiagramEditorSidePanelProps = {
32-
isOpen: boolean;
26+
selectedItems: { type: 'relationship' | 'collection'; id: string } | null;
3327
onClose: () => void;
3428
};
3529

3630
function DiagmramEditorSidePanel({
37-
isOpen,
31+
selectedItems,
3832
onClose,
3933
}: DiagramEditorSidePanelProps) {
4034
const isDarkMode = useDarkMode();
41-
if (!isOpen) {
35+
36+
if (!selectedItems) {
4237
return null;
4338
}
39+
40+
let content;
41+
42+
if (selectedItems.type === 'collection') {
43+
content = (
44+
<CollectionDrawerContent
45+
namespace={selectedItems.id}
46+
></CollectionDrawerContent>
47+
);
48+
} else if (selectedItems.type === 'relationship') {
49+
content = <RelationshipDrawerContent></RelationshipDrawerContent>;
50+
}
51+
4452
return (
4553
<div className={cx(containerStyles, isDarkMode && darkModeContainerStyles)}>
46-
<Body>This feature is under development.</Body>
54+
{content}
4755
<Button onClick={onClose} variant="primary" size="small">
4856
Close Side Panel
4957
</Button>
@@ -53,9 +61,8 @@ function DiagmramEditorSidePanel({
5361

5462
export default connect(
5563
(state: DataModelingState) => {
56-
const { sidePanel } = state;
5764
return {
58-
isOpen: sidePanel.isOpen,
65+
selectedItems: state.diagram?.selectedItems ?? null,
5966
};
6067
},
6168
{

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

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
moveCollection,
1515
getCurrentDiagramFromState,
1616
selectCurrentModel,
17+
selectCollection,
18+
selectRelationship,
1719
} from '../store/diagram';
1820
import {
1921
Banner,
@@ -38,7 +40,6 @@ import type { StaticModel } from '../services/data-model-storage';
3840
import DiagramEditorToolbar from './diagram-editor-toolbar';
3941
import ExportDiagramModal from './export-diagram-modal';
4042
import { useLogger } from '@mongodb-js/compass-logging/provider';
41-
import { openSidePanel } from '../store/side-panel';
4243

4344
const loadingContainerStyles = css({
4445
width: '100%',
@@ -189,7 +190,9 @@ const DiagramEditor: React.FunctionComponent<{
189190
onCancelClick: () => void;
190191
onApplyInitialLayout: (positions: Record<string, [number, number]>) => void;
191192
onMoveCollection: (ns: string, newPosition: [number, number]) => void;
192-
onOpenSidePanel: () => void;
193+
onCollectionSelect: (namespace: string) => void;
194+
onRelationshipSelect: (rId: string) => void;
195+
selectedItem?: string | null;
193196
}> = ({
194197
diagramLabel,
195198
step,
@@ -198,7 +201,9 @@ const DiagramEditor: React.FunctionComponent<{
198201
onCancelClick,
199202
onApplyInitialLayout,
200203
onMoveCollection,
201-
onOpenSidePanel,
204+
onCollectionSelect,
205+
onRelationshipSelect,
206+
selectedItem,
202207
}) => {
203208
const { log, mongoLogId } = useLogger('COMPASS-DATA-MODELING-DIAGRAM-EDITOR');
204209
const isDarkMode = useDarkMode();
@@ -226,9 +231,10 @@ const DiagramEditor: React.FunctionComponent<{
226231
target: target.ns,
227232
markerStart: source.cardinality === 1 ? 'one' : 'many',
228233
markerEnd: target.cardinality === 1 ? 'one' : 'many',
234+
selected: selectedItem === relationship.id,
229235
};
230236
});
231-
}, [model?.relationships]);
237+
}, [model?.relationships, selectedItem]);
232238

233239
const nodes = useMemo<NodeProps[]>(() => {
234240
return (model?.collections ?? []).map(
@@ -241,9 +247,10 @@ const DiagramEditor: React.FunctionComponent<{
241247
},
242248
title: toNS(coll.ns).collection,
243249
fields: getFieldsFromSchema(coll.jsonSchema),
250+
selected: selectedItem === coll.ns,
244251
})
245252
);
246-
}, [model?.collections]);
253+
}, [model?.collections, selectedItem]);
247254

248255
const applyInitialLayout = useCallback(async () => {
249256
try {
@@ -335,9 +342,19 @@ const DiagramEditor: React.FunctionComponent<{
335342
title={diagramLabel}
336343
edges={edges}
337344
nodes={areNodesReady ? nodes : []}
338-
onEdgeClick={() => {
339-
// TODO: we have to open a side panel with edge details
340-
onOpenSidePanel();
345+
// With threshold too low clicking sometimes gets confused with
346+
// dragging
347+
// @ts-expect-error expose this prop from the component
348+
nodeDragThreshold={3}
349+
// @ts-expect-error expose this prop from the component
350+
onNodeClick={(_evt, node) => {
351+
if (node.type !== 'collection') {
352+
return;
353+
}
354+
onCollectionSelect(node.id);
355+
}}
356+
onEdgeClick={(_evt, edge) => {
357+
onRelationshipSelect(edge.id);
341358
}}
342359
fitViewOptions={{
343360
maxZoom: 1,
@@ -366,17 +383,21 @@ export default connect(
366383
return {
367384
step: step,
368385
model: diagram
369-
? selectCurrentModel(getCurrentDiagramFromState(state))
386+
? selectCurrentModel(getCurrentDiagramFromState(state).edits)
370387
: null,
371388
editErrors: diagram?.editErrors,
372389
diagramLabel: diagram?.name || 'Schema Preview',
390+
selectedItem: state.diagram?.selectedItems
391+
? state.diagram.selectedItems.id
392+
: null,
373393
};
374394
},
375395
{
376396
onRetryClick: retryAnalysis,
377397
onCancelClick: cancelAnalysis,
378398
onApplyInitialLayout: applyInitialLayout,
379399
onMoveCollection: moveCollection,
380-
onOpenSidePanel: openSidePanel,
400+
onCollectionSelect: selectCollection,
401+
onRelationshipSelect: selectRelationship,
381402
}
382403
)(DiagramEditor);

0 commit comments

Comments
 (0)