Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions packages/compass-components/src/components/icons/png-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { useMemo } from 'react';
import { useDarkMode } from '../../hooks/use-theme';
import { palette } from '@leafygreen-ui/palette';

export function PngIcon() {
const darkMode = useDarkMode();

const fillColor = useMemo(
() => (darkMode ? palette.white : palette.black),
[darkMode]
);

return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
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"
fill={fillColor}
/>
</svg>
);
}
3 changes: 2 additions & 1 deletion packages/compass-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export { DocumentIcon } from './components/icons/document-icon';
export { FavoriteIcon } from './components/icons/favorite-icon';
export { ServerIcon } from './components/icons/server-icon';
export { NoSavedItemsIcon } from './components/icons/no-saved-items-icon';
export { PngIcon } from './components/icons/png-icon';
export { GuideCue as LGGuideCue } from '@leafygreen-ui/guide-cue';
export { Variant as BadgeVariant } from '@leafygreen-ui/badge';
export { Variant as BannerVariant } from '@leafygreen-ui/banner';
Expand All @@ -81,7 +82,7 @@ export { SplitButton } from '@leafygreen-ui/split-button';
export { default as LeafyGreenProvider } from '@leafygreen-ui/leafygreen-provider';

export { palette } from '@leafygreen-ui/palette';
export { rgba, lighten } from 'polished';
export { rgba, lighten, transparentize } from 'polished';
export { default as Portal } from '@leafygreen-ui/portal';
export { Size as RadioBoxSize } from '@leafygreen-ui/radio-box-group';
export { Size as SelectSize } from '@leafygreen-ui/select';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ function renderDiagramEditorToolbar(
step="EDITING"
hasUndo={true}
hasRedo={true}
onDownloadClick={() => {}}
onUndoClick={() => {}}
onRedoClick={() => {}}
onExportClick={() => {}}
Expand Down Expand Up @@ -72,13 +71,4 @@ describe('DiagramEditorToolbar', function () {
userEvent.click(exportButton);
expect(exportSpy).to.have.been.calledOnce;
});

it('renders download button and calls onDownloadClick', function () {
const downloadSpy = sinon.spy();
renderDiagramEditorToolbar({ onDownloadClick: downloadSpy });
const downloadButton = screen.getByRole('button', { name: 'Download' });
expect(downloadButton).to.exist;
userEvent.click(downloadButton);
expect(downloadSpy).to.have.been.calledOnce;
});
});
Original file line number Diff line number Diff line change
@@ -1,44 +1,75 @@
import React from 'react';
import { connect } from 'react-redux';
import type { DataModelingState } from '../store/reducer';
import { saveDiagram, redoEdit, undoEdit } from '../store/diagram';
import { redoEdit, undoEdit } from '../store/diagram';
import { showExportModal } from '../store/export-diagram';
import { Icon, IconButton } from '@mongodb-js/compass-components';
import {
Button,
css,
cx,
Icon,
IconButton,
palette,
spacing,
useDarkMode,
transparentize,
} from '@mongodb-js/compass-components';

const containerStyles = css({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: `${spacing[150]}px ${spacing[200]}px`,
backgroundColor: palette.gray.light3,
borderBottom: `1px solid ${palette.gray.light2}`,
marginBottom: spacing[50],
boxShadow: `0px ${spacing[50]}px ${spacing[100]}px -${
spacing[25]
}px ${transparentize(0.85, palette.black)}`,
});

const containerDarkStyles = css({
backgroundColor: palette.gray.dark3,
borderBottom: `1px solid ${palette.gray.dark2}`,
boxShadow: `0px ${spacing[50]}px ${spacing[100]}px -${
spacing[25]
}px ${transparentize(0.85, palette.white)}`,
});

const toolbarGroupStyles = css({
display: 'flex',
});

