From d91fb07ed6ab5275bf8f603ac70157694cf1e30d Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Wed, 1 Nov 2023 17:21:42 +0000 Subject: [PATCH] feat: reset the bulk update modal when re-opened COMPASS-7325 (#5047) * 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 f3894d4576def847e8d83843a8a4c25bbc4c7e43. * cleaner hack --- .../src/components/leafygreen.tsx | 1 + .../components/bulk-update-dialog.spec.tsx | 139 ++++++++++++++++++ .../src/components/bulk-update-dialog.tsx | 14 +- .../compass-crud/src/stores/crud-store.ts | 1 - 4 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 packages/compass-crud/src/components/bulk-update-dialog.spec.tsx diff --git a/packages/compass-components/src/components/leafygreen.tsx b/packages/compass-components/src/components/leafygreen.tsx index 894a14bbadc..25a12115386 100644 --- a/packages/compass-components/src/components/leafygreen.tsx +++ b/packages/compass-components/src/components/leafygreen.tsx @@ -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, diff --git a/packages/compass-crud/src/components/bulk-update-dialog.spec.tsx b/packages/compass-crud/src/components/bulk-update-dialog.spec.tsx new file mode 100644 index 00000000000..52262e183d0 --- /dev/null +++ b/packages/compass-crud/src/components/bulk-update-dialog.spec.tsx @@ -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> +) { + return render( + {}} + 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( + {}} + updateBulkUpdatePreview={() => {}} + runBulkUpdate={() => {}} + /> + ); + + // re-open + rerender( + {}} + 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; + }); +}); diff --git a/packages/compass-crud/src/components/bulk-update-dialog.tsx b/packages/compass-crud/src/components/bulk-update-dialog.tsx index 91f756035f1..82956c1e470 100644 --- a/packages/compass-crud/src/components/bulk-update-dialog.tsx +++ b/packages/compass-crud/src/components/bulk-update-dialog.tsx @@ -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'; @@ -19,6 +19,7 @@ import { InfoSprinkle, useDarkMode, TextInput, + usePrevious, } from '@mongodb-js/compass-components'; import type { Annotation } from '@mongodb-js/compass-editor'; @@ -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( @@ -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 (