Skip to content

Commit

Permalink
feat: add ChangeView component to bulk update preview (#5209)
Browse files Browse the repository at this point in the history
* plug ChangeView component into bulk update preview

* tests for unifyDocuments()

* leave space for expand/collapse buttons

* css tweaks

* toBSON fixes

* address TODOs

* factor out expand button

* fix bulk update e2e test

* depcheck

* unit tests for ChangeView component

* address TODO

* don't promote values when loading before&after preview docs

* remove TODO

* ignore the generated expected results

* maybe without the leading slash

* consistently use getValueShape()

* live with horizontal scrolling rather than weird wrapping

* wider preview, remove extra spacing, format the default update text

* sans only

* sans only

* json formatting seems to not be entirely deterministic..

* update e2e expected result

* larger modal for bulk update
  • Loading branch information
lerouxb authored Dec 12, 2023
1 parent 76a38f1 commit 7fc0b81
Show file tree
Hide file tree
Showing 50 changed files with 9,028 additions and 31 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
* text=auto eol=lf
/packages/bson-transpilers/lib/**/* linguist-generated=true
packages/compass-crud/test/fixture-results/* linguist-generated=true
59 changes: 59 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/compass-crud/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"enzyme": "^3.11.0",
"eslint": "^7.25.0",
"hadron-app": "^5.16.2",
"jsondiffpatch": "^0.5.0",
"lodash": "^4.17.21",
"mocha": "^10.2.0",
"mongodb-instance-model": "^12.16.4",
Expand Down
48 changes: 28 additions & 20 deletions packages/compass-crud/src/components/bulk-update-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useMemo, useState, useEffect, useCallback } from 'react';
import type { UpdatePreview } from 'mongodb-data-service';
import HadronDocument from 'hadron-document';
import type { Document } from 'bson';
import { toJSString } from 'mongodb-query-parser';
import {
css,
Expand All @@ -24,22 +24,26 @@ import {
InteractivePopover,
TextInput,
useId,
DocumentIcon,
} from '@mongodb-js/compass-components';
import type { Annotation } from '@mongodb-js/compass-editor';
import { CodemirrorMultilineEditor } from '@mongodb-js/compass-editor';

import Document from './document';
import type { BSONObject } from '../stores/crud-store';

import { ChangeView } from './change-view';
import { ReadonlyFilter } from './readonly-filter';
import { DocumentIcon } from '@mongodb-js/compass-components';

const modalContentStyles = css({
width: '100%',
maxWidth: '1280px',
});

const columnsStyles = css({
marginTop: spacing[4],
display: 'grid',
width: '100%',
gap: spacing[4],
gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
gridTemplateColumns: '2fr 3fr',
});

const queryStyles = css({
Expand All @@ -60,7 +64,7 @@ const descriptionStyles = css({

const previewStyles = css({
contain: 'size',
overflow: 'scroll',
overflow: 'auto',
});

const previewDescriptionStyles = css({
Expand All @@ -79,7 +83,7 @@ const codeLightContainerStyles = css({
});

const multilineContainerStyles = css({
maxHeight: spacing[4] * 20,
maxHeight: spacing[5] * 7, // fit at our default window size
});

const bannerContainerStyles = css({
Expand Down Expand Up @@ -252,12 +256,6 @@ const BulkUpdatePreview: React.FunctionComponent<BulkUpdatePreviewProps> = ({
count,
preview,
}) => {
const previewDocuments = useMemo(() => {
return preview.changes.map(
(change) => new HadronDocument(change.after as Record<string, unknown>)
);
}, [preview]);

// show a preview for the edge case where the count is undefined, not the
// empty state
if (count === 0) {
Expand Down Expand Up @@ -294,12 +292,13 @@ const BulkUpdatePreview: React.FunctionComponent<BulkUpdatePreviewProps> = ({
</Description>
</Label>
<div className={updatePreviewStyles}>
{previewDocuments.map((doc: HadronDocument, index: number) => {
{preview.changes.map(({ before, after }, index: number) => {
return (
<UpdatePreviewDocument
key={`change=${index}`}
data-testid="bulk-update-preview-document"
doc={doc}
before={before}
after={after}
/>
);
})}
Expand Down Expand Up @@ -390,8 +389,8 @@ export default function BulkUpdateDialog({
<Modal
open={isOpen}
setOpen={closeBulkUpdateDialog}
size={enablePreview ? 'large' : 'default'}
data-testid="bulk-update-dialog"
contentClassName={enablePreview ? modalContentStyles : undefined}
initialFocus={`#${bulkUpdateUpdateId} .cm-content`}
>
<ModalHeader title={modalTitleAndButtonText} subtitle={ns} />
Expand Down Expand Up @@ -483,16 +482,25 @@ export default function BulkUpdateDialog({
);
}

const previewCardStyles = css({
padding: spacing[3],
});

function UpdatePreviewDocument({
doc,
before,
after,
...props
}: {
'data-testid': string;
doc: HadronDocument;
before: Document;
after: Document;
}) {
return (
<KeylineCard data-testid={props['data-testid']}>
<Document doc={doc} editable={false} />
<KeylineCard
data-testid={props['data-testid']}
className={previewCardStyles}
>
<ChangeView before={before} after={after} name={props['data-testid']} />
</KeylineCard>
);
}
36 changes: 36 additions & 0 deletions packages/compass-crud/src/components/change-view/bson-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { EJSON } from 'bson';

import { getValueShape } from './shape-utils';

export function stringifyBSON(value: any) {
if (value?.inspect) {
return value.inspect();
}
if (value?.toISOString) {
return value.toISOString();
}
return EJSON.stringify(value);
}

export function unBSON(value: any | any[]): any | any[] {
const shape = getValueShape(value);
if (shape === 'array') {
return value.map(unBSON);
} else if (shape === 'object') {
const mapped: Record<string, any | any[]> = {};
for (const [k, v] of Object.entries(value)) {
mapped[k] = unBSON(v);
}
return mapped;
} else if (value?._bsontype) {
return stringifyBSON(value);
} else if (Object.prototype.toString.call(value) === '[object RegExp]') {
// make sure these match when diffing
return value.toString();
} else if (Object.prototype.toString.call(value) === '[object Date]') {
// make sure dates are consistently strings when diffing
return value.toISOString();
} else {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { expect } from 'chai';
import { render, screen, cleanup } from '@testing-library/react';
import { ChangeView } from './change-view';
import { fixtureGroups } from '../../../test/before-after-fixtures';

function renderChangeView(
props?: Partial<React.ComponentProps<typeof ChangeView>>
) {
return render(<ChangeView name="test" before={{}} after={{}} {...props} />);
}

describe('ChangeView Component', function () {
afterEach(function () {
cleanup();
});

it('renders', function () {
renderChangeView({
before: { a: 1 },
after: { b: 2 },
});

expect(screen.getByTestId('change-view-test')).to.exist;
});

for (const group of fixtureGroups) {
context(group.name, function () {
for (const { name, before, after } of group.fixtures) {
it(name, function () {
renderChangeView({
name,
before,
after,
});

// A bit primitive. Just trying to see if there are any errors as a
// sort of regression test. Not sure how to write an assertion that
// would workfor each one.
expect(screen.getByTestId(`change-view-${name}`)).to.exist;
});
}
});
}
});
Loading

0 comments on commit 7fc0b81

Please sign in to comment.