Skip to content

Commit abb53b2

Browse files
authored
chore(data-modeling): move export state to redux and add telemetry COMPASS-9523 (#7092)
* move export state to redux * add telemetry * handle rejecting * controller in args and add comment for rafraf
1 parent 4af9c82 commit abb53b2

File tree

9 files changed

+260
-131
lines changed

9 files changed

+260
-131
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from 'react';
22
import { connect } from 'react-redux';
33
import type { DataModelingState } from '../store/reducer';
4-
import { redoEdit, showExportModal, undoEdit } from '../store/diagram';
4+
import { redoEdit, undoEdit } from '../store/diagram';
5+
import { showExportModal } from '../store/export-diagram';
56
import { Icon, IconButton } from '@mongodb-js/compass-components';
67

78
export const DiagramEditorToolbar: React.FunctionComponent<{
Lines changed: 32 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useEffect, useRef, useState } from 'react';
1+
import React from 'react';
22
import {
33
Button,
44
css,
@@ -13,19 +13,17 @@ import {
1313
RadioGroup,
1414
spacing,
1515
SpinLoader,
16-
openToast,
1716
} from '@mongodb-js/compass-components';
17+
import type { ExportDiagramFormat } from '../store/export-diagram';
1818
import {
1919
closeExportModal,
20-
selectCurrentModel,
21-
getCurrentDiagramFromState,
22-
} from '../store/diagram';
20+
exportDiagram,
21+
selectFormat,
22+
} from '../store/export-diagram';
2323
import { connect } from 'react-redux';
2424
import type { DataModelingState } from '../store/reducer';
25-
import type { StaticModel } from '../services/data-model-storage';
26-
import { exportToJson, exportToPng } from '../services/export-diagram';
2725
import { useDiagram } from '@mongodb-js/diagramming';
28-
import { isCancelError } from '@mongodb-js/compass-utils';
26+
import type { DiagramInstance } from '@mongodb-js/diagramming';
2927

3028
const nbsp = '\u00a0';
3129

@@ -52,79 +50,27 @@ const footerStyles = css({
5250

5351
type ExportDiagramModalProps = {
5452
isModalOpen: boolean;
55-
diagramLabel: string;
56-
model: StaticModel | null;
53+
isExporting: boolean;
54+
exportFormat?: ExportDiagramFormat;
55+
onExportDiagram: (diagramInstance: DiagramInstance) => void;
56+
onSelectFormat: (format: ExportDiagramFormat) => void;
5757
onCloseClick: () => void;
5858
};
5959

6060
const ExportDiagramModal = ({
6161
isModalOpen,
62-
diagramLabel,
63-
model,
62+
isExporting,
63+
exportFormat,
64+
onExportDiagram,
65+
onSelectFormat,
6466
onCloseClick,
6567
}: ExportDiagramModalProps) => {
66-
const [exportFormat, setExportFormat] = useState<'png' | 'json' | null>(null);
6768
const diagram = useDiagram();
68-
const [isExporting, setIsExporting] = useState(false);
69-
const abortControllerRef = useRef<AbortController | null>(null);
70-
useEffect(() => {
71-
const cleanup = () => {
72-
if (abortControllerRef.current) {
73-
abortControllerRef.current.abort();
74-
abortControllerRef.current = null;
75-
}
76-
};
77-
const abortController = new AbortController();
78-
if (isModalOpen) {
79-
abortControllerRef.current = abortController;
80-
} else {
81-
cleanup();
82-
}
83-
return cleanup;
84-
}, [isModalOpen]);
85-
86-
const onClose = useCallback(() => {
87-
setIsExporting(false);
88-
abortControllerRef.current?.abort();
89-
abortControllerRef.current = null;
90-
onCloseClick();
91-
}, [onCloseClick]);
92-
93-
const onExport = useCallback(async () => {
94-
try {
95-
if (!exportFormat || !model) {
96-
return;
97-
}
98-
setIsExporting(true);
99-
if (exportFormat === 'json') {
100-
exportToJson(diagramLabel, model);
101-
} else if (exportFormat === 'png') {
102-
await exportToPng(
103-
diagramLabel,
104-
diagram,
105-
abortControllerRef.current?.signal
106-
);
107-
}
108-
} catch (error) {
109-
if (isCancelError(error)) {
110-
return;
111-
}
112-
openToast('export-diagram-error', {
113-
variant: 'warning',
114-
title: 'Export failed',
115-
description: `An error occurred while exporting the diagram: ${
116-
(error as Error).message
117-
}`,
118-
});
119-
} finally {
120-
onClose();
121-
}
122-
}, [exportFormat, onClose, model, diagram, diagramLabel]);
12369

12470
return (
12571
<Modal
12672
open={isModalOpen}
127-
setOpen={onClose}
73+
setOpen={onCloseClick}
12874
data-testid="export-diagram-modal"
12975
>
13076
<ModalHeader
@@ -153,7 +99,7 @@ const ExportDiagramModal = ({
15399
checked={exportFormat === 'png'}
154100
value="png"
155101
aria-label="PNG"
156-
onClick={() => setExportFormat('png')}
102+
onClick={() => onSelectFormat('png')}
157103
>
158104
PNG
159105
</Radio>
@@ -164,7 +110,7 @@ const ExportDiagramModal = ({
164110
checked={exportFormat === 'json'}
165111
value="json"
166112
aria-label="JSON"
167-
onClick={() => setExportFormat('json')}
113+
onClick={() => onSelectFormat('json')}
168114
>
169115
JSON
170116
</Radio>
@@ -175,15 +121,19 @@ const ExportDiagramModal = ({
175121
<ModalFooter className={footerStyles}>
176122
<Button
177123
variant="primary"
178-
onClick={() => void onExport()}
124+
onClick={() => onExportDiagram(diagram)}
179125
data-testid="export-button"
180-
disabled={!exportFormat || !model}
126+
disabled={!exportFormat}
181127
loadingIndicator={<SpinLoader />}
182128
isLoading={isExporting}
183129
>
184130
Export
185131
</Button>
186-
<Button variant="default" onClick={onClose} data-testid="cancel-button">
132+
<Button
133+
variant="default"
134+
onClick={onCloseClick}
135+
data-testid="cancel-button"
136+
>
187137
Cancel
188138
</Button>
189139
</ModalFooter>
@@ -193,17 +143,18 @@ const ExportDiagramModal = ({
193143

194144
export default connect(
195145
(state: DataModelingState) => {
196-
const { diagram } = state;
197-
const model = diagram
198-
? selectCurrentModel(getCurrentDiagramFromState(state))
199-
: null;
146+
const {
147+
exportDiagram: { isExporting, isModalOpen, exportFormat },
148+
} = state;
200149
return {
201-
model,
202-
diagramLabel: diagram?.name ?? 'Schema Preview',
203-
isModalOpen: Boolean(diagram?.isExportModalOpen),
150+
isModalOpen,
151+
isExporting,
152+
exportFormat,
204153
};
205154
},
206155
{
207156
onCloseClick: closeExportModal,
157+
onSelectFormat: selectFormat,
158+
onExportDiagram: exportDiagram,
208159
}
209160
)(ExportDiagramModal);

packages/compass-data-modeling/src/services/export-diagram.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export async function exportToPng(
4646
}
4747

4848
export function getExportPngDataUri(diagram: DiagramInstance): Promise<string> {
49-
return new Promise<string>((resolve, _reject) => {
49+
return new Promise<string>((resolve, reject) => {
5050
const bounds = getNodesBounds(diagram.getNodes());
5151

5252
const container = document.createElement('div');
@@ -65,11 +65,6 @@ export function getExportPngDataUri(diagram: DiagramInstance): Promise<string> {
6565
selected: false, // Dont show selected state (blue border)
6666
}));
6767

68-
const reject = (error: Error) => {
69-
document.body.removeChild(container);
70-
_reject(error);
71-
};
72-
7368
ReactDOM.render(
7469
<DiagramProvider>
7570
<Diagram
@@ -80,6 +75,9 @@ export function getExportPngDataUri(diagram: DiagramInstance): Promise<string> {
8075
</DiagramProvider>,
8176
container,
8277
() => {
78+
// We skip some frames here to ensure that the DOM has fully rendered and React has
79+
// committed all updates before we try to query for viewport element. Without this,
80+
// the element may not exist yet or may not have the correct styles etc.
8381
rafraf(() => {
8482
// For export we are selecting react-flow__viewport element,
8583
// which contains the export canvas. It excludes diagram
@@ -91,6 +89,7 @@ export function getExportPngDataUri(diagram: DiagramInstance): Promise<string> {
9189
'.react-flow__viewport'
9290
);
9391
if (!viewportElement) {
92+
document.body.removeChild(container);
9493
return reject(new Error('Diagram element not found'));
9594
}
9695

packages/compass-data-modeling/src/store/analysis-process.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export function startAnalysis(
144144
const namespaces = collections.map((collName) => {
145145
return `${database}.${collName}`;
146146
});
147-
const cancelController = (services.cancelControllerRef.current =
147+
const cancelController = (services.cancelAnalysisControllerRef.current =
148148
new AbortController());
149149
dispatch({
150150
type: AnalysisProcessActionTypes.ANALYZING_COLLECTIONS_START,
@@ -223,7 +223,7 @@ export function startAnalysis(
223223
});
224224
}
225225
} finally {
226-
services.cancelControllerRef.current = null;
226+
services.cancelAnalysisControllerRef.current = null;
227227
}
228228
};
229229
}
@@ -250,8 +250,8 @@ export function retryAnalysis(): DataModelingThunkAction<void, never> {
250250
}
251251

252252
export function cancelAnalysis(): DataModelingThunkAction<void, never> {
253-
return (_dispatch, _getState, { cancelControllerRef }) => {
254-
cancelControllerRef.current?.abort();
255-
cancelControllerRef.current = null;
253+
return (_dispatch, _getState, { cancelAnalysisControllerRef }) => {
254+
cancelAnalysisControllerRef.current?.abort();
255+
cancelAnalysisControllerRef.current = null;
256256
};
257257
}

packages/compass-data-modeling/src/store/diagram.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export type DiagramState =
2424
next: Edit[][];
2525
};
2626
editErrors?: string[];
27-
isExportModalOpen?: boolean;
2827
})
2928
| null; // null when no diagram is currently open
3029

@@ -37,8 +36,6 @@ export enum DiagramActionTypes {
3736
APPLY_EDIT_FAILED = 'data-modeling/diagram/APPLY_EDIT_FAILED',
3837
UNDO_EDIT = 'data-modeling/diagram/UNDO_EDIT',
3938
REDO_EDIT = 'data-modeling/diagram/REDO_EDIT',
40-
EXPORT_MODAL_OPENED = 'data-modeling/diagram/EXPORT_MODAL_OPENED',
41-
EXPORT_MODAL_CLOSED = 'data-modeling/diagram/EXPORT_MODAL_CLOSED',
4239
}
4340

4441
export type OpenDiagramAction = {
@@ -80,14 +77,6 @@ export type RedoEditAction = {
8077
type: DiagramActionTypes.REDO_EDIT;
8178
};
8279

83-
type ExportModalOpenedAction = {
84-
type: DiagramActionTypes.EXPORT_MODAL_OPENED;
85-
};
86-
87-
type ExportModalClosedAction = {
88-
type: DiagramActionTypes.EXPORT_MODAL_CLOSED;
89-
};
90-
9180
export type DiagramActions =
9281
| OpenDiagramAction
9382
| DeleteDiagramAction
@@ -231,18 +220,6 @@ export const diagramReducer: Reducer<DiagramState> = (
231220
updatedAt: new Date().toISOString(),
232221
};
233222
}
234-
if (isAction(action, DiagramActionTypes.EXPORT_MODAL_OPENED)) {
235-
return {
236-
...state,
237-
isExportModalOpen: true,
238-
};
239-
}
240-
if (isAction(action, DiagramActionTypes.EXPORT_MODAL_CLOSED)) {
241-
return {
242-
...state,
243-
isExportModalOpen: false,
244-
};
245-
}
246223
return state;
247224
};
248225

@@ -452,12 +429,4 @@ export function getCurrentDiagramFromState(
452429
return { id, connectionId, name, edits, createdAt, updatedAt };
453430
}
454431

455-
export function showExportModal(): ExportModalOpenedAction {
456-
return { type: DiagramActionTypes.EXPORT_MODAL_OPENED };
457-
}
458-
459-
export function closeExportModal(): ExportModalClosedAction {
460-
return { type: DiagramActionTypes.EXPORT_MODAL_CLOSED };
461-
}
462-
463432
export const selectCurrentModel = memoize(getCurrentModel);

0 commit comments

Comments
 (0)