Skip to content

Commit 6860c4c

Browse files
authored
feat(data-modeling): add telemetry and align design COMPASS-9594 (#7139)
* move download diagram to export modal * align toolbar with designs * add telemetry * clean up * fix e2e tests * box shadow * fix typo * use polished transparentize util
1 parent fdc453a commit 6860c4c

File tree

10 files changed

+134
-76
lines changed

10 files changed

+134
-76
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React, { useMemo } from 'react';
2+
import { useDarkMode } from '../../hooks/use-theme';
3+
import { palette } from '@leafygreen-ui/palette';
4+
5+
export function PngIcon() {
6+
const darkMode = useDarkMode();
7+
8+
const fillColor = useMemo(
9+
() => (darkMode ? palette.white : palette.black),
10+
[darkMode]
11+
);
12+
13+
return (
14+
<svg
15+
width="16"
16+
height="16"
17+
viewBox="0 0 16 16"
18+
fill="none"
19+
xmlns="http://www.w3.org/2000/svg"
20+
>
21+
<path
22+
d="M3.5 8.75H2.5C2.30109 8.75 2.11032 8.82902 1.96967 8.96967C1.82902 9.11032 1.75 9.30109 1.75 9.5V13C1.75 13.1989 1.82902 13.3897 1.96967 13.5303C2.11032 13.671 2.30109 13.75 2.5 13.75C2.69891 13.75 2.88968 13.671 3.03033 13.5303C3.17098 13.3897 3.25 13.1989 3.25 13V12.75H3.5C4.03043 12.75 4.53914 12.5393 4.91421 12.1642C5.28929 11.7891 5.5 11.2804 5.5 10.75C5.5 10.2196 5.28929 9.71086 4.91421 9.33579C4.53914 8.96071 4.03043 8.75 3.5 8.75ZM3.5 11.25H3.25V10.25H3.5C3.63261 10.25 3.75979 10.3027 3.85355 10.3964C3.94732 10.4902 4 10.6174 4 10.75C4 10.8826 3.94732 11.0098 3.85355 11.1036C3.75979 11.1973 3.63261 11.25 3.5 11.25ZM14.5 12.5544C14.5001 12.7476 14.4255 12.9335 14.2919 13.0731C14.0945 13.285 13.856 13.4544 13.5908 13.5708C13.3257 13.6872 13.0396 13.7482 12.75 13.75C11.5094 13.75 10.5 12.6281 10.5 11.25C10.5 9.87187 11.5094 8.75 12.75 8.75C13.1673 8.75121 13.5746 8.87748 13.9194 9.1125C14.0034 9.1665 14.0759 9.23676 14.1324 9.31914C14.1889 9.40152 14.2284 9.49436 14.2486 9.59222C14.2687 9.69008 14.2691 9.79098 14.2497 9.88899C14.2304 9.98701 14.1916 10.0802 14.1357 10.163C14.0798 10.2458 14.008 10.3166 13.9243 10.3713C13.8407 10.426 13.747 10.4634 13.6487 10.4813C13.5504 10.4992 13.4495 10.4974 13.352 10.4758C13.2544 10.4542 13.1622 10.4134 13.0806 10.3556C12.9836 10.2879 12.8683 10.2511 12.75 10.25C12.3438 10.25 12 10.7081 12 11.25C12 11.7919 12.3438 12.25 12.75 12.25C12.8073 12.2499 12.8642 12.241 12.9187 12.2238C12.8292 12.1138 12.7726 11.9807 12.7556 11.8399C12.7386 11.6991 12.7619 11.5563 12.8227 11.4282C12.8836 11.3001 12.9795 11.1919 13.0994 11.1161C13.2193 11.0403 13.3582 11 13.5 11H13.75C13.9489 11 14.1397 11.079 14.2803 11.2197C14.421 11.3603 14.5 11.5511 14.5 11.75V12.5544ZM10 9.5V13C10 13.1589 9.94955 13.3138 9.85586 13.4422C9.76218 13.5706 9.63012 13.6659 9.47875 13.7144C9.40477 13.7379 9.32762 13.7499 9.25 13.75C9.13081 13.7501 9.0133 13.7219 8.90722 13.6675C8.80113 13.6132 8.70953 13.5343 8.64 13.4375L7.5 11.84V13C7.5 13.1989 7.42098 13.3897 7.28033 13.5303C7.13968 13.671 6.94891 13.75 6.75 13.75C6.55109 13.75 6.36032 13.671 6.21967 13.5303C6.07902 13.3897 6 13.1989 6 13V9.5C5.99976 9.34104 6.05002 9.18612 6.14354 9.05759C6.23706 8.92905 6.369 8.83356 6.52032 8.78488C6.67164 8.7362 6.83451 8.73686 6.98543 8.78676C7.13636 8.83666 7.26752 8.93322 7.36 9.0625L8.5 10.6594V9.5C8.5 9.30109 8.57902 9.11032 8.71967 8.96967C8.86032 8.82902 9.05109 8.75 9.25 8.75C9.44891 8.75 9.63968 8.82902 9.78033 8.96967C9.92098 9.11032 10 9.30109 10 9.5ZM3 7.5C3.19891 7.5 3.38968 7.42098 3.53033 7.28033C3.67098 7.13968 3.75 6.94891 3.75 6.75V2.75H8.5V5.75C8.5 5.94891 8.57902 6.13968 8.71967 6.28033C8.86032 6.42098 9.05109 6.5 9.25 6.5H12.25V6.75C12.25 6.94891 12.329 7.13968 12.4697 7.28033C12.6103 7.42098 12.8011 7.5 13 7.5C13.1989 7.5 13.3897 7.42098 13.5303 7.28033C13.671 7.13968 13.75 6.94891 13.75 6.75V5.5C13.7501 5.40148 13.7307 5.30391 13.6931 5.21286C13.6555 5.12182 13.6003 5.03908 13.5306 4.96938L10.0306 1.46938C9.96092 1.39975 9.87818 1.34454 9.78714 1.3069C9.69609 1.26926 9.59852 1.24992 9.5 1.25H3.5C3.16848 1.25 2.85054 1.3817 2.61612 1.61612C2.3817 1.85054 2.25 2.16848 2.25 2.5V6.75C2.25 6.94891 2.32902 7.13968 2.46967 7.28033C2.61032 7.42098 2.80109 7.5 3 7.5ZM10 3.5625L11.4375 5H10V3.5625Z"
23+
fill={fillColor}
24+
/>
25+
</svg>
26+
);
27+
}

packages/compass-components/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export { DocumentIcon } from './components/icons/document-icon';
6969
export { FavoriteIcon } from './components/icons/favorite-icon';
7070
export { ServerIcon } from './components/icons/server-icon';
7171
export { NoSavedItemsIcon } from './components/icons/no-saved-items-icon';
72+
export { PngIcon } from './components/icons/png-icon';
7273
export { GuideCue as LGGuideCue } from '@leafygreen-ui/guide-cue';
7374
export { Variant as BadgeVariant } from '@leafygreen-ui/badge';
7475
export { Variant as BannerVariant } from '@leafygreen-ui/banner';
@@ -81,7 +82,7 @@ export { SplitButton } from '@leafygreen-ui/split-button';
8182
export { default as LeafyGreenProvider } from '@leafygreen-ui/leafygreen-provider';
8283

8384
export { palette } from '@leafygreen-ui/palette';
84-
export { rgba, lighten } from 'polished';
85+
export { rgba, lighten, transparentize } from 'polished';
8586
export { default as Portal } from '@leafygreen-ui/portal';
8687
export { Size as RadioBoxSize } from '@leafygreen-ui/radio-box-group';
8788
export { Size as SelectSize } from '@leafygreen-ui/select';

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ function renderDiagramEditorToolbar(
1212
step="EDITING"
1313
hasUndo={true}
1414
hasRedo={true}
15-
onDownloadClick={() => {}}
1615
onUndoClick={() => {}}
1716
onRedoClick={() => {}}
1817
onExportClick={() => {}}
@@ -72,13 +71,4 @@ describe('DiagramEditorToolbar', function () {
7271
userEvent.click(exportButton);
7372
expect(exportSpy).to.have.been.calledOnce;
7473
});
75-
76-
it('renders download button and calls onDownloadClick', function () {
77-
const downloadSpy = sinon.spy();
78-
renderDiagramEditorToolbar({ onDownloadClick: downloadSpy });
79-
const downloadButton = screen.getByRole('button', { name: 'Download' });
80-
expect(downloadButton).to.exist;
81-
userEvent.click(downloadButton);
82-
expect(downloadSpy).to.have.been.calledOnce;
83-
});
8474
});
Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,75 @@
11
import React from 'react';
22
import { connect } from 'react-redux';
33
import type { DataModelingState } from '../store/reducer';
4-
import { saveDiagram, redoEdit, undoEdit } from '../store/diagram';
4+
import { redoEdit, undoEdit } from '../store/diagram';
55
import { showExportModal } from '../store/export-diagram';
6-
import { Icon, IconButton } from '@mongodb-js/compass-components';
6+
import {
7+
Button,
8+
css,
9+
cx,
10+
Icon,
11+
IconButton,
12+
palette,
13+
spacing,
14+
useDarkMode,
15+
transparentize,
16+
} from '@mongodb-js/compass-components';
17+
18+
const containerStyles = css({
19+
display: 'flex',
20+
justifyContent: 'space-between',
21+
alignItems: 'center',
22+
padding: `${spacing[150]}px ${spacing[200]}px`,
23+
backgroundColor: palette.gray.light3,
24+
borderBottom: `1px solid ${palette.gray.light2}`,
25+
marginBottom: spacing[50],
26+
boxShadow: `0px ${spacing[50]}px ${spacing[100]}px -${
27+
spacing[25]
28+
}px ${transparentize(0.85, palette.black)}`,
29+
});
30+
31+
const containerDarkStyles = css({
32+
backgroundColor: palette.gray.dark3,
33+
borderBottom: `1px solid ${palette.gray.dark2}`,
34+
boxShadow: `0px ${spacing[50]}px ${spacing[100]}px -${
35+
spacing[25]
36+
}px ${transparentize(0.85, palette.white)}`,
37+
});
38+
39+
const toolbarGroupStyles = css({
40+
display: 'flex',
41+
});
742

