InPlaceEditor ignores blur when focus moves outside the editor
What you were expecting:
When an <InPlaceEditor> loses focus, it should save the edited value. When cancelOnBlur is set, it should cancel the edit instead.
This matches the documented behavior: the field is saved automatically when the user clicks outside it, and the cancelOnBlur prop changes that behavior to cancel.
What happened instead:
If focus moves to another focusable element (for example, a neighboring button, link, or input), the editor stays open and the value is neither saved nor canceled.
The current blur handler returns whenever event.relatedTarget is non-null. Browsers set relatedTarget to the element receiving focus, so the save/cancel logic is skipped for normal Tab navigation and clicks on focusable controls.
Steps to reproduce:
- Render an
InPlaceEditorfollowed by a focusable control. - Click the field to enter edit mode.
- Change its value.
- Press Tab to move to the next control, or click that control.
- Observe that the editor remains in edit mode and no update occurs.
<>
<InPlaceEditor source="name" />
<button>Next</button>
</>A focused test also reproduces the issue:
const input = await screen.findByDisplayValue('John Doe');
fireEvent.change(input, { target: { value: 'Jane Doe' } });
const nextButton = screen.getByRole('button', { name: 'Next' });
fireEvent.blur(input, { relatedTarget: nextButton });
await screen.findByText('Jane Doe'); // times out; the input remains openRelated code:
handleBlur currently exits for every non-null relatedTarget:
if (event.relatedTarget) {
return;
}The guard likely needs to distinguish focus moving within the editor (for example, to its Save/Cancel buttons) from focus moving outside the editor.
Documentation: https://marmelab.com/react-admin/InPlaceEditor.html
Other information:
The same problem affects cancelOnBlur: moving focus to another focusable element does not restore the initial value.
Environment
- React-admin version: 5.15.1 / current
master - Last version that did not exhibit the issue: N/A (the guard is present in the initial
InPlaceEditorimplementation) - React version: 18.3.1
- Browser: Reproduced at the React event level with standard browser
FocusEvent.relatedTargetbehavior - Stack trace: N/A
Source: marmelab/react-admin