Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelogs/CHANGELOG_alpha.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# [Unreleased]

### Features

* Keep cell selected by objectId and field name after data browser refresh ([#2631](https://github.com/parse-community/parse-dashboard/issues/2631))

# [9.1.0-alpha.7](https://github.com/parse-community/parse-dashboard/compare/9.1.0-alpha.6...9.1.0-alpha.7) (2026-03-02)


Expand Down
50 changes: 49 additions & 1 deletion src/dashboard/Data/Browser/DataBrowser.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export default class DataBrowser extends React.Component {
_objectsToFetch: [], // Temporary field for async fetch handling
loadingObjectIds: new Set(),
keyboardShortcuts: null, // Keyboard shortcuts from server
pendingRestore: null, // Stores { objectId, fieldName } to re-select the same cell after a refresh
showScriptConfirmationDialog: false,
selectedScript: null,
contextMenuX: null,
Expand Down Expand Up @@ -502,19 +503,66 @@ export default class DataBrowser extends React.Component {
// Note: We intentionally do NOT clear selectedObjectId when current becomes null.
// Clicking toolbar menus sets current=null, but the info panel should persist.

// When a refresh starts, data becomes null. Save the currently selected cell's
// objectId and field name so we can restore the selection once new data loads.
if (
this.props.data === null &&
prevProps.data !== null &&
this.props.className === prevProps.className
) {
const { current, order } = this.state;
if (current !== null) {
const objectId = prevProps.data[current.row]?.id;
const fieldName = order[current.col]?.name;
if (objectId && fieldName) {
this.setState({ pendingRestore: { objectId, fieldName } });
}
}
}

// When a refresh completes, data transitions from null back to a new array.
// Use the saved objectId + fieldName to find the row/col in the fresh data and
// restore the selection. If the document is gone, deselect to avoid confusion.
if (
this.props.data !== null &&
prevProps.data === null &&
this.state.pendingRestore
) {
const { objectId, fieldName } = this.state.pendingRestore;
const newRowIndex = this.props.data.findIndex(obj => obj.id === objectId);
const newColIndex = this.state.order.findIndex(col => col.name === fieldName);

if (newRowIndex !== -1 && newColIndex !== -1) {
this.setCurrent({ row: newRowIndex, col: newColIndex });
this.setState({ pendingRestore: null });
this.setSelectedObjectId(objectId);
this.handleCallCloudFunction(objectId, this.props.className, this.props.app.applicationId);
} else {
this.setState({ current: null, pendingRestore: null });
}
}

// If the user switches class entirely, discard any pending restore so it does
// not accidentally fire when the new class data loads.
if (this.props.className !== prevProps.className && this.state.pendingRestore) {
this.setState({ pendingRestore: null });
}

if (this.state.current && this.state.current !== prevState.current) {
if (this.state.current.col !== this.state.lastSelectedCol) {
this.setState({ lastSelectedCol: this.state.current.col });
}
}

// Auto-load first row if enabled and conditions are met
// Auto-load first row if enabled and conditions are met.
// Skip when a cell restore is pending to avoid overwriting the restored selection.
if (
this.state.autoLoadFirstRow &&
this.state.isPanelVisible &&
this.props.data &&
this.props.data.length > 0 &&
!this.state.selectedObjectId &&
!this.state.pendingRestore &&
((!prevProps.data || prevProps.data.length === 0) ||
prevProps.className !== this.props.className ||
prevState.isPanelVisible !== this.state.isPanelVisible)
Expand Down
Loading