843
export const DiagramEditorToolbar: React.FunctionComponent<{
944
step: DataModelingState['step'];
1045
hasUndo: boolean;
1146
hasRedo: boolean;
12-
onDownloadClick: () => void;
1347
onUndoClick: () => void;
1448
onRedoClick: () => void;
1549
onExportClick: () => void;
16-
}> = ({
17-
step,
18-
hasUndo,
19-
onUndoClick,
20-
hasRedo,
21-
onRedoClick,
22-
onExportClick,
23-
onDownloadClick,
24-
}) => {
50+
}> = ({ step, hasUndo, onUndoClick, hasRedo, onRedoClick, onExportClick }) => {
51+
const darkmode = useDarkMode();
2552
if (step !== 'EDITING') {
2653
return null;
2754
}
2855
return (
29-
<div data-testid="diagram-editor-toolbar">
30-
<IconButton aria-label="Download" onClick={onDownloadClick}>
31-
<Icon glyph="Download"></Icon>
32-
</IconButton>
33-
<IconButton aria-label="Undo" disabled={!hasUndo} onClick={onUndoClick}>
34-
<Icon glyph="Undo"></Icon>
35-
</IconButton>
36-
<IconButton aria-label="Redo" disabled={!hasRedo} onClick={onRedoClick}>
37-
<Icon glyph="Redo"></Icon>
38-
</IconButton>
39-
<IconButton aria-label="Export" onClick={onExportClick}>
40-
<Icon glyph="Export"></Icon>
41-
</IconButton>
56+
<div
57+
className={cx(containerStyles, darkmode && containerDarkStyles)}
58+
data-testid="diagram-editor-toolbar"
59+
>
60+
<div className={toolbarGroupStyles}>
61+
<IconButton aria-label="Undo" disabled={!hasUndo} onClick={onUndoClick}>
62+
<Icon glyph="Undo"></Icon>
63+
</IconButton>
64+
<IconButton aria-label="Redo" disabled={!hasRedo} onClick={onRedoClick}>
65+
<Icon glyph="Redo"></Icon>
66+
</IconButton>
67+
</div>
68+
<div className={toolbarGroupStyles}>
69+
<Button size="xsmall" aria-label="Export" onClick={onExportClick}>
70+
<Icon glyph="Export"></Icon>
71+
</Button>
72+
</div>
4273
</div>
4374
);
4475
};
@@ -56,6 +87,5 @@ export default connect(
5687
onUndoClick: undoEdit,
5788
onRedoClick: redoEdit,
5889
onExportClick: showExportModal,
59-
onDownloadClick: saveDiagram,
6090
}
6191
)(DiagramEditorToolbar);

