From 80f1ece53f693c66d1217479394e5180dc0ffb7d Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Mon, 3 Mar 2025 19:15:44 +0100 Subject: [PATCH 01/14] wip --- .../document-edit-actions-footer.tsx | 45 ++++++++++++++----- .../src/components/editable-document.tsx | 7 +++ .../compass-crud/src/stores/crud-store.ts | 6 +++ packages/hadron-document/src/document.ts | 4 +- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 6468e605f5b..51fee09b2b9 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; +import type { MongoServerError } from 'mongodb'; import type HadronDocument from 'hadron-document'; import { Element } from 'hadron-document'; import { Button } from '../leafygreen'; @@ -81,13 +82,26 @@ function useHadronDocumentStatus( ? 'Deleting' : 'Initial'; }); - const [errorMessage, setErrorMessage] = useState(null); + const [error, setError] = useState<{ + message: string; + details?: Record; + } | null>(null); const invalidElementsRef = useRef(new Set()); - const updateStatus = useCallback((newStatus: Status, errorMessage = null) => { - setStatus(newStatus); - setErrorMessage(errorMessage); - }, []); + const updateStatus = useCallback( + (newStatus: Status, error: Error | MongoServerError | null = null) => { + setStatus(newStatus); + setError( + error + ? { + message: error?.message, + details: (error as MongoServerError).errInfo, + } + : null + ); + }, + [] + ); useEffect(() => { if (status !== 'Initial') { @@ -128,7 +142,7 @@ function useHadronDocumentStatus( const onUpdateSuccess = () => { updateStatus('UpdateSuccess'); }; - const onUpdateError = (err: string) => { + const onUpdateError = (err: Error | MongoServerError) => { updateStatus('UpdateError', err); }; const onRemoveStart = () => { @@ -137,7 +151,7 @@ function useHadronDocumentStatus( const onRemoveSuccess = () => { updateStatus('DeleteSuccess'); }; - const onRemoveError = (err: string) => { + const onRemoveError = (err: Error | MongoServerError) => { updateStatus('DeleteError', err); }; @@ -183,7 +197,7 @@ function useHadronDocumentStatus( } }, [status, updateStatus]); - return { status, updateStatus, errorMessage }; + return { status, updateStatus, error }; } const container = css({ @@ -261,6 +275,7 @@ const EditActionsFooter: React.FunctionComponent<{ onUpdate(force: boolean): void; onDelete(): void; onCancel?: () => void; + onOpenErrorDetails?: (details: Record) => void; }> = ({ doc, editing, @@ -271,11 +286,12 @@ const EditActionsFooter: React.FunctionComponent<{ onUpdate, onDelete, onCancel, + onOpenErrorDetails, }) => { const { status: _status, updateStatus, - errorMessage, + error, } = useHadronDocumentStatus(doc, editing, deleting); const darkMode = useDarkMode(); @@ -303,7 +319,16 @@ const EditActionsFooter: React.FunctionComponent<{ data-status={status} >
- {errorMessage ?? statusMessage} + {error?.message ?? statusMessage} + {error?.details && ( + + )}
{!isSuccess(status) && (
diff --git a/packages/compass-crud/src/components/editable-document.tsx b/packages/compass-crud/src/components/editable-document.tsx index 9040e2378ff..17f46436361 100644 --- a/packages/compass-crud/src/components/editable-document.tsx +++ b/packages/compass-crud/src/components/editable-document.tsx @@ -278,6 +278,12 @@ class EditableDocument extends React.Component< onCancel={() => { this.handleCancel(); }} + onOpenErrorDetails={(details: Record) => { + this.props.openErrorDetailsDialog?.({ + details, + closeAction: 'close', + }); + }} /> ); } @@ -313,6 +319,7 @@ class EditableDocument extends React.Component< replaceDocument: PropTypes.func.isRequired, updateDocument: PropTypes.func.isRequired, openInsertDocumentDialog: PropTypes.func.isRequired, + openErrorDetailsDialog: PropTypes.func.isRequired, copyToClipboard: PropTypes.func.isRequired, showInsights: PropTypes.bool, }; diff --git a/packages/compass-crud/src/stores/crud-store.ts b/packages/compass-crud/src/stores/crud-store.ts index 56db6a43287..7fda7d5b519 100644 --- a/packages/compass-crud/src/stores/crud-store.ts +++ b/packages/compass-crud/src/stores/crud-store.ts @@ -705,6 +705,11 @@ class CrudStoreImpl const nbsp = '\u00a0'; error.message += ` (Updating fields whose names contain dots or start with $ require MongoDB${nbsp}5.0 or above.)`; } + console.log( + 'updateDocument error', + error, + (error as MongoServerError).errInfo + ); doc.onUpdateError(error as Error); } else if (d) { doc.onUpdateSuccess(d); @@ -803,6 +808,7 @@ class CrudStoreImpl 'replace' ); if (error) { + console.log('replaceDocument error', error); doc.onUpdateError(error as Error); } else { doc.onUpdateSuccess(d); diff --git a/packages/hadron-document/src/document.ts b/packages/hadron-document/src/document.ts index 1670ca3ccbd..54ce8337eed 100644 --- a/packages/hadron-document/src/document.ts +++ b/packages/hadron-document/src/document.ts @@ -478,7 +478,7 @@ export class Document extends EventEmitter { } onUpdateError(error: Error) { - this.emit('update-error', error.message); + this.emit('update-error', error); } markForDeletion() { @@ -505,7 +505,7 @@ export class Document extends EventEmitter { } onRemoveError(error: Error) { - this.emit('remove-error', error.message); + this.emit('remove-error', error); } setModifiedEJSONString(ejson: string | null) { From 9f2d23b67c72a645da00c8b7cf0ea60510b3ccb4 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Mon, 3 Mar 2025 20:10:49 +0100 Subject: [PATCH 02/14] feat: error details on update COMPASS-8865 --- .../document-edit-actions-footer.tsx | 19 ++++++++++--------- .../src/components/document-json-view.tsx | 1 + .../compass-crud/src/components/document.tsx | 1 + .../src/components/json-editor.tsx | 7 +++++++ .../table-view/document-table-view.tsx | 3 +++ .../table-view/full-width-cell-renderer.tsx | 8 ++++++++ .../virtualized-document-json-view.tsx | 4 ++++ .../virtualized-document-list-view.tsx | 4 ++++ 8 files changed, 38 insertions(+), 9 deletions(-) diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 51fee09b2b9..e24a2cf167f 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -320,18 +320,19 @@ const EditActionsFooter: React.FunctionComponent<{ >
{error?.message ?? statusMessage} - {error?.details && ( - - )}
{!isSuccess(status) && (
+ {error?.details && ( + + )}
); diff --git a/packages/compass-crud/src/components/table-view/document-table-view.tsx b/packages/compass-crud/src/components/table-view/document-table-view.tsx index 9f09c67d0b6..66f0e342ed7 100644 --- a/packages/compass-crud/src/components/table-view/document-table-view.tsx +++ b/packages/compass-crud/src/components/table-view/document-table-view.tsx @@ -56,6 +56,7 @@ export type DocumentTableViewProps = { ns: string; version: string; openInsertDocumentDialog?: CrudActions['openInsertDocumentDialog']; + openErrorDetailsDialog?: CrudActions['openErrorDetailsDialog']; pathChanged: (path: (string | number)[], types: TableHeaderType[]) => void; removeColumn: GridActions['removeColumn']; copyToClipboard: (doc: Document) => void; @@ -143,6 +144,7 @@ class DocumentTableView extends React.Component { removeDocument: this.props.removeDocument, replaceDocument: this.props.replaceDocument, updateDocument: this.props.updateDocument, + openErrorDetailsDialog: this.props.openErrorDetailsDialog, darkMode: this.props.darkMode, }, getRowNodeId: function (data) { @@ -1031,6 +1033,7 @@ class DocumentTableView extends React.Component { ns: PropTypes.string.isRequired, version: PropTypes.string.isRequired, openInsertDocumentDialog: PropTypes.func, + openErrorDetailsDialog: PropTypes.func, pathChanged: PropTypes.func.isRequired, removeColumn: PropTypes.func.isRequired, copyToClipboard: PropTypes.func.isRequired, diff --git a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx index 43cb3898027..8e7d906ea20 100644 --- a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx @@ -20,6 +20,7 @@ export type FullWidthCellRendererProps = Pick< replaceDocument: CrudActions['replaceDocument']; removeDocument: CrudActions['removeDocument']; updateDocument: CrudActions['updateDocument']; + openErrorDetailsDialog: CrudActions['openErrorDetailsDialog']; darkMode?: boolean; }; @@ -150,6 +151,12 @@ class FullWidthCellRenderer extends React.Component< this.handleCancelRemove(); } }} + onOpenErrorDetails={(details: Record) => { + this.props.openErrorDetailsDialog?.({ + details, + closeAction: 'close', + }); + }} /> ); @@ -163,6 +170,7 @@ class FullWidthCellRenderer extends React.Component< updateDocument: PropTypes.func.isRequired, removeDocument: PropTypes.func.isRequired, replaceDocument: PropTypes.func.isRequired, + openErrorDetailsDialog: PropTypes.func.isRequired, replaceDoc: PropTypes.func.isRequired, cleanCols: PropTypes.func.isRequired, darkMode: PropTypes.bool, diff --git a/packages/compass-crud/src/components/virtualized-document-json-view.tsx b/packages/compass-crud/src/components/virtualized-document-json-view.tsx index 8cffcf8f26f..12e594bba44 100644 --- a/packages/compass-crud/src/components/virtualized-document-json-view.tsx +++ b/packages/compass-crud/src/components/virtualized-document-json-view.tsx @@ -53,6 +53,7 @@ export type VirtualizedDocumentJsonViewProps = { | 'replaceDocument' | 'updateDocument' | 'openInsertDocumentDialog' + | 'openErrorDetailsDialog' >; const VirtualizedDocumentJsonView: React.FC< @@ -70,6 +71,7 @@ const VirtualizedDocumentJsonView: React.FC< replaceDocument, updateDocument, openInsertDocumentDialog, + openErrorDetailsDialog, __TEST_OVERSCAN_COUNT, __TEST_LIST_HEIGHT, listRef, @@ -90,6 +92,7 @@ const VirtualizedDocumentJsonView: React.FC< replaceDocument={replaceDocument} updateDocument={updateDocument} openInsertDocumentDialog={openInsertDocumentDialog} + openErrorDetailsDialog={openErrorDetailsDialog} /> ); @@ -101,6 +104,7 @@ const VirtualizedDocumentJsonView: React.FC< scrollTriggerRef, copyToClipboard, openInsertDocumentDialog, + openErrorDetailsDialog, removeDocument, replaceDocument, updateDocument, diff --git a/packages/compass-crud/src/components/virtualized-document-list-view.tsx b/packages/compass-crud/src/components/virtualized-document-list-view.tsx index 5ddfa237fb4..5446284359e 100644 --- a/packages/compass-crud/src/components/virtualized-document-list-view.tsx +++ b/packages/compass-crud/src/components/virtualized-document-list-view.tsx @@ -58,6 +58,7 @@ type VirtualizedDocumentListViewProps = { | 'replaceDocument' | 'updateDocument' | 'openInsertDocumentDialog' + | 'openErrorDetailsDialog' >; const VirtualizedDocumentListView: React.FC< @@ -74,6 +75,7 @@ const VirtualizedDocumentListView: React.FC< replaceDocument, updateDocument, openInsertDocumentDialog, + openErrorDetailsDialog, listRef, __TEST_OVERSCAN_COUNT, __TEST_LIST_HEIGHT, @@ -104,6 +106,7 @@ const VirtualizedDocumentListView: React.FC< replaceDocument={replaceDocument} updateDocument={updateDocument} openInsertDocumentDialog={openInsertDocumentDialog} + openErrorDetailsDialog={openErrorDetailsDialog} /> ); @@ -114,6 +117,7 @@ const VirtualizedDocumentListView: React.FC< scrollTriggerRef, copyToClipboard, openInsertDocumentDialog, + openErrorDetailsDialog, removeDocument, replaceDocument, updateDocument, From 9ec2f20c47e6a0b7d180f9773d9e1f29f9351044 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Tue, 4 Mar 2025 13:22:58 +0100 Subject: [PATCH 03/14] add tests --- .../document-edit-actions-footer.tsx | 2 +- .../compass-crud/src/components/document.tsx | 1 - .../compass-crud/src/stores/crud-store.ts | 6 - .../compass-e2e-tests/helpers/selectors.ts | 3 + .../tests/collection-documents-tab.test.ts | 152 ++++++++++++++++++ 5 files changed, 156 insertions(+), 8 deletions(-) diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index e24a2cf167f..8f5a53e306b 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -328,7 +328,7 @@ const EditActionsFooter: React.FunctionComponent<{ className={button} size="xsmall" onClick={() => onOpenErrorDetails?.(error.details!)} - data-testid="insert-document-error-details-button" + data-testid="edit-actions-footer-error-details-button" > VIEW ERROR DETAILS diff --git a/packages/compass-crud/src/components/document.tsx b/packages/compass-crud/src/components/document.tsx index 3e22f31161f..675367a5f6f 100644 --- a/packages/compass-crud/src/components/document.tsx +++ b/packages/compass-crud/src/components/document.tsx @@ -43,7 +43,6 @@ const Document = (props: DocumentProps) => { } if (editable) { - console.log('PROP IS HERE', props.openErrorDetailsDialog); return ; } diff --git a/packages/compass-crud/src/stores/crud-store.ts b/packages/compass-crud/src/stores/crud-store.ts index 7fda7d5b519..56db6a43287 100644 --- a/packages/compass-crud/src/stores/crud-store.ts +++ b/packages/compass-crud/src/stores/crud-store.ts @@ -705,11 +705,6 @@ class CrudStoreImpl const nbsp = '\u00a0'; error.message += ` (Updating fields whose names contain dots or start with $ require MongoDB${nbsp}5.0 or above.)`; } - console.log( - 'updateDocument error', - error, - (error as MongoServerError).errInfo - ); doc.onUpdateError(error as Error); } else if (d) { doc.onUpdateSuccess(d); @@ -808,7 +803,6 @@ class CrudStoreImpl 'replace' ); if (error) { - console.log('replaceDocument error', error); doc.onUpdateError(error as Error); } else { doc.onUpdateSuccess(d); diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 4db1898db71..25f08009eb4 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -593,6 +593,7 @@ export const DocumentListFetchingStopButton = '[data-testid="documents-content"] [data-testid="fetching-documents"] button'; export const DocumentListError = '[data-testid="document-list-error-summary"]'; export const AddDataButton = '[data-testid="crud-add-data-show-actions"]'; +export const EditDocumentButton = '[data-testid="edit-document-button"]'; export const InsertDocumentOption = '[data-testid="crud-add-data-insert-document-action"]'; export const ImportFileOption = @@ -608,6 +609,8 @@ export const CloneDocumentButton = '[data-testid="clone-document-button"]'; export const DeleteDocumentButton = '[data-testid="remove-document-button"]'; export const DocumentFooter = '[data-testid="document-footer"]'; export const DocumentFooterMessage = '[data-testid="document-footer-message"]'; +export const DocumentFooterErrorDetailsButton = + '[data-testid="edit-actions-footer-error-details-button"]'; export const UpdateDocumentButton = `${DocumentFooter} [data-testid="update-button"]`; export const ConfirmDeleteDocumentButton = `${DocumentFooter} [data-testid="delete-button"]`; export const JSONDocumentCard = '[data-testid="editable-json"]'; diff --git a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts index 1819c645d6d..88fbac58678 100644 --- a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts @@ -20,6 +20,7 @@ import { import { context as testRunnerContext } from '../helpers/test-runner-context'; import type { ChainablePromiseElement } from 'webdriverio'; import { tryToInsertDocument } from '../helpers/commands/try-to-insert-document'; +import { isClickable } from 'webdriverio/build/commands/element'; const { expect } = chai; @@ -725,5 +726,156 @@ FindIterable result = collection.find(filter);`); await browser.clickVisible(Selectors.ErrorDetailsBackButton); await errorElement.waitForDisplayed(); }); + + describe('Editing', function () { + beforeEach(async function () { + await browser.navigateWithinCurrentCollectionTabs('Documents'); + await tryToInsertDocument(browser, `{ "phone": 12345 }`); + await browser.runFindOperation('Documents', '{ "phone": 12345 }'); + }); + + it.only('shows error info when editing via list view', async function () { + const document = browser.$(Selectors.DocumentListEntry); + await document.waitForDisplayed(); + + // enter edit mode + await browser.hover(Selectors.DocumentListEntry); + await browser.clickVisible(Selectors.EditDocumentButton); + + // delete the required field + await browser.hover(`${Selectors.HadronDocumentElement}:last-child`); + const deleteBtn = browser.$( + `${Selectors.HadronDocumentElement}:last-child ${Selectors.HadronDocumentRemoveElement}` + ); + console.log({ + isDisplayed: await deleteBtn.isDisplayed(), + isClickable: await deleteBtn.isClickable(), + isEnabled: await deleteBtn.isEnabled(), + }); + + await deleteBtn.waitForDisplayed(); + console.log('I am displayed'); + await deleteBtn.waitForClickable(); + console.log('I am clickable'); + await deleteBtn.click(); + + // confirm update + const footer = document.$(Selectors.DocumentFooterMessage); + expect(await footer.getText()).to.equal('Document modified.'); + + const button = document.$(Selectors.UpdateDocumentButton); + await button.click(); + + const errorMessage = browser.$(Selectors.DocumentFooterMessage); + await errorMessage.waitForDisplayed(); + expect(await errorMessage.getText()).to.include( + 'Document failed validation' + ); + + // enter details + const errorDetailsBtn = browser.$( + Selectors.DocumentFooterErrorDetailsButton + ); + await errorDetailsBtn.waitForDisplayed(); + console.log({ errorDetailsBtn }); + await errorDetailsBtn.click(); + + const errorDetailsJson = browser.$(Selectors.ErrorDetailsJson); + await errorDetailsJson.waitForDisplayed(); + + // exit details + await browser.clickVisible(Selectors.ErrorDetailsCloseButton); + await errorDetailsJson.waitForDisplayed({ reverse: true }); + }); + + it('shows error info when editing via json view', async function () { + await browser.clickVisible(Selectors.SelectJSONView); + + const document = browser.$(Selectors.DocumentJSONEntry); + await document.waitForDisplayed(); + + await waitForJSON(browser, document); + + // enter edit mode + await browser.hover(Selectors.JSONDocumentCard); + await browser.clickVisible(Selectors.JSONEditDocumentButton); + + // remove the required field + await browser.setCodemirrorEditorValue( + Selectors.DocumentJSONEntry, + `{}` + ); + + // confirm update + const footer = document.$(Selectors.DocumentFooterMessage); + expect(await footer.getText()).to.equal('Document modified.'); + + const button = document.$(Selectors.UpdateDocumentButton); + await button.click(); + + const errorMessage = browser.$(Selectors.DocumentFooterMessage); + await errorMessage.waitForDisplayed(); + expect(await errorMessage.getText()).to.include( + 'Document failed validation' + ); + + // enter details + const errorDetailsBtn = browser.$( + Selectors.DocumentFooterErrorDetailsButton + ); + await errorDetailsBtn.waitForDisplayed(); + await errorDetailsBtn.click(); + + const errorDetailsJson = browser.$(Selectors.ErrorDetailsJson); + await errorDetailsJson.waitForDisplayed(); + + // exit details + await browser.clickVisible(Selectors.ErrorDetailsCloseButton); + await errorDetailsJson.waitForDisplayed({ reverse: true }); + }); + + it('shows error info when editing via table view', async function () { + await browser.clickVisible(Selectors.SelectTableView); + + const document = browser.$('.ag-center-cols-clipper .ag-row-first'); + await document.waitForDisplayed(); + + // enter edit mode + const value = document.$('[col-id="phone"] .element-value'); + await value.doubleClick(); + + // remove the required field + await browser.clickVisible( + '[data-testid="table-view-cell-editor-remove-field-button"]' + ); + + // confirm update + const footer = browser.$(Selectors.DocumentFooterMessage); + expect(await footer.getText()).to.equal('Document modified.'); + + const button = browser.$(Selectors.UpdateDocumentButton); + await button.click(); + + const errorMessage = browser.$(Selectors.DocumentFooterMessage); + await errorMessage.waitForDisplayed(); + expect(await errorMessage.getText()).to.include( + 'Document failed validation' + ); + + // enter details + const errorDetailsBtn = browser.$( + Selectors.DocumentFooterErrorDetailsButton + ); + await errorDetailsBtn.waitForDisplayed(); + await errorDetailsBtn.click(); + + const errorDetailsJson = browser.$(Selectors.ErrorDetailsJson); + await errorDetailsJson.waitForDisplayed(); + + // exit details + await browser.clickVisible(Selectors.ErrorDetailsCloseButton); + await errorDetailsJson.waitForDisplayed({ reverse: true }); + }); + }); }); }); From a16eabafc5e29c39fa332f8b160e8850ce159603 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Thu, 6 Mar 2025 14:05:27 +0100 Subject: [PATCH 04/14] styling --- .../document-edit-actions-footer.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 8f5a53e306b..775ebbd0fe6 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -202,25 +202,25 @@ function useHadronDocumentStatus( const container = css({ display: 'flex', - paddingTop: spacing[2], - paddingRight: spacing[2], - paddingBottom: spacing[2], - paddingLeft: spacing[3], + paddingTop: spacing[200], + paddingRight: spacing[200], + paddingBottom: spacing[200], + paddingLeft: spacing[400], alignItems: 'center', - gap: spacing[2], + gap: spacing[200], borderBottomLeftRadius: 'inherit', borderBottomRightRadius: 'inherit', }); const message = css({ - overflow: 'hidden', - textOverflow: 'ellipsis', + overflow: 'scroll', }); const buttonGroup = css({ display: 'flex', marginLeft: 'auto', - gap: spacing[2], + gap: spacing[200], + flexShrink: 0, }); const button = css({ From d872d618526a5deff4fe78a42b85f3ea645a25d6 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Fri, 14 Mar 2025 14:51:44 +0100 Subject: [PATCH 05/14] cleanup --- package-lock.json | 2 ++ packages/compass-components/package.json | 1 + .../document-edit-actions-footer.tsx | 23 +++++++++++++------ .../tests/collection-documents-tab.test.ts | 8 +------ packages/hadron-document/src/document.ts | 5 ++-- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1532a6bcee0..421b765ba6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44119,6 +44119,7 @@ "chai": "^4.3.4", "eslint": "^7.25.0", "mocha": "^10.2.0", + "mongodb": "^6.12.0", "nyc": "^15.1.0", "prettier": "^2.7.1", "react-dom": "^17.0.2", @@ -57049,6 +57050,7 @@ "is-electron-renderer": "^2.0.1", "lodash": "^4.17.21", "mocha": "^10.2.0", + "mongodb": "^6.12.0", "nyc": "^15.1.0", "polished": "^4.2.2", "prettier": "^2.7.1", diff --git a/packages/compass-components/package.json b/packages/compass-components/package.json index 03ddf3feba7..73bf950c21b 100644 --- a/packages/compass-components/package.json +++ b/packages/compass-components/package.json @@ -97,6 +97,7 @@ "@mongodb-js/prettier-config-compass": "^1.2.5", "@mongodb-js/testing-library-compass": "^1.2.5", "@mongodb-js/tsconfig-compass": "^1.2.5", + "mongodb": "^6.12.0", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 775ebbd0fe6..ce9f412b21c 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import type { MongoServerError } from 'mongodb'; import type HadronDocument from 'hadron-document'; import { Element } from 'hadron-document'; import { Button } from '../leafygreen'; @@ -89,13 +88,17 @@ function useHadronDocumentStatus( const invalidElementsRef = useRef(new Set()); const updateStatus = useCallback( - (newStatus: Status, error: Error | MongoServerError | null = null) => { + ( + newStatus: Status, + error: Error | null = null, + errorDetails?: Record + ) => { setStatus(newStatus); setError( error ? { message: error?.message, - details: (error as MongoServerError).errInfo, + details: errorDetails, } : null ); @@ -142,8 +145,11 @@ function useHadronDocumentStatus( const onUpdateSuccess = () => { updateStatus('UpdateSuccess'); }; - const onUpdateError = (err: Error | MongoServerError) => { - updateStatus('UpdateError', err); + const onUpdateError = ( + err: Error, + errorDetails?: Record + ) => { + updateStatus('UpdateError', err, errorDetails); }; const onRemoveStart = () => { updateStatus('DeleteStart'); @@ -151,8 +157,11 @@ function useHadronDocumentStatus( const onRemoveSuccess = () => { updateStatus('DeleteSuccess'); }; - const onRemoveError = (err: Error | MongoServerError) => { - updateStatus('DeleteError', err); + const onRemoveError = ( + err: Error, + errorDetails?: Record + ) => { + updateStatus('DeleteError', err, errorDetails); }; doc.on(Element.Events.Added, onUpdate); diff --git a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts index 88fbac58678..24902649e45 100644 --- a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts @@ -20,7 +20,6 @@ import { import { context as testRunnerContext } from '../helpers/test-runner-context'; import type { ChainablePromiseElement } from 'webdriverio'; import { tryToInsertDocument } from '../helpers/commands/try-to-insert-document'; -import { isClickable } from 'webdriverio/build/commands/element'; const { expect } = chai; @@ -734,7 +733,7 @@ FindIterable result = collection.find(filter);`); await browser.runFindOperation('Documents', '{ "phone": 12345 }'); }); - it.only('shows error info when editing via list view', async function () { + it('shows error info when editing via list view', async function () { const document = browser.$(Selectors.DocumentListEntry); await document.waitForDisplayed(); @@ -747,11 +746,6 @@ FindIterable result = collection.find(filter);`); const deleteBtn = browser.$( `${Selectors.HadronDocumentElement}:last-child ${Selectors.HadronDocumentRemoveElement}` ); - console.log({ - isDisplayed: await deleteBtn.isDisplayed(), - isClickable: await deleteBtn.isClickable(), - isEnabled: await deleteBtn.isEnabled(), - }); await deleteBtn.waitForDisplayed(); console.log('I am displayed'); diff --git a/packages/hadron-document/src/document.ts b/packages/hadron-document/src/document.ts index 54ce8337eed..76068b3d6de 100644 --- a/packages/hadron-document/src/document.ts +++ b/packages/hadron-document/src/document.ts @@ -12,6 +12,7 @@ import type { BSONArray, BSONObject, BSONValue } from './utils'; import { objectToIdiomaticEJSON } from './utils'; import type { HadronEJSONOptions } from './utils'; import { DocumentEvents } from '.'; +import type { MongoServerError } from 'mongodb'; /** * The event constant. @@ -478,7 +479,7 @@ export class Document extends EventEmitter { } onUpdateError(error: Error) { - this.emit('update-error', error); + this.emit('update-error', error, (error as MongoServerError).errInfo); } markForDeletion() { @@ -505,7 +506,7 @@ export class Document extends EventEmitter { } onRemoveError(error: Error) { - this.emit('remove-error', error); + this.emit('remove-error', error, (error as MongoServerError).errInfo); } setModifiedEJSONString(ejson: string | null) { From 7375b3d3f06c237fe65660160c49c117661777ea Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Fri, 14 Mar 2025 15:06:49 +0100 Subject: [PATCH 06/14] fix type --- packages/compass-crud/src/components/editable-document.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/compass-crud/src/components/editable-document.tsx b/packages/compass-crud/src/components/editable-document.tsx index 17f46436361..5a7ee35a0d0 100644 --- a/packages/compass-crud/src/components/editable-document.tsx +++ b/packages/compass-crud/src/components/editable-document.tsx @@ -18,6 +18,7 @@ export type EditableDocumentProps = { removeDocument?: CrudActions['removeDocument']; replaceDocument?: CrudActions['replaceDocument']; updateDocument?: CrudActions['updateDocument']; + openErrorDetailsDialog?: CrudActions['openErrorDetailsDialog']; openInsertDocumentDialog?: CrudActions['openInsertDocumentDialog']; copyToClipboard?: CrudActions['copyToClipboard']; showInsights?: boolean; From 03fdfe743f91552841c983080ce3e354b753ad61 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Mon, 17 Mar 2025 16:25:33 +0100 Subject: [PATCH 07/14] refactor: hook --- .../document-edit-actions-footer.tsx | 12 +- .../components/modals/error-details-modal.tsx | 2 +- .../src/hooks/use-error-details.tsx | 134 ++++++++++++ packages/compass-components/src/index.ts | 5 + packages/compass-crud/src/actions/index.ts | 2 - .../src/components/document-json-view.tsx | 2 - .../components/document-list-view.spec.tsx | 1 - .../src/components/document-list-view.tsx | 1 - .../src/components/document-list.tsx | 198 ++++++++---------- .../src/components/editable-document.tsx | 8 - .../src/components/insert-document-dialog.tsx | 12 +- .../src/components/json-editor.tsx | 8 - .../table-view/document-table-view.tsx | 3 - .../table-view/full-width-cell-renderer.tsx | 59 +++--- .../virtualized-document-json-view.tsx | 4 - .../virtualized-document-list-view.tsx | 4 - .../src/stores/crud-store.spec.ts | 36 ---- .../compass-crud/src/stores/crud-store.ts | 39 ---- .../tests/collection-documents-tab.test.ts | 15 +- .../tests/collection-import.test.ts | 9 - .../tests/collection-validation-tab.test.ts | 2 +- .../import-error-details-modal.spec.tsx | 72 ------- .../components/import-error-details-modal.tsx | 33 --- .../src/import-plugin.tsx | 2 - .../src/modules/import.ts | 45 +--- 25 files changed, 285 insertions(+), 423 deletions(-) create mode 100644 packages/compass-components/src/hooks/use-error-details.tsx delete mode 100644 packages/compass-import-export/src/components/import-error-details-modal.spec.tsx delete mode 100644 packages/compass-import-export/src/components/import-error-details-modal.tsx diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index ce9f412b21c..2759a96ed5b 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -6,6 +6,7 @@ import { css } from '@leafygreen-ui/emotion'; import { palette } from '@leafygreen-ui/palette'; import { spacing } from '@leafygreen-ui/tokens'; import { useDarkMode } from '../../hooks/use-theme'; +import { useErrorDetailsModal } from '../../hooks/use-error-details'; type Status = | 'Initial' @@ -284,7 +285,6 @@ const EditActionsFooter: React.FunctionComponent<{ onUpdate(force: boolean): void; onDelete(): void; onCancel?: () => void; - onOpenErrorDetails?: (details: Record) => void; }> = ({ doc, editing, @@ -295,7 +295,6 @@ const EditActionsFooter: React.FunctionComponent<{ onUpdate, onDelete, onCancel, - onOpenErrorDetails, }) => { const { status: _status, @@ -305,6 +304,8 @@ const EditActionsFooter: React.FunctionComponent<{ const darkMode = useDarkMode(); + const { showErrorDetails } = useErrorDetailsModal(); + // Allow props to override event based status of the document (helpful for // JSON editor where changing the document text doesn't really generate any // changes of the HadronDocument) @@ -336,7 +337,12 @@ const EditActionsFooter: React.FunctionComponent<{
); diff --git a/packages/compass-crud/src/components/table-view/document-table-view.tsx b/packages/compass-crud/src/components/table-view/document-table-view.tsx index 66f0e342ed7..9f09c67d0b6 100644 --- a/packages/compass-crud/src/components/table-view/document-table-view.tsx +++ b/packages/compass-crud/src/components/table-view/document-table-view.tsx @@ -56,7 +56,6 @@ export type DocumentTableViewProps = { ns: string; version: string; openInsertDocumentDialog?: CrudActions['openInsertDocumentDialog']; - openErrorDetailsDialog?: CrudActions['openErrorDetailsDialog']; pathChanged: (path: (string | number)[], types: TableHeaderType[]) => void; removeColumn: GridActions['removeColumn']; copyToClipboard: (doc: Document) => void; @@ -144,7 +143,6 @@ class DocumentTableView extends React.Component { removeDocument: this.props.removeDocument, replaceDocument: this.props.replaceDocument, updateDocument: this.props.updateDocument, - openErrorDetailsDialog: this.props.openErrorDetailsDialog, darkMode: this.props.darkMode, }, getRowNodeId: function (data) { @@ -1033,7 +1031,6 @@ class DocumentTableView extends React.Component { ns: PropTypes.string.isRequired, version: PropTypes.string.isRequired, openInsertDocumentDialog: PropTypes.func, - openErrorDetailsDialog: PropTypes.func, pathChanged: PropTypes.func.isRequired, removeColumn: PropTypes.func.isRequired, copyToClipboard: PropTypes.func.isRequired, diff --git a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx index 8e7d906ea20..0b6d81c00f4 100644 --- a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { DocumentList, + ErrorDetailsModalArea, LeafyGreenProvider, } from '@mongodb-js/compass-components'; import type Document from 'hadron-document'; @@ -20,7 +21,6 @@ export type FullWidthCellRendererProps = Pick< replaceDocument: CrudActions['replaceDocument']; removeDocument: CrudActions['removeDocument']; updateDocument: CrudActions['updateDocument']; - openErrorDetailsDialog: CrudActions['openErrorDetailsDialog']; darkMode?: boolean; }; @@ -128,36 +128,32 @@ class FullWidthCellRenderer extends React.Component< // this is needed cause ag-grid renders this component outside // of the context chain - { - this.props.api.stopEditing(); - if (force) { - this.props.replaceDocument(this.doc); - } else { - this.props.updateDocument(this.doc); - } - }} - onDelete={() => { - this.props.api.stopEditing(); - this.props.removeDocument(this.doc); - }} - onCancel={() => { - if (this.state.mode === 'editing') { - this.handleCancelUpdate(); - } else { - this.handleCancelRemove(); - } - }} - onOpenErrorDetails={(details: Record) => { - this.props.openErrorDetailsDialog?.({ - details, - closeAction: 'close', - }); - }} - /> + + { + this.props.api.stopEditing(); + if (force) { + this.props.replaceDocument(this.doc); + } else { + this.props.updateDocument(this.doc); + } + }} + onDelete={() => { + this.props.api.stopEditing(); + this.props.removeDocument(this.doc); + }} + onCancel={() => { + if (this.state.mode === 'editing') { + this.handleCancelUpdate(); + } else { + this.handleCancelRemove(); + } + }} + /> + ); } @@ -170,7 +166,6 @@ class FullWidthCellRenderer extends React.Component< updateDocument: PropTypes.func.isRequired, removeDocument: PropTypes.func.isRequired, replaceDocument: PropTypes.func.isRequired, - openErrorDetailsDialog: PropTypes.func.isRequired, replaceDoc: PropTypes.func.isRequired, cleanCols: PropTypes.func.isRequired, darkMode: PropTypes.bool, diff --git a/packages/compass-crud/src/components/virtualized-document-json-view.tsx b/packages/compass-crud/src/components/virtualized-document-json-view.tsx index 12e594bba44..8cffcf8f26f 100644 --- a/packages/compass-crud/src/components/virtualized-document-json-view.tsx +++ b/packages/compass-crud/src/components/virtualized-document-json-view.tsx @@ -53,7 +53,6 @@ export type VirtualizedDocumentJsonViewProps = { | 'replaceDocument' | 'updateDocument' | 'openInsertDocumentDialog' - | 'openErrorDetailsDialog' >; const VirtualizedDocumentJsonView: React.FC< @@ -71,7 +70,6 @@ const VirtualizedDocumentJsonView: React.FC< replaceDocument, updateDocument, openInsertDocumentDialog, - openErrorDetailsDialog, __TEST_OVERSCAN_COUNT, __TEST_LIST_HEIGHT, listRef, @@ -92,7 +90,6 @@ const VirtualizedDocumentJsonView: React.FC< replaceDocument={replaceDocument} updateDocument={updateDocument} openInsertDocumentDialog={openInsertDocumentDialog} - openErrorDetailsDialog={openErrorDetailsDialog} /> ); @@ -104,7 +101,6 @@ const VirtualizedDocumentJsonView: React.FC< scrollTriggerRef, copyToClipboard, openInsertDocumentDialog, - openErrorDetailsDialog, removeDocument, replaceDocument, updateDocument, diff --git a/packages/compass-crud/src/components/virtualized-document-list-view.tsx b/packages/compass-crud/src/components/virtualized-document-list-view.tsx index 5446284359e..5ddfa237fb4 100644 --- a/packages/compass-crud/src/components/virtualized-document-list-view.tsx +++ b/packages/compass-crud/src/components/virtualized-document-list-view.tsx @@ -58,7 +58,6 @@ type VirtualizedDocumentListViewProps = { | 'replaceDocument' | 'updateDocument' | 'openInsertDocumentDialog' - | 'openErrorDetailsDialog' >; const VirtualizedDocumentListView: React.FC< @@ -75,7 +74,6 @@ const VirtualizedDocumentListView: React.FC< replaceDocument, updateDocument, openInsertDocumentDialog, - openErrorDetailsDialog, listRef, __TEST_OVERSCAN_COUNT, __TEST_LIST_HEIGHT, @@ -106,7 +104,6 @@ const VirtualizedDocumentListView: React.FC< replaceDocument={replaceDocument} updateDocument={updateDocument} openInsertDocumentDialog={openInsertDocumentDialog} - openErrorDetailsDialog={openErrorDetailsDialog} /> ); @@ -117,7 +114,6 @@ const VirtualizedDocumentListView: React.FC< scrollTriggerRef, copyToClipboard, openInsertDocumentDialog, - openErrorDetailsDialog, removeDocument, replaceDocument, updateDocument, diff --git a/packages/compass-crud/src/stores/crud-store.spec.ts b/packages/compass-crud/src/stores/crud-store.spec.ts index 045b3cf9141..5b78d8d63ba 100644 --- a/packages/compass-crud/src/stores/crud-store.spec.ts +++ b/packages/compass-crud/src/stores/crud-store.spec.ts @@ -12,7 +12,6 @@ import type { CrudStore, CrudStoreOptions, DocumentsPluginServices, - ErrorDetailsDialogOptions, } from './crud-store'; import { findAndModifyWithFLEFallback, @@ -1455,41 +1454,6 @@ describe('store', function () { }); }); - describe('#openErrorDetailsDialog #closeErrorDetailsDialog', function () { - const options: ErrorDetailsDialogOptions = { - details: { abc: 'abc' }, - closeAction: 'close', - }; - let store: CrudStore; - - beforeEach(function () { - const plugin = activatePlugin(); - store = plugin.store; - deactivate = () => plugin.deactivate(); - }); - - it('manages the errorDetails state', async function () { - const openListener = waitForState(store, (state) => { - expect(state.errorDetails).to.deep.equal({ - isOpen: true, - ...options, - }); - }); - - void store.openErrorDetailsDialog(options); - - await openListener; - - const closeListener = waitForState(store, (state) => { - expect(state.errorDetails.isOpen).to.be.false; - }); - - void store.closeErrorDetailsDialog(); - - await closeListener; - }); - }); - describe('#openInsertDocumentDialog', function () { const doc = { _id: 1, name: 'test' }; let store: CrudStore; diff --git a/packages/compass-crud/src/stores/crud-store.ts b/packages/compass-crud/src/stores/crud-store.ts index 56db6a43287..4ec244ec0f1 100644 --- a/packages/compass-crud/src/stores/crud-store.ts +++ b/packages/compass-crud/src/stores/crud-store.ts @@ -81,23 +81,6 @@ export type EmittedAppRegistryEvents = | 'document-deleted' | 'document-inserted'; -export type ErrorDetailsDialogState = - | { - isOpen: false; - details?: Record; - closeAction?: 'back' | 'close'; - } - | { - isOpen: true; - details: Record; - closeAction?: 'back' | 'close'; - }; - -export type ErrorDetailsDialogOptions = Omit< - Extract, - 'isOpen' ->; - export type CrudActions = { drillDown( doc: Document, @@ -111,8 +94,6 @@ export type CrudActions = { removeDocument(doc: Document): void; replaceDocument(doc: Document): void; openInsertDocumentDialog(doc: BSONObject, cloned: boolean): void; - openErrorDetailsDialog(options: ErrorDetailsDialogOptions): void; - closeErrorDetailsDialog(): void; copyToClipboard(doc: Document): void; //XXX openBulkDeleteDialog(): void; runBulkUpdate(): void; @@ -360,7 +341,6 @@ type CrudState = { bulkDelete: BulkDeleteState; docsPerPage: number; collectionStats: CollectionStats | null; - errorDetails: ErrorDetailsDialogState; }; type CrudStoreActionsOptions = { @@ -467,7 +447,6 @@ class CrudStoreImpl this.instance.topologyDescription.type !== 'Single', docsPerPage: this.getInitialDocsPerPage(), collectionStats: extractCollectionStats(this.collection), - errorDetails: { isOpen: false }, }; } @@ -975,24 +954,6 @@ class CrudStoreImpl }); } - openErrorDetailsDialog(options: ErrorDetailsDialogOptions) { - this.setState({ - errorDetails: { - isOpen: true, - ...options, - }, - }); - } - - closeErrorDetailsDialog() { - this.setState({ - errorDetails: { - ...this.state.errorDetails, - isOpen: false, - }, - }); - } - /** * Open the insert document dialog. * diff --git a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts index 24902649e45..e1c6a51b4be 100644 --- a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts @@ -741,17 +741,11 @@ FindIterable result = collection.find(filter);`); await browser.hover(Selectors.DocumentListEntry); await browser.clickVisible(Selectors.EditDocumentButton); - // delete the required field - await browser.hover(`${Selectors.HadronDocumentElement}:last-child`); - const deleteBtn = browser.$( - `${Selectors.HadronDocumentElement}:last-child ${Selectors.HadronDocumentRemoveElement}` + // rename the required field + const input = document.$( + `${Selectors.HadronDocumentElement}:last-child ${Selectors.HadronDocumentKeyEditor}` ); - - await deleteBtn.waitForDisplayed(); - console.log('I am displayed'); - await deleteBtn.waitForClickable(); - console.log('I am clickable'); - await deleteBtn.click(); + await browser.setValueVisible(input, 'somethingElse'); // confirm update const footer = document.$(Selectors.DocumentFooterMessage); @@ -771,7 +765,6 @@ FindIterable result = collection.find(filter);`); Selectors.DocumentFooterErrorDetailsButton ); await errorDetailsBtn.waitForDisplayed(); - console.log({ errorDetailsBtn }); await errorDetailsBtn.click(); const errorDetailsJson = browser.$(Selectors.ErrorDetailsJson); diff --git a/packages/compass-e2e-tests/tests/collection-import.test.ts b/packages/compass-e2e-tests/tests/collection-import.test.ts index 28a3c57e623..264069fe8d4 100644 --- a/packages/compass-e2e-tests/tests/collection-import.test.ts +++ b/packages/compass-e2e-tests/tests/collection-import.test.ts @@ -575,15 +575,6 @@ describe('Collection import', function () { 'schemaRulesNotSatisfied' ); await browser.clickVisible(Selectors.ErrorDetailsCloseButton); - - // Close the toast - await browser - .$(Selectors.closeToastButton(Selectors.ImportToast)) - .waitForDisplayed(); - await browser.clickVisible( - Selectors.closeToastButton(Selectors.ImportToast) - ); - await toastElement.waitForDisplayed({ reverse: true }); }); it('with CSV + abort on error unchecked, it includes the details in a file', async function () { diff --git a/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts b/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts index ae8fd286eeb..4c56ae5c8a5 100644 --- a/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts @@ -89,7 +89,7 @@ describe('Collection validation tab', function () { }); // generated rules can be edited and saved - await browser.setValidation(PASSING_VALIDATOR); + await browser.setValidationWithinValidationTab(PASSING_VALIDATOR); }); }); diff --git a/packages/compass-import-export/src/components/import-error-details-modal.spec.tsx b/packages/compass-import-export/src/components/import-error-details-modal.spec.tsx deleted file mode 100644 index c8c890bf1be..00000000000 --- a/packages/compass-import-export/src/components/import-error-details-modal.spec.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { - render, - screen, - userEvent, - waitFor, -} from '@mongodb-js/testing-library-compass'; -import { expect } from 'chai'; -import { configureStore } from '../stores/import-store'; -import { Provider } from 'react-redux'; - -import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; -import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; -import ImportErrorDetailsModal from './import-error-details-modal'; - -function renderModal(importState: any = {}) { - // TODO: mutating state directly doesn't guarantee that we are testing the - // component in a state that can actually be achieved when actions are emitted - // on the store. Refactor this to either test unconnected component, or to - // not mutate state directly for tests - const store = configureStore({ - dataService: {}, - globalAppRegistry: {}, - logger: createNoopLogger(), - track: createNoopTrack(), - connections: { - getConnectionById: () => ({ info: { id: 'TEST' } }), - }, - } as any); - const state = store.getState(); - state.import = { - ...state.import, - ...importState, - }; - const renderResult = render( - - - - ); - return { renderResult, store }; -} - -describe('ImportErrorDetailsModal Component', function () { - context('When import error details are open', function () { - const errorDetails = { details: 'abc' }; - - beforeEach(function () { - renderModal({ - errorDetails: { - isOpen: true, - details: errorDetails, - }, - }); - }); - - it('Should render error details and be closable', async function () { - const codeDetails = await screen.findByTestId('error-details-json'); - expect(codeDetails).to.be.visible; - expect(JSON.parse(codeDetails.textContent || '')).to.deep.equal( - errorDetails - ); - - const closeBtn = await screen.findByRole('button', { name: 'Close' }); - expect(closeBtn).to.be.visible; - - userEvent.click(closeBtn); - await waitFor(() => { - expect(screen.queryByTestId('import-error-details-modal')).not.to.exist; - }); - }); - }); -}); diff --git a/packages/compass-import-export/src/components/import-error-details-modal.tsx b/packages/compass-import-export/src/components/import-error-details-modal.tsx deleted file mode 100644 index c425ac44867..00000000000 --- a/packages/compass-import-export/src/components/import-error-details-modal.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import { ErrorDetailsModal } from '@mongodb-js/compass-components'; -import { connect } from 'react-redux'; -import type { RootImportState } from '../stores/import-store'; -import { onErrorDetailsClose } from '../modules/import'; - -const ImportErrorDetailsModal: React.FunctionComponent<{ - isOpen: boolean; - errorDetails?: Record; - onClose: () => void; -}> = ({ isOpen, errorDetails, onClose }) => { - return ( - - ); -}; - -const ConnectedImportErrorDetailsModal = connect( - (state: RootImportState) => ({ - isOpen: state.import.errorDetails.isOpen, - errorDetails: state.import.errorDetails.details, - }), - { - onClose: onErrorDetailsClose, - } -)(ImportErrorDetailsModal); - -export default ConnectedImportErrorDetailsModal; diff --git a/packages/compass-import-export/src/import-plugin.tsx b/packages/compass-import-export/src/import-plugin.tsx index 3fd4fa31c66..e260279aa4b 100644 --- a/packages/compass-import-export/src/import-plugin.tsx +++ b/packages/compass-import-export/src/import-plugin.tsx @@ -1,14 +1,12 @@ import React from 'react'; import ImportModal from './components/import-modal'; import ImportInProgressModal from './components/import-in-progress-modal'; -import ImportErrorDetailsModal from './components/import-error-details-modal'; function ImportPlugin() { return ( <> - ); } diff --git a/packages/compass-import-export/src/modules/import.ts b/packages/compass-import-export/src/modules/import.ts index 3ebb0f3ab97..412e2a8ef9e 100644 --- a/packages/compass-import-export/src/modules/import.ts +++ b/packages/compass-import-export/src/modules/import.ts @@ -35,6 +35,7 @@ import { import type { ImportThunkAction } from '../stores/import-store'; import { openFile } from '../utils/open-file'; import type { DataService } from 'mongodb-data-service'; +import { showErrorDetails } from '@mongodb-js/compass-components'; const checkFileExists = promisify(fs.exists); const getFileStats = promisify(fs.stat); @@ -82,16 +83,6 @@ type FieldFromJSON = { }; type FieldType = FieldFromJSON | FieldFromCSV; -export type ErrorDetailsDialogState = - | { - isOpen: false; - details?: Record; - } - | { - isOpen: true; - details: Record; - }; - type ImportState = { isOpen: boolean; isInProgressMessageOpen: boolean; @@ -99,7 +90,6 @@ type ImportState = { fileType: AcceptedFileType | ''; fileName: string; errorLogFilePath: string; - errorDetails: ErrorDetailsDialogState; fileIsMultilineJSON: boolean; useHeaderLines: boolean; status: ProcessStatus; @@ -135,7 +125,6 @@ export const INITIAL_STATE: ImportState = { firstErrors: [], fileName: '', errorLogFilePath: '', - errorDetails: { isOpen: false }, fileIsMultilineJSON: false, useHeaderLines: true, status: PROCESS_STATUS.UNSPECIFIED, @@ -183,8 +172,6 @@ const onFinished = ({ const onFailed = (error: Error) => ({ type: FAILED, error }); -export const onErrorDetailsClose = () => ({ type: ERROR_DETAILS_CLOSED }); - const onFileSelectError = (error: Error) => ({ type: FILE_SELECT_ERROR, error, @@ -391,10 +378,12 @@ export const startImport = (): ImportThunkAction> => { progressCallback.flush(); const errInfo = err?.writeErrors?.length && err?.writeErrors[0]?.err?.errInfo; - const showErrorDetails: () => void | undefined = - errInfo && - (() => dispatch({ type: ERROR_DETAILS_OPENED, errorDetails: errInfo })); - showFailedToast(err as Error, showErrorDetails); + showFailedToast(err as Error, () => + showErrorDetails({ + details: errInfo, + closeAction: 'close', + }) + ); dispatch(onFailed(err as Error)); return; @@ -1090,26 +1079,6 @@ export const importReducer: Reducer = ( }; } - if (action.type === ERROR_DETAILS_OPENED) { - return { - ...state, - errorDetails: { - isOpen: true, - details: action.errorDetails, - }, - }; - } - - if (action.type === ERROR_DETAILS_CLOSED) { - return { - ...state, - errorDetails: { - ...state.errorDetails, - isOpen: false, - }, - }; - } - if (action.type === STARTED) { return { ...state, From 80af7bb199a6f83091c3d1e73d2584d329359b0d Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Mon, 17 Mar 2025 17:26:57 +0100 Subject: [PATCH 08/14] do not use the event with the hook --- .../compass-components/src/hooks/use-error-details.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/compass-components/src/hooks/use-error-details.tsx b/packages/compass-components/src/hooks/use-error-details.tsx index d799e4a1407..fd65cd2ba1e 100644 --- a/packages/compass-components/src/hooks/use-error-details.tsx +++ b/packages/compass-components/src/hooks/use-error-details.tsx @@ -77,8 +77,12 @@ export const ErrorDetailsModalArea: React.FC = ({ children }) => { }); const contextValue = React.useMemo( - () => ({ showErrorDetails, isMounted: true }), - [] + () => ({ + showErrorDetails: (options: ErrorDetailsOptions) => + setErrorDetailsProps({ open: true, ...options }), + isMounted: true, + }), + [setErrorDetailsProps] ); // Event listener to use confirmation modal outside of react From 6bfc571987ed35d2c74d5666ab3053aa06e0fe96 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Mon, 17 Mar 2025 17:33:30 +0100 Subject: [PATCH 09/14] cleanup --- packages/compass-components/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/compass-components/package.json b/packages/compass-components/package.json index 73bf950c21b..03ddf3feba7 100644 --- a/packages/compass-components/package.json +++ b/packages/compass-components/package.json @@ -97,7 +97,6 @@ "@mongodb-js/prettier-config-compass": "^1.2.5", "@mongodb-js/testing-library-compass": "^1.2.5", "@mongodb-js/tsconfig-compass": "^1.2.5", - "mongodb": "^6.12.0", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", From 9ae12db32ade29dc328c9c80f27f57c85490bf0b Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Tue, 18 Mar 2025 11:29:40 +0100 Subject: [PATCH 10/14] wip --- .../src/hooks/use-confirmation.tsx | 23 ++- .../src/hooks/use-error-details.tsx | 165 ++++------------- .../src/components/document-list.tsx | 170 +++++++++--------- .../table-view/full-width-cell-renderer.tsx | 5 +- 4 files changed, 139 insertions(+), 224 deletions(-) diff --git a/packages/compass-components/src/hooks/use-confirmation.tsx b/packages/compass-components/src/hooks/use-confirmation.tsx index cc94cff7027..ff37513feb7 100644 --- a/packages/compass-components/src/hooks/use-confirmation.tsx +++ b/packages/compass-components/src/hooks/use-confirmation.tsx @@ -100,8 +100,28 @@ export const ConfirmationModalArea: React.FC = ({ children }) => { }); const callbackRef = useRef(); + const listenerRef = + useRef<(event: CustomEvent) => void>(); + const contextValue = React.useMemo( - () => ({ showConfirmation, isMounted: true }), + () => ({ + showConfirmation: (props: ConfirmationProperties) => { + return new Promise((resolve, reject) => { + const event = new CustomEvent( + 'show-confirmation', + { + detail: { + props: { ...props, confirmationId: ++confirmationId }, + resolve, + reject, + }, + } + ); + listenerRef.current?.(event); + }); + }, + isMounted: true, + }), [] ); @@ -127,6 +147,7 @@ export const ConfirmationModalArea: React.FC = ({ children }) => { }; props.signal?.addEventListener('abort', onAbort); }; + listenerRef.current = listener; globalConfirmation.addEventListener('show-confirmation', listener); return () => { globalConfirmation.removeEventListener('show-confirmation', listener); diff --git a/packages/compass-components/src/hooks/use-error-details.tsx b/packages/compass-components/src/hooks/use-error-details.tsx index fd65cd2ba1e..04aff9d9c02 100644 --- a/packages/compass-components/src/hooks/use-error-details.tsx +++ b/packages/compass-components/src/hooks/use-error-details.tsx @@ -1,138 +1,33 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { - ErrorDetailsModal, - type ErrorDetailsModalProps, -} from '../components/modals/error-details-modal'; - -type ErrorDetailsOptions = Omit; - -interface ErrorDetailsModalContextData { - showErrorDetails: (props: ErrorDetailsOptions) => void; - isMounted: boolean; -} - -let errorDetailsId = 0; - -interface ErrorDetailsEventMap { - 'show-error-details': CustomEvent; -} - -interface GlobalErrorDetails extends EventTarget { - addEventListener( - type: K, - listener: (this: GlobalErrorDetails, ev: ErrorDetailsEventMap[K]) => void - ): void; - addEventListener( - type: string, - listener: EventListenerOrEventListenerObject - ): void; - removeEventListener( - type: K, - listener: (this: GlobalErrorDetails, ev: ErrorDetailsEventMap[K]) => void - ): void; - removeEventListener( - type: string, - listener: EventListenerOrEventListenerObject - ): void; -} - -type ShowErrorDetailsEventDetail = ErrorDetailsOptions & { - errorDetailsId: number; -}; - -class GlobalErrorDetails extends EventTarget { - showErrorDetails(props: ErrorDetailsOptions) { - this.dispatchEvent( - new CustomEvent('show-error-details', { - detail: { - ...props, - errorDetailsId: ++errorDetailsId, - }, - }) - ); - } -} -const globalErrorDetails = new GlobalErrorDetails(); - -export const showErrorDetails = - globalErrorDetails.showErrorDetails.bind(globalErrorDetails); - -const ErrorDetailsModalContext = - React.createContext({ - isMounted: false, - showErrorDetails, - }); - -type ErrorDetailsModalAreaProps = Partial & { - open: boolean; -}; - -export const ErrorDetailsModalArea: React.FC = ({ children }) => { - const hasParentContext = useContext(ErrorDetailsModalContext).isMounted; - - const [errorDetailsProps, setErrorDetailsProps] = - useState({ - open: false, - errorDetailsId: -1, +import { useConfirmationModal } from './use-confirmation'; +import { Code } from '../components/leafygreen'; +import React from 'react'; + +export function useErrorDetailsModal() { + const { showConfirmation } = useConfirmationModal(); + + const showErrorDetails = ({ + details, + closeAction, + }: { + details: Record; + closeAction: 'back' | 'close'; + }) => + showConfirmation({ + title: 'Error details', + description: ( + + {JSON.stringify(details, undefined, 2)} + + ), + hideCancelButton: true, + buttonText: closeAction.replace(/\b\w/g, (c) => c.toUpperCase()), + // modalProps + // buttonProps }); - const contextValue = React.useMemo( - () => ({ - showErrorDetails: (options: ErrorDetailsOptions) => - setErrorDetailsProps({ open: true, ...options }), - isMounted: true, - }), - [setErrorDetailsProps] - ); - - // Event listener to use confirmation modal outside of react - useEffect(() => { - const listener = ({ detail }: CustomEvent) => { - setErrorDetailsProps({ open: true, ...detail }); - }; - globalErrorDetails.addEventListener('show-error-details', listener); - return () => { - globalErrorDetails.removeEventListener('show-error-details', listener); - }; - }, []); - - const handleClose = () => { - setErrorDetailsProps((state: ErrorDetailsModalAreaProps) => ({ - ...state, - open: false, - })); - }; - - if (hasParentContext) { - return <>{children}; - } - - return ( - - {children} - - - ); -}; - -export const useErrorDetailsModal = () => { - const { isMounted, showErrorDetails } = useContext(ErrorDetailsModalContext); - if (!isMounted) { - throw new Error( - 'useErrorDetailsModal must be used within a ErrorDetailsModalArea' - ); - } return { showErrorDetails }; -}; +} diff --git a/packages/compass-crud/src/components/document-list.tsx b/packages/compass-crud/src/components/document-list.tsx index 43ec90fa8e2..d6a9f941973 100644 --- a/packages/compass-crud/src/components/document-list.tsx +++ b/packages/compass-crud/src/components/document-list.tsx @@ -519,92 +519,90 @@ const DocumentList: React.FunctionComponent = (props) => { return (
- - - } - > - {renderContent} - - - {isEditable && ( - <> - - - - - )} - + + } + > + {renderContent} + + + {isEditable && ( + <> + + + + + )}
); }; diff --git a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx index 0b6d81c00f4..031bdcabcb7 100644 --- a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { + ConfirmationModalArea, DocumentList, ErrorDetailsModalArea, LeafyGreenProvider, @@ -128,7 +129,7 @@ class FullWidthCellRenderer extends React.Component< // this is needed cause ag-grid renders this component outside // of the context chain - + - + ); } From 154575e4f84f2ba1c4aaf4ee71092d5ef12385ca Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Tue, 18 Mar 2025 12:13:52 +0100 Subject: [PATCH 11/14] update tests --- packages/compass-e2e-tests/helpers/selectors.ts | 8 ++------ .../tests/collection-documents-tab.test.ts | 8 ++++---- .../compass-e2e-tests/tests/collection-import.test.ts | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 25f08009eb4..c8cc0345385 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -619,10 +619,6 @@ export const ShowMoreFieldsButton = '[data-testid="show-more-fields-button"]'; export const OpenBulkUpdateButton = '[data-testid="crud-update"]'; export const OpenBulkDeleteButton = '[data-testid="crud-bulk-delete"]'; export const ErrorDetailsJson = '[data-testid="error-details-json"]'; -export const ErrorDetailsBackButton = - '[data-testid="error-details-back-button"]'; -export const ErrorDetailsCloseButton = - '[data-testid="error-details-close-button"]'; // Insert Document modal @@ -1325,11 +1321,11 @@ export const ConfirmationModalInput = `${ConfirmationModal} input`; export const confirmationModalConfirmButton = ( modalSelector = ConfirmationModal -) => `${modalSelector} [role=dialog] button:nth-of-type(1)`; +) => `${modalSelector} [role=dialog] [data-testid*="confirm_button"]`; export const confirmationModalCancelButton = ( modalSelector = ConfirmationModal -) => `${modalSelector} [role=dialog] button:nth-of-type(2)`; +) => `${modalSelector} [role=dialog] [data-testid*="cancel_button"]`; // New pipeline from text modal export const NewPipelineFromTextModal = '[data-testid="import-pipeline-modal"]'; diff --git a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts index e1c6a51b4be..ad8a568c70d 100644 --- a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts @@ -722,7 +722,7 @@ FindIterable result = collection.find(filter);`); await errorDetailsJson.waitForDisplayed(); // exit details - await browser.clickVisible(Selectors.ErrorDetailsBackButton); + await browser.clickVisible(Selectors.confirmationModalConfirmButton()); await errorElement.waitForDisplayed(); }); @@ -771,7 +771,7 @@ FindIterable result = collection.find(filter);`); await errorDetailsJson.waitForDisplayed(); // exit details - await browser.clickVisible(Selectors.ErrorDetailsCloseButton); + await browser.clickVisible(Selectors.confirmationModalConfirmButton()); await errorDetailsJson.waitForDisplayed({ reverse: true }); }); @@ -817,7 +817,7 @@ FindIterable result = collection.find(filter);`); await errorDetailsJson.waitForDisplayed(); // exit details - await browser.clickVisible(Selectors.ErrorDetailsCloseButton); + await browser.clickVisible(Selectors.confirmationModalConfirmButton()); await errorDetailsJson.waitForDisplayed({ reverse: true }); }); @@ -860,7 +860,7 @@ FindIterable result = collection.find(filter);`); await errorDetailsJson.waitForDisplayed(); // exit details - await browser.clickVisible(Selectors.ErrorDetailsCloseButton); + await browser.clickVisible(Selectors.confirmationModalConfirmButton()); await errorDetailsJson.waitForDisplayed({ reverse: true }); }); }); diff --git a/packages/compass-e2e-tests/tests/collection-import.test.ts b/packages/compass-e2e-tests/tests/collection-import.test.ts index 264069fe8d4..cecf27e9a30 100644 --- a/packages/compass-e2e-tests/tests/collection-import.test.ts +++ b/packages/compass-e2e-tests/tests/collection-import.test.ts @@ -574,7 +574,7 @@ describe('Collection import', function () { expect(await errorDetailsModal.getText()).to.include( 'schemaRulesNotSatisfied' ); - await browser.clickVisible(Selectors.ErrorDetailsCloseButton); + await browser.clickVisible(Selectors.confirmationModalConfirmButton()); }); it('with CSV + abort on error unchecked, it includes the details in a file', async function () { From b0944f7ba880f3a189b5a006df732552363a4cbb Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Tue, 18 Mar 2025 13:40:54 +0100 Subject: [PATCH 12/14] cleanup and fix the non-react path --- package-lock.json | 2 - .../document-edit-actions-footer.tsx | 2 +- .../components/modals/error-details-modal.tsx | 69 ------------------- .../src/hooks/use-confirmation.tsx | 2 +- .../src/hooks/use-error-details.tsx | 24 +++++-- packages/compass-components/src/index.ts | 2 - .../src/components/insert-document-dialog.tsx | 2 +- .../src/modules/import.ts | 13 ++-- 8 files changed, 28 insertions(+), 88 deletions(-) delete mode 100644 packages/compass-components/src/components/modals/error-details-modal.tsx diff --git a/package-lock.json b/package-lock.json index 421b765ba6a..1532a6bcee0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44119,7 +44119,6 @@ "chai": "^4.3.4", "eslint": "^7.25.0", "mocha": "^10.2.0", - "mongodb": "^6.12.0", "nyc": "^15.1.0", "prettier": "^2.7.1", "react-dom": "^17.0.2", @@ -57050,7 +57049,6 @@ "is-electron-renderer": "^2.0.1", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.12.0", "nyc": "^15.1.0", "polished": "^4.2.2", "prettier": "^2.7.1", diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 2759a96ed5b..7724d38d3d4 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -339,7 +339,7 @@ const EditActionsFooter: React.FunctionComponent<{ size="xsmall" onClick={() => showErrorDetails({ - details: error.details, + details: error.details!, closeAction: 'close', }) } diff --git a/packages/compass-components/src/components/modals/error-details-modal.tsx b/packages/compass-components/src/components/modals/error-details-modal.tsx deleted file mode 100644 index e5a4e38c092..00000000000 --- a/packages/compass-components/src/components/modals/error-details-modal.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useMemo } from 'react'; - -import { css, cx } from '@leafygreen-ui/emotion'; - -import { Modal } from './modal'; -import { Button, Code, ModalFooter } from '../leafygreen'; -import { ModalBody } from './modal-body'; -import { ModalHeader } from './modal-header'; - -const leftDirectionFooter = css({ - justifyContent: 'left', -}); - -type ModalProps = React.ComponentProps; -export type ErrorDetailsModalProps = Omit & { - title?: string; - subtitle?: string; - details?: Record; - closeAction: 'close' | 'back'; - onClose: () => void; -}; - -function ErrorDetailsModal({ - title = 'Error details', - subtitle, - details, - closeAction, - onClose, - open, - ...modalProps -}: ErrorDetailsModalProps) { - const prettyDetails = useMemo( - () => JSON.stringify(details, undefined, 2), - [details] - ); - - return ( - - - - - {prettyDetails} - - - - - - - ); -} - -export { ErrorDetailsModal }; diff --git a/packages/compass-components/src/hooks/use-confirmation.tsx b/packages/compass-components/src/hooks/use-confirmation.tsx index ff37513feb7..38328e327ac 100644 --- a/packages/compass-components/src/hooks/use-confirmation.tsx +++ b/packages/compass-components/src/hooks/use-confirmation.tsx @@ -106,7 +106,7 @@ export const ConfirmationModalArea: React.FC = ({ children }) => { const contextValue = React.useMemo( () => ({ showConfirmation: (props: ConfirmationProperties) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const event = new CustomEvent( 'show-confirmation', { diff --git a/packages/compass-components/src/hooks/use-error-details.tsx b/packages/compass-components/src/hooks/use-error-details.tsx index 04aff9d9c02..65fc27ff4fa 100644 --- a/packages/compass-components/src/hooks/use-error-details.tsx +++ b/packages/compass-components/src/hooks/use-error-details.tsx @@ -1,18 +1,22 @@ -import { useConfirmationModal } from './use-confirmation'; +import { + type showConfirmation as originalShowConfirmation, + showConfirmation, + useConfirmationModal, +} from './use-confirmation'; import { Code } from '../components/leafygreen'; import React from 'react'; -export function useErrorDetailsModal() { - const { showConfirmation } = useConfirmationModal(); - - const showErrorDetails = ({ +const getShowErrorDetails = ( + showConfirmation: typeof originalShowConfirmation +) => { + return ({ details, closeAction, }: { details: Record; closeAction: 'back' | 'close'; }) => - showConfirmation({ + void showConfirmation({ title: 'Error details', description: ( = ({ className={errorDetailsBtnStyles} onClick={() => showErrorDetails({ - details: error.info, + details: error.info!, closeAction: 'back', }) } diff --git a/packages/compass-import-export/src/modules/import.ts b/packages/compass-import-export/src/modules/import.ts index 412e2a8ef9e..0357dfd2feb 100644 --- a/packages/compass-import-export/src/modules/import.ts +++ b/packages/compass-import-export/src/modules/import.ts @@ -378,11 +378,14 @@ export const startImport = (): ImportThunkAction> => { progressCallback.flush(); const errInfo = err?.writeErrors?.length && err?.writeErrors[0]?.err?.errInfo; - showFailedToast(err as Error, () => - showErrorDetails({ - details: errInfo, - closeAction: 'close', - }) + showFailedToast( + err as Error, + errInfo && + (() => + showErrorDetails({ + details: errInfo, + closeAction: 'close', + })) ); dispatch(onFailed(err as Error)); From 1ac104e993fd05debe273ea54e84556652d752f0 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Tue, 18 Mar 2025 14:21:13 +0100 Subject: [PATCH 13/14] cleanup hook --- .../document-edit-actions-footer.tsx | 4 +- .../src/hooks/use-confirmation.tsx | 3 ++ .../src/hooks/use-error-details.tsx | 13 ++--- packages/compass-components/src/index.ts | 5 +- .../src/components/insert-document-dialog.tsx | 4 +- .../table-view/full-width-cell-renderer.tsx | 50 +++++++++---------- 6 files changed, 34 insertions(+), 45 deletions(-) diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 7724d38d3d4..4dffbae7253 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -6,7 +6,7 @@ import { css } from '@leafygreen-ui/emotion'; import { palette } from '@leafygreen-ui/palette'; import { spacing } from '@leafygreen-ui/tokens'; import { useDarkMode } from '../../hooks/use-theme'; -import { useErrorDetailsModal } from '../../hooks/use-error-details'; +import { showErrorDetails } from '../../hooks/use-error-details'; type Status = | 'Initial' @@ -304,8 +304,6 @@ const EditActionsFooter: React.FunctionComponent<{ const darkMode = useDarkMode(); - const { showErrorDetails } = useErrorDetailsModal(); - // Allow props to override event based status of the document (helpful for // JSON editor where changing the document text doesn't really generate any // changes of the HadronDocument) diff --git a/packages/compass-components/src/hooks/use-confirmation.tsx b/packages/compass-components/src/hooks/use-confirmation.tsx index 38328e327ac..1fd30fd1bfa 100644 --- a/packages/compass-components/src/hooks/use-confirmation.tsx +++ b/packages/compass-components/src/hooks/use-confirmation.tsx @@ -2,6 +2,7 @@ import React, { useContext, useEffect, useRef, useState } from 'react'; import { Variant as ConfirmationModalVariant } from '@leafygreen-ui/confirmation-modal'; import ConfirmationModal from '../components/modals/confirmation-modal'; import { css } from '@leafygreen-ui/emotion'; +import type { ButtonProps } from '@leafygreen-ui/button'; export { ConfirmationModalVariant }; @@ -11,6 +12,7 @@ type ConfirmationProperties = Partial< Pick > & { buttonText?: React.ReactNode; + confirmButtonProps?: Omit; hideConfirmButton?: boolean; hideCancelButton?: boolean; description?: React.ReactNode; @@ -191,6 +193,7 @@ export const ConfirmationModalArea: React.FC = ({ children }) => { : undefined, children: confirmationProps.buttonText ?? 'Confirm', onClick: handleConfirm, + ...confirmationProps.confirmButtonProps, }} cancelButtonProps={{ className: confirmationProps.hideCancelButton diff --git a/packages/compass-components/src/hooks/use-error-details.tsx b/packages/compass-components/src/hooks/use-error-details.tsx index 65fc27ff4fa..99302afa1c2 100644 --- a/packages/compass-components/src/hooks/use-error-details.tsx +++ b/packages/compass-components/src/hooks/use-error-details.tsx @@ -1,10 +1,10 @@ import { type showConfirmation as originalShowConfirmation, showConfirmation, - useConfirmationModal, } from './use-confirmation'; import { Code } from '../components/leafygreen'; import React from 'react'; +import { ButtonVariant } from '..'; const getShowErrorDetails = ( showConfirmation: typeof originalShowConfirmation @@ -29,15 +29,10 @@ const getShowErrorDetails = ( ), hideCancelButton: true, buttonText: closeAction.replace(/\b\w/g, (c) => c.toUpperCase()), - // modalProps - // buttonProps + confirmButtonProps: { + variant: ButtonVariant.Default, + }, }); }; -export function useErrorDetailsModal() { - const { showConfirmation } = useConfirmationModal(); - - return { showErrorDetails: getShowErrorDetails(showConfirmation) }; -} - export const showErrorDetails = getShowErrorDetails(showConfirmation); diff --git a/packages/compass-components/src/index.ts b/packages/compass-components/src/index.ts index aee0ba9b2aa..2b47da41542 100644 --- a/packages/compass-components/src/index.ts +++ b/packages/compass-components/src/index.ts @@ -182,10 +182,7 @@ export { ConfirmationModalArea, showConfirmation, } from './hooks/use-confirmation'; -export { - useErrorDetailsModal, - showErrorDetails, -} from './hooks/use-error-details'; +export { showErrorDetails } from './hooks/use-error-details'; export { useHotkeys, formatHotkey, diff --git a/packages/compass-crud/src/components/insert-document-dialog.tsx b/packages/compass-crud/src/components/insert-document-dialog.tsx index a2040703b2a..bcf5cbf8304 100644 --- a/packages/compass-crud/src/components/insert-document-dialog.tsx +++ b/packages/compass-crud/src/components/insert-document-dialog.tsx @@ -11,7 +11,7 @@ import { SegmentedControl, SegmentedControlOption, spacing, - useErrorDetailsModal, + showErrorDetails, } from '@mongodb-js/compass-components'; import type { InsertCSFLEWarningBannerProps } from './insert-csfle-warning-banner'; @@ -141,8 +141,6 @@ const InsertDocumentDialog: React.FC = ({ ); const [insertInProgress, setInsertInProgress] = useState(false); - const { showErrorDetails } = useErrorDetailsModal(); - const hasManyDocuments = useCallback(() => { let parsed: unknown; try { diff --git a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx index 031bdcabcb7..7033d013f49 100644 --- a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx @@ -129,32 +129,30 @@ class FullWidthCellRenderer extends React.Component< // this is needed cause ag-grid renders this component outside // of the context chain - - { - this.props.api.stopEditing(); - if (force) { - this.props.replaceDocument(this.doc); - } else { - this.props.updateDocument(this.doc); - } - }} - onDelete={() => { - this.props.api.stopEditing(); - this.props.removeDocument(this.doc); - }} - onCancel={() => { - if (this.state.mode === 'editing') { - this.handleCancelUpdate(); - } else { - this.handleCancelRemove(); - } - }} - /> - + { + this.props.api.stopEditing(); + if (force) { + this.props.replaceDocument(this.doc); + } else { + this.props.updateDocument(this.doc); + } + }} + onDelete={() => { + this.props.api.stopEditing(); + this.props.removeDocument(this.doc); + }} + onCancel={() => { + if (this.state.mode === 'editing') { + this.handleCancelUpdate(); + } else { + this.handleCancelRemove(); + } + }} + /> ); } From afe8fab6f18808d426d41e82e1c083a93b7aefdb Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Wed, 19 Mar 2025 14:38:25 +0100 Subject: [PATCH 14/14] cleanup --- packages/compass-crud/src/components/document-list.tsx | 1 - .../src/components/table-view/full-width-cell-renderer.tsx | 2 -- 2 files changed, 3 deletions(-) diff --git a/packages/compass-crud/src/components/document-list.tsx b/packages/compass-crud/src/components/document-list.tsx index d6a9f941973..f9021c0cd1d 100644 --- a/packages/compass-crud/src/components/document-list.tsx +++ b/packages/compass-crud/src/components/document-list.tsx @@ -9,7 +9,6 @@ import { WorkspaceContainer, spacing, withDarkMode, - ErrorDetailsModalArea, } from '@mongodb-js/compass-components'; import type { InsertDocumentDialogProps } from './insert-document-dialog'; import InsertDocumentDialog from './insert-document-dialog'; diff --git a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx index 7033d013f49..43cb3898027 100644 --- a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx @@ -1,9 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - ConfirmationModalArea, DocumentList, - ErrorDetailsModalArea, LeafyGreenProvider, } from '@mongodb-js/compass-components'; import type Document from 'hadron-document';