Skip to content

Commit

Permalink
feat: reset the bulk update modal when re-opened COMPASS-7325 (#5047)
Browse files Browse the repository at this point in the history
* reset the bulk update modal when re-opened

* don't set updateText twice

* demonstrate the oscillation problem

* Revert "demonstrate the oscillation problem"

This reverts commit f3894d4.

* cleaner hack
  • Loading branch information
lerouxb authored Nov 1, 2023
1 parent a4ccbd4 commit d91fb07
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/compass-components/src/components/leafygreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import TextArea from '@leafygreen-ui/text-area';
import TextInput from '@leafygreen-ui/text-input';
import { SearchInput } from '@leafygreen-ui/search-input';
export { ToastProvider, useToast, ToastProps } from '@leafygreen-ui/toast';
export { usePrevious } from '@leafygreen-ui/hooks';
import Toggle from '@leafygreen-ui/toggle';
import {
H1,
Expand Down
139 changes: 139 additions & 0 deletions packages/compass-crud/src/components/bulk-update-dialog.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React from 'react';
import { expect } from 'chai';
import sinon from 'sinon';
import { render, screen, cleanup, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import BulkUpdateDialog from './bulk-update-dialog';

function renderBulkUpdateDialog(
props?: Partial<React.ComponentProps<typeof BulkUpdateDialog>>
) {
return render(
<BulkUpdateDialog
isOpen={true}
ns="mydb.mycoll"
filter={{ a: 1 }}
count={0}
updateText="{ $set: {} }"
preview={{
changes: [
{
before: { foo: 1 },
after: { foo: 1 },
},
],
}}
closeBulkUpdateDialog={() => {}}
updateBulkUpdatePreview={() => {}}
runBulkUpdate={() => {}}
{...props}
/>
);
}

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

it('does not render if closed', function () {
renderBulkUpdateDialog({ isOpen: false });
expect(screen.queryByText(/Update/)).to.not.exist;
});

it('renders if open', function () {
renderBulkUpdateDialog({ count: 42 });

// filter
expect(
screen.getByTestId('bulk-update-filter').getAttribute('value')
).to.equal('{\n a: 1\n}');

// update
expect(screen.getByTestId('bulk-update-update').textContent).to.match(
/{ \$set: {} }/
);

// preview
expect(
screen.getAllByTestId('bulk-update-preview-document')
).to.have.lengthOf(1);

// buttons
expect(screen.getByRole('button', { name: 'Close' })).to.exist;
expect(screen.getByRole('button', { name: 'Update documents' })).to.exist;
});

it('resets if the modal is re-opened', async function () {
// initial open
const { rerender } = renderBulkUpdateDialog({ isOpen: true });

// close
rerender(
<BulkUpdateDialog
isOpen={false}
ns="mydb.mycoll"
filter={{ a: 1 }}
count={0}
updateText="{ $set: {} }"
preview={{
changes: [
{
before: {},
after: {},
},
],
}}
closeBulkUpdateDialog={() => {}}
updateBulkUpdatePreview={() => {}}
runBulkUpdate={() => {}}
/>
);

// re-open
rerender(
<BulkUpdateDialog
isOpen={true}
ns="mydb.mycoll"
filter={{ a: 1 }}
count={0}
updateText="foo"
preview={{
changes: [
{
before: {},
after: {},
},
],
}}
closeBulkUpdateDialog={() => {}}
updateBulkUpdatePreview={() => {}}
runBulkUpdate={() => {}}
/>
);

await waitFor(() => {
expect(
screen
.getByTestId('bulk-update-update')
.getElementsByClassName('cm-content')[0].textContent
).to.equal('foo');
});
});

it('closes the modal when the close button is clicked', function () {
const onCloseSpy = sinon.spy();
renderBulkUpdateDialog({ closeBulkUpdateDialog: onCloseSpy });

userEvent.click(screen.getByRole('button', { name: 'Close' }));
expect(onCloseSpy).to.have.been.calledOnce;
});

it('runs the update when the update button is clicked', function () {
const onUpdateSpy = sinon.spy();
renderBulkUpdateDialog({ runBulkUpdate: onUpdateSpy });

userEvent.click(screen.getByRole('button', { name: 'Update documents' }));
expect(onUpdateSpy).to.have.been.calledOnce;
});
});
14 changes: 13 additions & 1 deletion packages/compass-crud/src/components/bulk-update-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useMemo, useState, useEffect } from 'react';
import Document from './document';
import HadronDocument from 'hadron-document';

Expand All @@ -19,6 +19,7 @@ import {
InfoSprinkle,
useDarkMode,
TextInput,
usePrevious,
} from '@mongodb-js/compass-components';

import type { Annotation } from '@mongodb-js/compass-editor';
Expand Down Expand Up @@ -163,6 +164,7 @@ export default function BulkUpdateDialog({
const darkMode = useDarkMode();

const [text, setText] = useState(updateText);
const wasOpen = usePrevious(isOpen);

const previewDocuments = useMemo(() => {
return preview.changes.map(
Expand Down Expand Up @@ -193,6 +195,15 @@ export default function BulkUpdateDialog({
return [];
}, [syntaxError]);

// This hack in addition to keeping the text state locally exists due to
// reflux (unlike redux) being async. We can remove it once we move
// compass-crud to redux.
useEffect(() => {
if (isOpen && !wasOpen) {
setText(updateText);
}
}, [isOpen, wasOpen, updateText]);

return (
<FormModal
title={title}
Expand All @@ -209,6 +220,7 @@ export default function BulkUpdateDialog({
<div className={queryStyles}>
<div className={queryFieldStyles}>
<TextInput
data-testid="bulk-update-filter"
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore the label can be any component, but it's weirdly typed to string
label={
Expand Down
1 change: 0 additions & 1 deletion packages/compass-crud/src/stores/crud-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,6 @@ class CrudStoreImpl
this.setState({
bulkUpdate: {
...this.state.bulkUpdate,
updateText,
preview,
serverError: undefined,
syntaxError: undefined,
Expand Down

0 comments on commit d91fb07

Please sign in to comment.