[AI] Fix running balance text selection in draggable transaction rows (#9125)

Co-authored-by: Kristián Valentín <[email protected]>
This commit is contained in:
Kristián Valentín
2026-10-07 20:53:48 +00:00
committed by GitHub
co-authored by Kristián Valentín
parent a5a3c26397
commit ec78d7c4d7
3 changed files with 60 additions and 0 deletions
@@ -164,6 +164,9 @@ type LiveTransactionTableProps = {
showCategory: boolean;
showGroup?: boolean;
showCleared: boolean;
showBalances?: boolean;
balances?: Record<TransactionEntity['id'], number>;
onReorder?: () => void;
isAdding: boolean;
onTransactionsChange?: (newTrans: TransactionEntity[]) => void;
onCloseAddTransaction?: () => void;
@@ -1591,6 +1594,37 @@ describe('Transactions', () => {
);
});
test('pressing the mouse on the running balance pauses row drag so the text can be selected', () => {
// Two transactions on the same date, so the rows can be reordered
const transactions = generateTransactions(2).map(t => ({
...t,
date: '2017-01-01',
}));
const { container } = renderTransactions({
transactions,
showBalances: true,
balances: Object.fromEntries(transactions.map(t => [t.id, 1000])),
onReorder: vi.fn(),
});
const balance = queryField(container, 'balance', '', 0);
const row = balance.closest('[data-testid=row]');
expect(row).toHaveAttribute('draggable', 'true');
fireEvent.mouseDown(balance, { button: 2 });
expect(row).toHaveAttribute('draggable', 'true');
fireEvent.mouseDown(balance);
expect(row).toHaveAttribute('draggable', 'false');
fireEvent.mouseUp(window);
expect(row).toHaveAttribute('draggable', 'true');
fireEvent.mouseDown(balance);
fireEvent.blur(window);
expect(row).toHaveAttribute('draggable', 'true');
});
test('transaction can be split, updated, and deleted', async () => {
const { container, getTransactions, updateProps } = renderTransactions();
@@ -1415,8 +1415,23 @@ const Transaction = memo(function Transaction({
// Row-level drag must not compete with inline editors (notes, amounts,
// payee, etc.): otherwise clicks/drags inside inputs start a reorder drag
// instead of moving the caret or selecting text (see GH #7567).
// The running balance is read-only, so pause the drag while the mouse is
// pressed on it to let the user select and copy the value (see GH #7833).
const [isSelectingBalance, setIsSelectingBalance] = useState(false);
useEffect(() => {
if (!isSelectingBalance) return;
const stopSelecting = () => setIsSelectingBalance(false);
// `blur` covers a mouseup the window never gets (e.g. alt-tab while pressed)
window.addEventListener('mouseup', stopSelecting);
window.addEventListener('blur', stopSelecting);
return () => {
window.removeEventListener('mouseup', stopSelecting);
window.removeEventListener('blur', stopSelecting);
};
}, [isSelectingBalance]);
const allowRowDrag =
canDrag &&
!isSelectingBalance &&
!isOnlyTransactionOnDate &&
(!editing || focusedField === 'select' || focusedField === 'cleared');
const { dragRef, dragProps } = useDrag<TransactionEntity>({
@@ -2055,6 +2070,11 @@ const Transaction = memo(function Transaction({
width={amountColumnWidths.balance}
textAlign="right"
privacyFilter
onMouseDown={e => {
// Only the primary button selects text; a right-click opens the
// context menu, which can swallow the mouseup
if (e.button === 0) setIsSelectingBalance(true);
}}
/>
);
case 'cleared':
@@ -0,0 +1,6 @@
---
category: Bugfix
authors: [kikoval]
---
Fix selecting the running balance text on the account page