LinkDesk/frontend/docs/shot-delete-event-bubbling-...

6.0 KiB

Shot Delete Event Bubbling - Final Fix

Issue Description

The shot detail panel was still being triggered when performing delete shot actions, despite previous attempts to fix event bubbling. This was happening because the event prevention logic in the handleRowClick function was not comprehensive enough to catch all interactive elements within the dropdown menu.

Root Cause Analysis

The issue occurred in the ShotsDataTable.vue component where the handleRowClick function was not properly detecting all types of interactive elements that should prevent row click events. Specifically:

  1. Incomplete Element Detection: The original selector list was missing some Radix UI specific attributes and SVG icons
  2. Missing Parent Element Traversal: The function wasn't checking parent elements for event handlers
  3. Icon Click Events: SVG icons within buttons were not being properly detected
  4. Radix UI Elements: Some Radix UI dropdown elements have dynamic attributes that weren't being caught

Solution Implementation

1. Enhanced Interactive Element Detection

Updated the handleRowClick function in ShotsDataTable.vue to include a more comprehensive list of selectors:

const isInteractiveElement = target.closest([
  'button',
  '[role="menuitem"]',
  '[role="menu"]',
  '[data-radix-collection-item]',
  '[data-radix-dropdown-menu-trigger]',
  '[data-radix-dropdown-menu-content]',
  '[data-radix-dropdown-menu-item]',
  '[data-state]', // Radix UI elements often have data-state
  '.dropdown-menu',
  '.dropdown-trigger',
  'input',
  'select',
  'textarea',
  'a[href]',
  '[tabindex]:not([tabindex="-1"])',
  '[onclick]',
  'svg', // Icons within buttons
  '.lucide' // Lucide icons specifically
].join(', '))

2. Parent Element Traversal

Added logic to traverse up the DOM tree to check for elements with event handlers:

let currentElement: HTMLElement | null = target
while (currentElement && currentElement !== event.currentTarget) {
  if (
    currentElement.onclick ||
    currentElement.getAttribute('role') === 'button' ||
    currentElement.tagName === 'BUTTON' ||
    (currentElement.classList.contains('cursor-pointer') && 
     currentElement !== event.currentTarget)
  ) {
    return // Don't emit row-click
  }
  currentElement = currentElement.parentElement
}

3. Enhanced Event Prevention in Columns

The columns.ts file was already enhanced by the IDE autofix to include onMouseDown event handlers on all dropdown elements:

// Dropdown trigger
h(DropdownMenuTrigger, { 
  asChild: true, 
  onMouseDown: (e: Event) => {
    e.stopPropagation()
  },
  onClick: (e: Event) => {
    e.stopPropagation()
    e.preventDefault()
  }
})

// Dropdown content and items
h(DropdownMenuContent, { 
  align: 'end',
  onMouseDown: (e: Event) => {
    e.stopPropagation()
  },
  onClick: (e: Event) => {
    e.stopPropagation()
  }
})

Files Modified

1. frontend/src/components/shot/ShotsDataTable.vue

  • Enhanced handleRowClick function with comprehensive interactive element detection
  • Added parent element traversal logic
  • Fixed TypeScript typing for element traversal
  • Removed unused computed import

2. frontend/src/components/shot/columns.ts (Enhanced by IDE autofix)

  • Added onMouseDown event handlers to all dropdown elements
  • Enhanced event prevention with both stopPropagation() and preventDefault()
  • Improved event handling for all dropdown menu items

Testing

Test File Created

  • frontend/test-shot-delete-event-bubbling-final-fix.html - Comprehensive test interface

Test Scenarios Covered

  1. Row Click Test: Clicking on non-interactive areas should trigger row click
  2. Dropdown Button Test: Clicking the three-dot menu button should NOT trigger row click
  3. Menu Item Test: Clicking "Edit Shot" or "View Tasks" should NOT trigger row click
  4. Delete Test: Clicking "Delete Shot" should NOT trigger row click (main issue)
  5. Icon Test: Clicking directly on SVG icons within buttons should NOT trigger row click

Expected Results

  • Row clicks work on non-interactive areas
  • Dropdown button clicks don't trigger row click
  • Menu item clicks don't trigger row click
  • Delete button clicks don't trigger row click
  • Shot detail panel doesn't open during delete operations

Technical Details

Event Flow

  1. User clicks delete button in dropdown menu
  2. onMouseDown handler on dropdown elements prevents initial bubbling
  3. onClick handler on menu item executes delete action
  4. Enhanced handleRowClick function detects interactive element and prevents row click emission
  5. Shot detail panel remains closed during delete operation

Browser Compatibility

  • Uses standard DOM traversal methods (closest, parentElement)
  • Compatible with all modern browsers
  • Handles both mouse and touch events

Performance Impact

  • Minimal performance impact due to efficient DOM traversal
  • Event prevention happens early in the event chain
  • No unnecessary event listeners or watchers added

Prevention Measures

To prevent similar issues in the future:

  1. Comprehensive Testing: Always test interactive elements within table rows
  2. Event Handler Documentation: Document all event prevention strategies
  3. Radix UI Awareness: Be aware of Radix UI's dynamic attributes and event handling
  4. Icon Handling: Remember that SVG icons can be click targets within buttons
  5. Parent Traversal: Consider parent element event handlers when preventing bubbling

Verification

The fix can be verified by:

  1. Opening the shot table in the application
  2. Clicking the three-dot menu on any shot row
  3. Clicking "Delete Shot" option
  4. Confirming that the shot detail panel does NOT open
  5. Confirming that the delete confirmation dialog opens instead

This fix resolves the core issue where delete actions were inadvertently opening the shot detail panel, improving the user experience and preventing confusion during delete operations.