packages/compass-data-modeling/src/components/export-diagram-modal.tsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
css,
55
Icon,
66
Label,
7-
Link,
87
Modal,
98
ModalBody,
109
ModalFooter,
@@ -13,6 +12,7 @@ import {
1312
RadioGroup,
1413
spacing,
1514
SpinLoader,
15+
PngIcon,
1616
} from '@mongodb-js/compass-components';
1717
import type { ExportDiagramFormat } from '../store/export-diagram';
1818
import {
@@ -25,8 +25,6 @@ import type { DataModelingState } from '../store/reducer';
2525
import { useDiagram } from '@mongodb-js/diagramming';
2626
import type { DiagramInstance } from '@mongodb-js/diagramming';
2727

28-
const nbsp = '\u00a0';
29-
3028
const modelBodyStyles = css({
3129
paddingTop: spacing[600],
3230
});
@@ -39,8 +37,10 @@ const contentContainerStyles = css({
3937

4038
const radioItemStyles = css({
4139
display: 'flex',
42-
alignItems: 'center',
4340
gap: spacing[200],
41+
'> svg': {
42+
marginTop: spacing[50],
43+
},
4444
});
4545

4646
const footerStyles = css({
@@ -73,34 +73,33 @@ const ExportDiagramModal = ({
7373
setOpen={onCloseClick}
7474
data-testid="export-diagram-modal"
7575
>
76-
<ModalHeader
77-
title="Export data model"
78-
subtitle={
79-
<div>
80-
Export your data model as either an image or JSON file.
81-
{nbsp}
82-
<Link
83-
href="https://www.mongodb.com/docs/manual/data-modeling//"
84-
target="_blank"
85-
rel="noopener noreferrer"
86-
>
87-
Learn more
88-
</Link>
89-
</div>
90-
}
91-
/>
76+
<ModalHeader title="Export data model" />
9277
<ModalBody className={modelBodyStyles}>
9378
<div className={contentContainerStyles}>
9479
<Label htmlFor="">Select file format:</Label>
9580
<RadioGroup className={contentContainerStyles} value={exportFormat}>
9681
<div className={radioItemStyles}>
97-
<Icon glyph="Diagram2" />
82+
<Icon glyph="Diagram" />
83+
<Radio
84+
checked={exportFormat === 'diagram'}
85+
value="diagram"
86+
aria-label="Diagram File"
87+
onClick={() => onSelectFormat('diagram')}
88+
size="small"
89+
description="Importable into Compass so teammates can collaborate."
90+
>
91+
Diagram File
92+
</Radio>
93+
</div>
94+
<div className={radioItemStyles}>
95+
<PngIcon />
9896
<Radio
9997
checked={exportFormat === 'png'}
10098
value="png"
10199
aria-label="PNG"
102100
onClick={() => onSelectFormat('png')}
103101
size="small"
102+
description="Shareable image for documentation or presentations."
104103
>
105104
PNG
106105
</Radio>
@@ -113,6 +112,7 @@ const ExportDiagramModal = ({
113112
aria-label="JSON"
114113
onClick={() => onSelectFormat('json')}
115114
size="small"
115+
description="Raw schema data for programmatic use."
116116
>
117117
JSON
118118
</Radio>

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

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
showPrompt,
1818
} from '@mongodb-js/compass-components';
1919
import {
20-
downloadDiagram,
2120
getDiagramContentsFromFile,
2221
getDiagramName,
2322
} from '../services/open-and-download-diagram';
@@ -444,16 +443,6 @@ export function deleteDiagram(
444443
};
445444
}
446445

447-
export function saveDiagram(): DataModelingThunkAction<void, never> {
448-
return (_dispatch, getState) => {
449-
const { diagram } = getState();
450-
if (!diagram) {
451-
return;
452-
}
453-
downloadDiagram(diagram.name, diagram.edits.current);
454-
};
455-
}
456-
457446
export function renameDiagram(
458447
id: string // TODO maybe pass the whole thing here, we always have it when calling this, then we don't need to re-load storage
459448
): DataModelingThunkAction<Promise<void>, RenameDiagramAction> {
@@ -482,7 +471,7 @@ export function renameDiagram(
482471
export function openDiagramFromFile(
483472
file: File
484473
): DataModelingThunkAction<Promise<void>, OpenDiagramAction> {
485-
return async (dispatch, getState, { dataModelStorage }) => {
474+
return async (dispatch, getState, { dataModelStorage, track }) => {
486475
try {
487476
const { name, edits } = await getDiagramContentsFromFile(file);
488477

@@ -499,6 +488,7 @@ export function openDiagramFromFile(
499488
edits,
500489
};
501490
dispatch(openDiagram(diagram));
491+
track('Data Modeling Diagram Imported', {});
502492
void dataModelStorage.save(diagram);
503493
} catch (error) {
504494
openToast('data-modeling-file-read-error', {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import { getCurrentDiagramFromState, selectCurrentModel } from './diagram';
66
import { openToast } from '@mongodb-js/compass-components';
77
import { isCancelError } from '@mongodb-js/compass-utils';
88
import type { DiagramInstance } from '@mongodb-js/diagramming';
9+
import { downloadDiagram } from '../services/open-and-download-diagram';
910

10-
export type ExportDiagramFormat = 'png' | 'json';
11+
export type ExportDiagramFormat = 'png' | 'json' | 'diagram';
1112

1213
export type ExportDiagramState = {
1314
isModalOpen: boolean;
@@ -129,6 +130,10 @@ export function exportDiagram(
129130
diagramInstance,
130131
cancelController.signal
131132
);
133+
} else if (exportFormat === 'diagram') {
134+
downloadDiagram(diagram.name, diagram.edits.current);
135+
} else {
136+
throw new Error(`Unsupported export format: ${exportFormat}`);
132137
}
133138
track('Data Modeling Diagram Exported', {
134139
format: exportFormat,

packages/compass-e2e-tests/helpers/selectors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1447,13 +1447,13 @@ export const DataModelPreviewCollection = (collectionId: string) =>
14471447
`${DataModelPreview} [data-nodeid="${collectionId}"]`;
14481448
export const DataModelApplyEditor = `${DataModelEditor} [data-testid="apply-editor"]`;
14491449
export const DataModelEditorApplyButton = `${DataModelApplyEditor} [data-testid="apply-button"]`;
1450-
export const DataModelDownloadButton = 'button[aria-label="Download"]';
14511450
export const DataModelUndoButton = 'button[aria-label="Undo"]';
14521451
export const DataModelRedoButton = 'button[aria-label="Redo"]';
14531452
export const DataModelExportButton = 'button[aria-label="Export"]';
14541453
export const DataModelExportModal = '[data-testid="export-diagram-modal"]';
14551454
export const DataModelExportPngOption = `${DataModelExportModal} input[aria-label="PNG"]`;
14561455
export const DataModelExportJsonOption = `${DataModelExportModal} input[aria-label="JSON"]`;
1456+
export const DataModelExportDiagramOption = `${DataModelExportModal} input[aria-label="Diagram File"]`;
14571457
export const DataModelExportModalConfirmButton =
14581458
'[data-testid="export-button"]';
14591459
export const DataModelsListItem = (diagramName?: string) => {

packages/compass-e2e-tests/tests/data-modeling-tab.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ describe('Data Modeling tab', function () {
386386
expect(text).to.include('String string'.toLowerCase());
387387
});
388388

389-
it('downloads the data model and opens it', async function () {
389+
it('exports the data model to compass format and imports it back', async function () {
390390
const dataModelName = 'Test Export Model - Save-Open';
391391
exportFileName = `${dataModelName}.compass`;
392392
await setupDiagram(browser, {
@@ -406,8 +406,12 @@ describe('Data Modeling tab', function () {
406406

407407
await browser.waitForAnimations(dataModelEditor);
408408

409-
await browser.clickVisible(Selectors.DataModelDownloadButton);
410-
await browser.waitForAnimations(dataModelEditor);
409+
await browser.clickVisible(Selectors.DataModelExportButton);
410+
const exportModal = browser.$(Selectors.DataModelExportModal);
411+
await exportModal.waitForDisplayed();
412+
413+
await browser.clickParent(Selectors.DataModelExportDiagramOption);
414+
await browser.clickVisible(Selectors.DataModelExportModalConfirmButton);
411415

412416
const { fileExists, filePath } = await waitForFileDownload(
413417
exportFileName,

0 commit comments

Comments
 (0)