Skip to content

Commit daaa6e3

Browse files
authored
fix(compass-crud): persist modified document value COMPASS-8373 (#6404)
* persist json value in document * use ref and remove useEffect dep * ref current assignment
1 parent 67cb7b7 commit daaa6e3

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

packages/compass-crud/src/components/json-editor.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,26 @@ const JSONEditor: React.FunctionComponent<JSONEditorProps> = ({
7676
const [expanded, setExpanded] = useState<boolean>(doc.expanded);
7777
const [editing, setEditing] = useState<boolean>(doc.editing);
7878
const [deleting, setDeleting] = useState<boolean>(doc.markedForDeletion);
79-
const [value, setValue] = useState<string>(() => doc.toEJSON());
79+
const [value, setValue] = useState<string>(
80+
() => doc.modifiedEJSONString ?? doc.toEJSON()
81+
);
8082
const [initialValue] = useState<string>(() => doc.toEJSON());
8183
const [containsErrors, setContainsErrors] = useState<boolean>(false);
84+
const setModifiedEJSONStringRef = useRef<(value: string | null) => void>(
85+
doc.setModifiedEJSONString.bind(doc)
86+
);
87+
setModifiedEJSONStringRef.current = doc.setModifiedEJSONString.bind(doc);
88+
89+
useEffect(() => {
90+
const setModifiedEJSONString = setModifiedEJSONStringRef.current;
91+
return () => {
92+
// When this component is used in virtualized list, the editor is
93+
// unmounted on scroll and if the user is editing the document, the
94+
// editor value is lost. This is a way to keep track of the editor
95+
// value when the it's unmounted and is restored on next mount.
96+
setModifiedEJSONString(editing ? value : null);
97+
};
98+
}, [value, editing]);
8299

83100
const handleCopy = useCallback(() => {
84101
copyToClipboard?.(doc);

packages/compass-crud/src/components/virtualized-document-json-view.spec.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import {
1212
import { type VirtualListRef } from '@mongodb-js/compass-components';
1313

1414
import VirtualizedDocumentJsonView from './virtualized-document-json-view';
15+
import {
16+
getCodemirrorEditorValue,
17+
setCodemirrorEditorValue,
18+
} from '@mongodb-js/compass-editor';
1519

1620
const createBigDocument = (variable: number) =>
1721
new HadronDocument({
@@ -168,4 +172,44 @@ describe('VirtualizedDocumentJsonView', function () {
168172
expect(() => within(documentElement).getByText('Cancel')).to.throw;
169173
expect(() => within(documentElement).getByText('Replace')).to.throw;
170174
});
175+
176+
it('preserves the edit state of document when a document goes out of visible viewport when scrolling', async function () {
177+
const bigDocuments = Array.from({ length: 20 }, (_, idx) =>
178+
createBigDocument(idx)
179+
);
180+
const listRef: VirtualListRef = React.createRef();
181+
render(
182+
<VirtualizedDocumentJsonView
183+
namespace="x.y"
184+
docs={bigDocuments}
185+
isEditable={true}
186+
__TEST_OVERSCAN_COUNT={0}
187+
__TEST_LIST_HEIGHT={178}
188+
__TEST_LIST_REF={listRef}
189+
/>
190+
);
191+
192+
let [firstDocumentElement] = screen.getAllByTestId('editable-json');
193+
// Trigger the edit state (we only set the edited value if the document is being edited)
194+
userEvent.click(within(firstDocumentElement).getByLabelText('Edit'));
195+
196+
let cmEditor = firstDocumentElement.querySelector(
197+
'[data-codemirror="true"]'
198+
);
199+
await setCodemirrorEditorValue(cmEditor, '{value: "edited"}');
200+
201+
// Scroll down and then scroll back up
202+
act(() => {
203+
listRef.current?.scrollToItem(15);
204+
});
205+
act(() => {
206+
listRef.current?.scrollToItem(0);
207+
});
208+
209+
[firstDocumentElement] = screen.getAllByTestId('editable-json');
210+
cmEditor = firstDocumentElement.querySelector('[data-codemirror="true"]');
211+
212+
const value = getCodemirrorEditorValue(cmEditor);
213+
expect(value).to.equal('{value: "edited"}');
214+
});
171215
});

packages/hadron-document/src/document.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ export class Document extends EventEmitter {
5050
maxVisibleElementsCount = DEFAULT_VISIBLE_ELEMENTS;
5151
editing = false;
5252
markedForDeletion = false;
53+
// This is used to store the changed EJSON string when the document is modified
54+
// via the JSONEditor.
55+
modifiedEJSONString: string | null = null;
5356

5457
/**
5558
* Send cancel event.
@@ -456,6 +459,7 @@ export class Document extends EventEmitter {
456459
finishEditing() {
457460
if (this.editing) {
458461
this.editing = false;
462+
this.setModifiedEJSONString(null);
459463
this.emit(DocumentEvents.EditingFinished);
460464
}
461465
}
@@ -503,6 +507,10 @@ export class Document extends EventEmitter {
503507
onRemoveError(error: Error) {
504508
this.emit('remove-error', error.message);
505509
}
510+
511+
setModifiedEJSONString(ejson: string | null) {
512+
this.modifiedEJSONString = ejson;
513+
}
506514
}
507515

508516
export default Document;

0 commit comments

Comments
 (0)