diff --git a/src/components/DraggableList/SortableItem.tsx b/src/components/DraggableList/SortableItem.tsx index b4c412bbdcbd1..2ab761df91d9c 100644 --- a/src/components/DraggableList/SortableItem.tsx +++ b/src/components/DraggableList/SortableItem.tsx @@ -1,10 +1,13 @@ import {useSortable} from '@dnd-kit/sortable'; import {CSS} from '@dnd-kit/utilities'; -import React from 'react'; +import React, {useLayoutEffect} from 'react'; +import CONST from '@src/CONST'; import type {SortableItemProps} from './types'; +const PRESSABLE_SELECTOR = '[data-tag="pressable"]'; + function SortableItem({id, children, disabled = false}: SortableItemProps) { - const {attributes, listeners, setNodeRef, transform, transition, isDragging} = useSortable({id, disabled}); + const {attributes, listeners, setNodeRef, transform, transition, isDragging, node} = useSortable({id, disabled}); const style = { touchAction: 'none', @@ -12,27 +15,45 @@ function SortableItem({id, children, disabled = false}: SortableItemProps) { transition, }; - // Prevent Enter key from reaching MenuItem when dragging to avoid navigation conflicts + // The sortable wrapper is the single Tab stop (tabIndex: 0 via dnd-kit attributes). + // Inner pressables must be non-focusable to avoid a double Tab stop per item. + useLayoutEffect(() => { + for (const el of node.current?.querySelectorAll(PRESSABLE_SELECTOR) ?? []) { + el.setAttribute('tabindex', '-1'); + } + }, [children, node]); + const handleKeyDown = (e: React.KeyboardEvent) => { - if (!isDragging || e.key !== 'Enter') { + if (e.key !== CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { + return; + } + + // Block Enter during active drag + if (isDragging) { + e.preventDefault(); + e.stopPropagation(); return; } - e.preventDefault(); - e.stopPropagation(); + + // Forward Enter to the inner pressable for navigation + const innerPressable = node.current?.querySelector(PRESSABLE_SELECTOR); + if (innerPressable) { + innerPressable.click(); + e.preventDefault(); + e.stopPropagation(); + } }; return (
{children}
diff --git a/src/components/DraggableList/index.tsx b/src/components/DraggableList/index.tsx index 09660a743dfb5..9fc99e29e3ad9 100644 --- a/src/components/DraggableList/index.tsx +++ b/src/components/DraggableList/index.tsx @@ -7,6 +7,7 @@ import React, {Fragment, useEffect, useId, useRef} from 'react'; import type {ScrollView as RNScrollView} from 'react-native'; import ScrollView from '@components/ScrollView'; import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; import SortableItem from './SortableItem'; import type DraggableListProps from './types'; @@ -40,7 +41,9 @@ function DraggableList({ if (typeof document === 'undefined' || !isDraggingRef.current) { return; } - document.dispatchEvent(new KeyboardEvent('keydown', {key: 'Escape', code: 'Escape', bubbles: true, cancelable: true})); + document.dispatchEvent( + new KeyboardEvent('keydown', {key: CONST.KEYBOARD_SHORTCUTS.ESCAPE.shortcutKey, code: CONST.KEYBOARD_SHORTCUTS.ESCAPE.shortcutKey, bubbles: true, cancelable: true}), + ); }; }, []); @@ -103,9 +106,9 @@ function DraggableList({ useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, keyboardCodes: { - start: ['Space'], - cancel: ['Escape'], - end: ['Space'], + start: [CONST.KEYBOARD_SHORTCUTS.SPACE.shortcutKey], + cancel: [CONST.KEYBOARD_SHORTCUTS.ESCAPE.shortcutKey], + end: [CONST.KEYBOARD_SHORTCUTS.SPACE.shortcutKey], }, }), );