diff --git a/components/email/__tests__/composer-plugin-surfaces.test.tsx b/components/email/__tests__/composer-plugin-surfaces.test.tsx
index f236cba0b..c9812f7c9 100644
--- a/components/email/__tests__/composer-plugin-surfaces.test.tsx
+++ b/components/email/__tests__/composer-plugin-surfaces.test.tsx
@@ -518,17 +518,19 @@ describe('composer-sidebar and composer-sidebar-right', () => {
offerSlots('p1', ['composer-sidebar', 'composer-sidebar-right']);
const { unmount } = render();
+ // A mouse drag, as pointer events with the button held (the handle follows pointers).
+ const drag = (handle: HTMLElement, from: number, to: number) => {
+ fireEvent.pointerDown(handle, { pointerId: 1, button: 0, buttons: 1, pointerType: 'mouse', clientX: from, clientY: 10 });
+ fireEvent.pointerMove(document, { pointerId: 1, buttons: 1, pointerType: 'mouse', clientX: to, clientY: 10 });
+ fireEvent.pointerUp(document, { pointerId: 1, button: 0, buttons: 0, pointerType: 'mouse' });
+ };
// The right panel's handle is on its left edge: dragging left widens it.
const right = screen.getByTestId('composer-sidebar-resize-right');
- fireEvent.mouseDown(right, { clientX: 1000 });
- fireEvent.mouseMove(document, { clientX: 900 });
- fireEvent.mouseUp(document);
+ drag(right, 1000, 900);
expect(panel('right').style.width).toBe('360px');
// The left panel's handle is on its right edge: dragging left narrows it, down to the minimum.
const left = screen.getByTestId('composer-sidebar-resize-left');
- fireEvent.mouseDown(left, { clientX: 300 });
- fireEvent.mouseMove(document, { clientX: 100 });
- fireEvent.mouseUp(document);
+ drag(left, 300, 100);
expect(panel('left').style.width).toBe('220px');
fireEvent.keyDown(left, { key: 'ArrowRight' });
expect(panel('left').style.width).toBe('230px');
diff --git a/components/email/email-composer.tsx b/components/email/email-composer.tsx
index cc9972586..211c89f6c 100644
--- a/components/email/email-composer.tsx
+++ b/components/email/email-composer.tsx
@@ -2753,6 +2753,17 @@ export function EmailComposer({
const wantsPluginSelection = wantsPluginDraft;
const [pluginSelection, setPluginSelection] = useState<{ text: string; empty: boolean }>(EMPTY_SELECTION);
const [pluginEditor, setPluginEditor] = useState(null);
+ // One callback for the composer's life: the editor reports itself ready
+ // whenever this changes, and every report now sets state, so a new arrow
+ // per render would report, render and report again without end.
+ const handleEditorReady = useCallback((ed: Editor) => {
+ editorRef.current = ed;
+ setPluginEditor(ed);
+ if (!replyFocusedRef.current && (mode === 'reply' || mode === 'replyAll')) {
+ replyFocusedRef.current = true;
+ ed.commands.focus('start');
+ }
+ }, [mode]);
useEffect(() => {
if (!wantsPluginSelection) return;
let timer: ReturnType | null = null;
@@ -4158,14 +4169,7 @@ export function EmailComposer({
hasError={validationErrors.body}
collapseToolbarOnMobile={!showFormattingOnMobile}
toolbarPlacement={mvWindow ? (mvShowFormatting ? 'bottom' : 'hidden') : 'top'}
- onEditorReady={(ed) => {
- editorRef.current = ed;
- setPluginEditor(ed);
- if (!replyFocusedRef.current && (mode === 'reply' || mode === 'replyAll')) {
- replyFocusedRef.current = true;
- ed.commands.focus('start');
- }
- }}
+ onEditorReady={handleEditorReady}
mentionCandidates={recipientMentionsEnabled ? mentionCandidates : undefined}
findMentionContacts={recipientMentionsEnabled && mentionAddsRecipientTo !== 'off' ? findMentionContacts : undefined}
mentionAddsTo={mentionAddsRecipientTo === 'cc' ? 'cc' : 'to'}