Skip to content

Commit d719b48

Browse files
authored
feat(data-modeling): add collection COMPASS-9655 (#7195)
1 parent 6a784a2 commit d719b48

File tree

19 files changed

+1002
-449
lines changed

19 files changed

+1002
-449
lines changed

configs/webpack-config-compass/src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,10 @@ export function createElectronRendererConfig(
276276
writeToDisk: true,
277277
},
278278
client: {
279-
overlay: {
280-
errors: true,
281-
warnings: false,
282-
},
279+
overlay:
280+
process.env.DISABLE_DEVSERVER_OVERLAY === 'true'
281+
? false
282+
: { warnings: false, errors: true, runtimeErrors: true },
283283
},
284284
https: false,
285285
hot: opts.hot,

package-lock.json

Lines changed: 212 additions & 260 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-components/src/components/drawer-portal.spec.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
DrawerContentProvider,
1010
DrawerSection,
1111
DrawerAnchor,
12+
useDrawerState,
13+
useDrawerActions,
1214
} from './drawer-portal';
1315
import { expect } from 'chai';
1416

@@ -162,4 +164,68 @@ describe('DrawerSection', function () {
162164
screen.getByTestId('lg-drawer')
163165
).to.have.attribute('aria-hidden', 'true');
164166
});
167+
168+
it('can control drawer state via the hooks', async function () {
169+
const ControlElement = () => {
170+
const { isDrawerOpen } = useDrawerState();
171+
const { openDrawer, closeDrawer } = useDrawerActions();
172+
return (
173+
<div>
174+
<span data-testid="drawer-state">
175+
{isDrawerOpen ? 'open' : 'closed'}
176+
</span>
177+
<button
178+
data-testid="toggle-drawer"
179+
onClick={
180+
isDrawerOpen
181+
? () => closeDrawer()
182+
: () => openDrawer('controlled-section')
183+
}
184+
>
185+
{isDrawerOpen ? 'Hook Close drawer' : 'Hook Open drawer'}
186+
</button>
187+
</div>
188+
);
189+
};
190+
render(
191+
<DrawerContentProvider>
192+
<ControlElement />
193+
<DrawerAnchor>
194+
<DrawerSection
195+
id="unrelated-section"
196+
label="Test section 1"
197+
title="Test section 1"
198+
glyph="Trash"
199+
>
200+
This is an unrelated section
201+
</DrawerSection>
202+
<DrawerSection
203+
id="controlled-section"
204+
label="Test section 2"
205+
title="Test section 2"
206+
glyph="Bell"
207+
>
208+
This is the controlled section
209+
</DrawerSection>
210+
</DrawerAnchor>
211+
</DrawerContentProvider>
212+
);
213+
214+
// Drawer is closed by default
215+
expect(screen.getByTestId('drawer-state')).to.have.text('closed');
216+
217+
// Open the drawer
218+
userEvent.click(screen.getByRole('button', { name: 'Hook Open drawer' }));
219+
await waitFor(() => {
220+
expect(screen.getByTestId('drawer-state')).to.have.text('open');
221+
expect(screen.getByText('This is the controlled section')).to.be.visible;
222+
});
223+
224+
// Close the drawer
225+
userEvent.click(screen.getByRole('button', { name: 'Hook Close drawer' }));
226+
await waitFor(() => {
227+
expect(screen.getByTestId('drawer-state')).to.have.text('closed');
228+
expect(screen.queryByText('This is the controlled section')).not.to.exist;
229+
});
230+
});
165231
});

packages/compass-components/src/components/drawer-portal.tsx

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ type DrawerSectionProps = Omit<SectionData, 'content' | 'onClick'> & {
3232
order?: number;
3333
};
3434

