Hi everyone,
I am facing a UI/UX issue with child tables (grid) in Frappe/ERPNext and would appreciate guidance or a proper solution. I’m using v15
Issue Description
In doctypes like Sales Order, Quotation, and other forms with child tables:
- When I click on a row (e.g., an Item row), it correctly enters editable mode
- However, when I click anywhere outside the child table (form area, header, etc.), the row does not exit editable mode
Ideally, it should revert back to static display showing:
Item Code : Item Name
Steps to Reproduce
- Open Sales Order / Quotation
- Add or select an item in Items table
- Click on a row → it becomes editable
- Click anywhere outside the grid
Current Behavior
- Row remains in editable mode
- Does not revert to static display
- Requires page refresh or selecting another row to reset
Expected Behavior
- Clicking outside the grid should:
- Exit editable mode
- Return to static display
- Show formatted values (Item Code : Item Name)
What I Tried
I created a custom script to handle deselection:
File:grid_deselection_handler.js
(function () {
"use strict";
console.log("\[GridDeselect\] Global handler active");
function force_grid_deselection() {
if (!window.cur_frm || !cur_frm.fields_dict) return;
if (document.activeElement && $(document.activeElement).closest('.form-grid').length) {
document.activeElement.blur();
}
Object.keys(cur_frm.fields_dict).forEach(fieldname => {
const field = cur_frm.fields_dict\[fieldname\];
if (field && field.df && field.df.fieldtype === 'Table' && field.grid) {
const grid = field.grid;
if (grid.wrapper.find('.grid-row-open, .grid-row.active').length > 0) {
try {
grid.row_display(null, false);
grid.active_row = null;
grid.open_grid_row = null;
grid.refresh();
if (typeof apply_profit_colors === 'function') {
apply_profit_colors(cur_frm);
}
console.log(\`\[GridDeselect\] Table '${fieldname}' reset to view mode\`);
} catch (err) {
console.debug("\[GridDeselect\] Error refreshing grid:", err);
}
}
}
});
}
window.addEventListener('mousedown', function(e) {
if (!window.cur_frm) return;
const is_inside_grid = $(e.target).closest('.form-grid').length > 0;
const is_inside_ui_overlay = $(e.target).closest('.popover, .awesomplete, .link-field-results, .modal, .link-preview-box, .btn-open-row').length > 0;
if (!is_inside_grid && !is_inside_ui_overlay) {
setTimeout(force_grid_deselection, 200);
}
}, true);
}());
Included via hooks:
app_include_js = [
“/assets/novasource/js/grid_deselection_handler.js”
]
I tried:
- Blurring active element
- Using
frappe.ui.form.editable_row.toggle_editable_row(false) - DOM-based fallback handling
However, the row still remains in editable mode or sometimes re-enters edit mode due to internal grid click handling.
Technical Observation
From debugging, it seems:
- Editable state is controlled by
frappe.ui.form.editable_row - There is no global handler to reset this when clicking outside the grid
- Grid column click events may be re-triggering edit mode
Questions
- Is this expected behavior in Frappe grid?
- Is there any standard way to:
- Deselect a grid row
- Exit editable mode on outside click
- Has anyone implemented a reliable solution for this?
Thanks in advance!