From 231e43bbe6cba087ecadc1ee31e62e2001ed4da4 Mon Sep 17 00:00:00 2001 From: John Costa Date: Tue, 6 Oct 2026 17:44:22 +0000 Subject: [PATCH] [AI] fix: let Firefox place the caret when renaming a category (#8950) * fix: let Firefox place the caret when renaming a category or account react-dnd's HTML5 backend sets draggable="true" on the connected node and only consults canDrag on dragstart, so the attribute stays on while a row is being edited. Firefox refuses to place the caret in an input under a draggable ancestor, which made click-to-position impossible in the category and account rename fields. useDraggable now mirrors canDrag onto the attribute, which covers every caller at once. Closes #5620 * chore: name the release note after the PR * chore: scope release note to category rename --- .../src/components/sort.test.tsx | 58 +++++++++++++++++++ .../desktop-client/src/components/sort.tsx | 19 +++++- upcoming-release-notes/8950.md | 6 ++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 packages/desktop-client/src/components/sort.test.tsx create mode 100644 upcoming-release-notes/8950.md diff --git a/packages/desktop-client/src/components/sort.test.tsx b/packages/desktop-client/src/components/sort.test.tsx new file mode 100644 index 0000000000..589849158b --- /dev/null +++ b/packages/desktop-client/src/components/sort.test.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; + +import { render, screen } from '@testing-library/react'; + +import { useDragRef } from '#hooks/useDragRef'; + +import { useDraggable } from './sort'; + +function Row({ canDrag }: { canDrag: boolean }) { + const { dragRef } = useDraggable({ + type: 'row', + item: { id: 'row-1' }, + canDrag, + onDragChange: vi.fn(), + }); + const handleDragRef = useDragRef(dragRef); + + return
; +} + +function renderRow(canDrag: boolean) { + return render( + + + , + ); +} + +describe('useDraggable', () => { + // #5620: react-dnd only consults canDrag on dragstart and leaves + // draggable="true" on the node, which stops Firefox from placing the caret + // in an input rendered inside it (e.g. the category rename field). + it('mirrors canDrag onto the draggable attribute', () => { + const { rerender } = renderRow(true); + expect(screen.getByTestId('row')).toHaveAttribute('draggable', 'true'); + + rerender( + + + , + ); + expect(screen.getByTestId('row')).toHaveAttribute('draggable', 'false'); + + rerender( + + + , + ); + expect(screen.getByTestId('row')).toHaveAttribute('draggable', 'true'); + }); + + it('is not draggable when mounted while editing', () => { + renderRow(false); + expect(screen.getByTestId('row')).toHaveAttribute('draggable', 'false'); + }); +}); diff --git a/packages/desktop-client/src/components/sort.tsx b/packages/desktop-client/src/components/sort.tsx index 4b549f454b..dbd42a0bd9 100644 --- a/packages/desktop-client/src/components/sort.tsx +++ b/packages/desktop-client/src/components/sort.tsx @@ -1,5 +1,6 @@ import React, { createContext, + useCallback, useContext, useEffect, useLayoutEffect, @@ -42,8 +43,9 @@ export function useDraggable({ onDragChange, }: UseDraggableArgs) { const _onDragChange = useRef(onDragChange); + const node = useRef(null); - const [, dragRef] = useDrag({ + const [, connectDragSource] = useDrag({ type, item: () => { void _onDragChange.current({ state: 'start-preview', type, item }); @@ -69,6 +71,21 @@ export function useDraggable({ _onDragChange.current = onDragChange; }, [onDragChange]); + const dragRef = useCallback( + (el: HTMLElement | null) => { + node.current = el; + connectDragSource(el); + }, + [connectDragSource], + ); + + // react-dnd sets draggable="true" when it connects and only consults + // canDrag on dragstart. Firefox refuses to place the caret in an input under + // a draggable ancestor, so mirror canDrag onto the attribute (#5620). + useLayoutEffect(() => { + node.current?.setAttribute('draggable', String(canDrag)); + }, [canDrag, dragRef]); + return { dragRef }; } diff --git a/upcoming-release-notes/8950.md b/upcoming-release-notes/8950.md new file mode 100644 index 0000000000..1817d03e2f --- /dev/null +++ b/upcoming-release-notes/8950.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [costajohnt] +--- + +Allow clicking to place the cursor when renaming a category in Firefox