35+
type DrawerOpenStateContextValue = boolean;
36+
37+
type DrawerSetOpenStateContextValue = (isOpen: boolean) => void;
38+
3539
type DrawerActionsContextValue = {
3640
current: {
3741
openDrawer: (id: string) => void;
@@ -43,6 +47,12 @@ type DrawerActionsContextValue = {
4347

4448
const DrawerStateContext = React.createContext<DrawerSectionProps[]>([]);
4549

50+
const DrawerOpenStateContext =
51+
React.createContext<DrawerOpenStateContextValue>(false);
52+
53+
const DrawerSetOpenStateContext =
54+
React.createContext<DrawerSetOpenStateContextValue>(() => {});
55+
4656
const DrawerActionsContext = React.createContext<DrawerActionsContextValue>({
4757
current: {
4858
openDrawer: () => undefined,
@@ -89,6 +99,8 @@ export const DrawerContentProvider: React.FunctionComponent = ({
8999
children,
90100
}) => {
91101
const [drawerState, setDrawerState] = useState<DrawerSectionProps[]>([]);
102+
const [drawerOpenState, setDrawerOpenState] =
103+
useState<DrawerOpenStateContextValue>(false);
92104
const drawerActions = useRef({
93105
openDrawer: () => undefined,
94106
closeDrawer: () => undefined,
@@ -116,18 +128,26 @@ export const DrawerContentProvider: React.FunctionComponent = ({
116128

117129
return (
118130
<DrawerStateContext.Provider value={drawerState}>
119-
<DrawerActionsContext.Provider value={drawerActions}>
120-
{children}
121-
</DrawerActionsContext.Provider>
131+
<DrawerOpenStateContext.Provider value={drawerOpenState}>
132+
<DrawerSetOpenStateContext.Provider value={setDrawerOpenState}>
133+
<DrawerActionsContext.Provider value={drawerActions}>
134+
{children}
135+
</DrawerActionsContext.Provider>
136+
</DrawerSetOpenStateContext.Provider>
137+
</DrawerOpenStateContext.Provider>
122138
</DrawerStateContext.Provider>
123139
);
124140
};
125141

126142
const DrawerContextGrabber: React.FunctionComponent = ({ children }) => {
127143
const drawerToolbarContext = useDrawerToolbarContext();
128144
const actions = useContext(DrawerActionsContext);
145+
const openStateSetter = useContext(DrawerSetOpenStateContext);
129146
actions.current.openDrawer = drawerToolbarContext.openDrawer;
130147
actions.current.closeDrawer = drawerToolbarContext.closeDrawer;
148+
useEffect(() => {
149+
openStateSetter(drawerToolbarContext.isDrawerOpen);
150+
}, [drawerToolbarContext.isDrawerOpen, openStateSetter]);
131151
return <>{children}</>;
132152
};
133153

@@ -321,3 +341,14 @@ export function useDrawerActions() {
321341
});
322342
return stableActions.current;
323343
}
344+
345+
export const useDrawerState = () => {
346+
const drawerOpenStateContext = useContext(DrawerOpenStateContext);
347+
const drawerState = useContext(DrawerStateContext);
348+
return {
349+
isDrawerOpen:
350+
drawerOpenStateContext &&
351+
// the second check is a workaround, because LG doesn't set isDrawerOpen to false when it's empty
352+
drawerState.length > 0,
353+
};
354+
};

packages/compass-data-modeling/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@mongodb-js/compass-user-data": "^0.9.0",
6464
"@mongodb-js/compass-utils": "^0.9.10",
6565
"@mongodb-js/compass-workspaces": "^0.51.0",
66-
"@mongodb-js/diagramming": "^1.3.3",
66+
"@mongodb-js/diagramming": "^1.3.5",
6767
"bson": "^6.10.4",
6868
"compass-preferences-model": "^2.50.0",
6969
"html-to-image": "1.11.11",

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ function renderDiagramEditorToolbar(
1717
onRedoClick={() => {}}
1818
onExportClick={() => {}}
1919
onRelationshipDrawingToggle={() => {}}
20+
onAddCollectionClick={() => {}}
2021
{...props}
2122
/>
2223
);
@@ -65,6 +66,16 @@ describe('DiagramEditorToolbar', function () {
6566
});
6667
});
6768

69+
context('add collection button', function () {
70+
it('starts adding collection', function () {
71+
const addCollectionSpy = sinon.spy();
72+
renderDiagramEditorToolbar({ onAddCollectionClick: addCollectionSpy });
73+
const addButton = screen.getByRole('button', { name: 'Add Collection' });
74+
userEvent.click(addButton);
75+
expect(addCollectionSpy).to.have.been.calledOnce;
76+
});
77+
});
78+
6879
context('add relationship button', function () {
6980
it('renders it active if isInRelationshipDrawingMode is true', function () {
7081
renderDiagramEditorToolbar({ isInRelationshipDrawingMode: true });

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
transparentize,
1616
Tooltip,
1717
} from '@mongodb-js/compass-components';
18-
18+
import AddCollection from './icons/add-collection';
1919
const containerStyles = css({
2020
display: 'flex',
2121
justifyContent: 'space-between',
@@ -50,6 +50,7 @@ export const DiagramEditorToolbar: React.FunctionComponent<{
5050
onRedoClick: () => void;
5151
onExportClick: () => void;
5252
onRelationshipDrawingToggle: () => void;
53+
onAddCollectionClick: () => void;
5354
}> = ({
5455
step,
5556
hasUndo,
@@ -58,6 +59,7 @@ export const DiagramEditorToolbar: React.FunctionComponent<{
5859
onRedoClick,
5960
onExportClick,
6061
onRelationshipDrawingToggle,
62+
onAddCollectionClick,
6163
isInRelationshipDrawingMode,
6264
}) => {
6365
const darkmode = useDarkMode();
@@ -70,6 +72,15 @@ export const DiagramEditorToolbar: React.FunctionComponent<{
7072
data-testid="diagram-editor-toolbar"
7173
>
7274
<div className={toolbarGroupStyles}>
75+
<IconButton aria-label="Undo" disabled={!hasUndo} onClick={onUndoClick}>
76+
<Icon glyph="Undo"></Icon>
77+
</IconButton>
78+
<IconButton aria-label="Redo" disabled={!hasRedo} onClick={onRedoClick}>
79+
<Icon glyph="Redo"></Icon>
80+
</IconButton>
81+
<IconButton aria-label="Add Collection" onClick={onAddCollectionClick}>
82+
<AddCollection />
83+
</IconButton>
7384
<Tooltip
7485
trigger={
7586
<IconButton
@@ -88,12 +99,6 @@ export const DiagramEditorToolbar: React.FunctionComponent<{
8899
>
89100
Drag from one collection to another to create a relationship.
90101
</Tooltip>
91-
<IconButton aria-label="Undo" disabled={!hasUndo} onClick={onUndoClick}>
92-
<Icon glyph="Undo"></Icon>
93-
</IconButton>
94-
<IconButton aria-label="Redo" disabled={!hasRedo} onClick={onRedoClick}>
95-
<Icon glyph="Redo"></Icon>
96-
</IconButton>
97102
</div>
98103
<div className={toolbarGroupStyles}>
99104
<Button size="xsmall" aria-label="Export" onClick={onExportClick}>

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import sinon from 'sinon';
1616
import { DiagramProvider } from '@mongodb-js/diagramming';
1717
import { DataModelingWorkspaceTab } from '..';
1818
import { openDiagram } from '../store/diagram';
19+
import { DrawerAnchor } from '@mongodb-js/compass-components';
1920

2021
const storageItems: MongoDBDataModelDescription[] = [
2122
{
@@ -143,9 +144,11 @@ const renderDiagramEditor = ({
143144
const {
144145
plugin: { store },
145146
} = renderWithConnections(
146-
<DiagramProvider fitView>
147-
<DiagramEditor />
148-
</DiagramProvider>
147+
<DrawerAnchor>
148+
<DiagramProvider fitView>
149+
<DiagramEditor />
150+
</DiagramProvider>
151+
</DrawerAnchor>
149152
);
150153
store.dispatch(openDiagram(renderedItem));
151154

0 commit comments

Comments
 (0)