export const DiagramEditorToolbar: React.FunctionComponent<{
step: DataModelingState['step'];
hasUndo: boolean;
hasRedo: boolean;
onDownloadClick: () => void;
onUndoClick: () => void;
onRedoClick: () => void;
onExportClick: () => void;
}> = ({
step,
hasUndo,
onUndoClick,
hasRedo,
onRedoClick,
onExportClick,
onDownloadClick,
}) => {
}> = ({ step, hasUndo, onUndoClick, hasRedo, onRedoClick, onExportClick }) => {
const darkmode = useDarkMode();
if (step !== 'EDITING') {
return null;
}
return (
<div data-testid="diagram-editor-toolbar">
<IconButton aria-label="Download" onClick={onDownloadClick}>
<Icon glyph="Download"></Icon>
</IconButton>
<IconButton aria-label="Undo" disabled={!hasUndo} onClick={onUndoClick}>
<Icon glyph="Undo"></Icon>
</IconButton>
<IconButton aria-label="Redo" disabled={!hasRedo} onClick={onRedoClick}>
<Icon glyph="Redo"></Icon>
</IconButton>
<IconButton aria-label="Export" onClick={onExportClick}>
<Icon glyph="Export"></Icon>
</IconButton>
<div
className={cx(containerStyles, darkmode && containerDarkStyles)}
data-testid="diagram-editor-toolbar"
>
<div className={toolbarGroupStyles}>
<IconButton aria-label="Undo" disabled={!hasUndo} onClick={onUndoClick}>
<Icon glyph="Undo"></Icon>
</IconButton>
<IconButton aria-label="Redo" disabled={!hasRedo} onClick={onRedoClick}>
<Icon glyph="Redo"></Icon>
</IconButton>
</div>
<div className={toolbarGroupStyles}>
<Button size="xsmall" aria-label="Export" onClick={onExportClick}>
<Icon glyph="Export"></Icon>
</Button>
</div>
</div>
);
};
Expand All @@ -56,6 +87,5 @@ export default connect(
onUndoClick: undoEdit,
onRedoClick: redoEdit,
onExportClick: showExportModal,
onDownloadClick: saveDiagram,
}
)(DiagramEditorToolbar);
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
css,
Icon,
Label,
Link,
Modal,
ModalBody,
ModalFooter,
Expand All @@ -13,6 +12,7 @@ import {
RadioGroup,
spacing,
SpinLoader,
PngIcon,
} from '@mongodb-js/compass-components';
import type { ExportDiagramFormat } from '../store/export-diagram';
import {
Expand All @@ -25,8 +25,6 @@ import type { DataModelingState } from '../store/reducer';
import { useDiagram } from '@mongodb-js/diagramming';
import type { DiagramInstance } from '@mongodb-js/diagramming';

const nbsp = '\u00a0';

const modelBodyStyles = css({
paddingTop: spacing[600],
});
Expand All @@ -39,8 +37,10 @@ const contentContainerStyles = css({

const radioItemStyles = css({
display: 'flex',
alignItems: 'center',
gap: spacing[200],
'> svg': {
marginTop: spacing[50],
},
});

const footerStyles = css({
Expand Down Expand Up @@ -73,34 +73,33 @@ const ExportDiagramModal = ({
setOpen={onCloseClick}
data-testid="export-diagram-modal"
>
<ModalHeader
title="Export data model"
subtitle={
<div>
Export your data model as either an image or JSON file.
{nbsp}
<Link
href="https://www.mongodb.com/docs/manual/data-modeling//"
target="_blank"
rel="noopener noreferrer"
>
Learn more
</Link>
</div>
}
/>
<ModalHeader title="Export data model" />
<ModalBody className={modelBodyStyles}>
<div className={contentContainerStyles}>
<Label htmlFor="">Select file format:</Label>
<RadioGroup className={contentContainerStyles} value={exportFormat}>
<div className={radioItemStyles}>
<Icon glyph="Diagram2" />
<Icon glyph="Diagram" />
<Radio
checked={exportFormat === 'diagram'}
value="diagram"
aria-label="Diagram File"
onClick={() => onSelectFormat('diagram')}
size="small"
description="Importable into Compass so teammates can collaborate."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a leafygreen thing, but this description not being clickable is such a weird design decision 😬

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, i was thinking of keeping it outside of the radio component for this purposes, but to keep things cleaner, choose this way :)

>
Diagram File
</Radio>
</div>
<div className={radioItemStyles}>
<PngIcon />
<Radio
checked={exportFormat === 'png'}
value="png"
aria-label="PNG"
onClick={() => onSelectFormat('png')}
size="small"
description="Shareable image for documentation or presentations."
>
PNG
</Radio>
Expand All @@ -113,6 +112,7 @@ const ExportDiagramModal = ({
aria-label="JSON"
onClick={() => onSelectFormat('json')}
size="small"
description="Raw schema data for programmatic use."
>
JSON
</Radio>
Expand Down
14 changes: 2 additions & 12 deletions packages/compass-data-modeling/src/store/diagram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
showPrompt,
} from '@mongodb-js/compass-components';
import {
downloadDiagram,
getDiagramContentsFromFile,
getDiagramName,
} from '../services/open-and-download-diagram';
Expand Down Expand Up @@ -444,16 +443,6 @@ export function deleteDiagram(
};
}

export function saveDiagram(): DataModelingThunkAction<void, never> {
return (_dispatch, getState) => {
const { diagram } = getState();
if (!diagram) {
return;
}
downloadDiagram(diagram.name, diagram.edits.current);
};
}

export function renameDiagram(
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
): DataModelingThunkAction<Promise<void>, RenameDiagramAction> {
Expand Down Expand Up @@ -482,7 +471,7 @@ export function renameDiagram(
export function openDiagramFromFile(
file: File
): DataModelingThunkAction<Promise<void>, OpenDiagramAction> {
return async (dispatch, getState, { dataModelStorage }) => {
return async (dispatch, getState, { dataModelStorage, track }) => {
try {
const { name, edits } = await getDiagramContentsFromFile(file);

Expand All @@ -499,6 +488,7 @@ export function openDiagramFromFile(
edits,
};
dispatch(openDiagram(diagram));
track('Data Modeling Diagram Imported', {});
void dataModelStorage.save(diagram);
} catch (error) {
openToast('data-modeling-file-read-error', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { getCurrentDiagramFromState, selectCurrentModel } from './diagram';
import { openToast } from '@mongodb-js/compass-components';
import { isCancelError } from '@mongodb-js/compass-utils';
import type { DiagramInstance } from '@mongodb-js/diagramming';
import { downloadDiagram } from '../services/open-and-download-diagram';

export type ExportDiagramFormat = 'png' | 'json';
export type ExportDiagramFormat = 'png' | 'json' | 'diagram';

export type ExportDiagramState = {
isModalOpen: boolean;
Expand Down Expand Up @@ -129,6 +130,10 @@ export function exportDiagram(
diagramInstance,
cancelController.signal
);
} else if (exportFormat === 'diagram') {
downloadDiagram(diagram.name, diagram.edits.current);
} else {
throw new Error(`Unsupported export format: ${exportFormat}`);
}
track('Data Modeling Diagram Exported', {
format: exportFormat,
Expand Down
2 changes: 1 addition & 1 deletion packages/compass-e2e-tests/helpers/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1447,13 +1447,13 @@ export const DataModelPreviewCollection = (collectionId: string) =>
`${DataModelPreview} [data-nodeid="${collectionId}"]`;
export const DataModelApplyEditor = `${DataModelEditor} [data-testid="apply-editor"]`;
export const DataModelEditorApplyButton = `${DataModelApplyEditor} [data-testid="apply-button"]`;
export const DataModelDownloadButton = 'button[aria-label="Download"]';
export const DataModelUndoButton = 'button[aria-label="Undo"]';
export const DataModelRedoButton = 'button[aria-label="Redo"]';
export const DataModelExportButton = 'button[aria-label="Export"]';
export const DataModelExportModal = '[data-testid="export-diagram-modal"]';
export const DataModelExportPngOption = `${DataModelExportModal} input[aria-label="PNG"]`;
export const DataModelExportJsonOption = `${DataModelExportModal} input[aria-label="JSON"]`;
export const DataModelExportDiagramOption = `${DataModelExportModal} input[aria-label="Diagram File"]`;
export const DataModelExportModalConfirmButton =
'[data-testid="export-button"]';
export const DataModelsListItem = (diagramName?: string) => {
Expand Down
10 changes: 7 additions & 3 deletions packages/compass-e2e-tests/tests/data-modeling-tab.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ describe('Data Modeling tab', function () {
expect(text).to.include('String string'.toLowerCase());
});

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

await browser.waitForAnimations(dataModelEditor);

await browser.clickVisible(Selectors.DataModelDownloadButton);
await browser.waitForAnimations(dataModelEditor);
await browser.clickVisible(Selectors.DataModelExportButton);
const exportModal = browser.$(Selectors.DataModelExportModal);
await exportModal.waitForDisplayed();

await browser.clickParent(Selectors.DataModelExportDiagramOption);
await browser.clickVisible(Selectors.DataModelExportModalConfirmButton);

const { fileExists, filePath } = await waitForFileDownload(
exportFileName,
Expand Down
Loading
Loading