Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## YYYY-MM-DD - Initial Explore
**Learning:** Found several input fields without explicit `aria-label` or missing `htmlFor` association, such as in `LibraryHeader.tsx` and `UnresolvedFilesSection.tsx`. We will improve the accessibility of one of them.
**Action:** Enhance accessibility by properly associating labels and adding clear focus states.
## 2024-05-18 - Tooltips for Disabled States
**Learning:** We often disable buttons (like "Match All" when there are no unresolved files), but without a tooltip, users might not understand *why* the button is disabled, leading to frustration. By wrapping the disabled button in a span with a title attribute, we can provide immediate, contextual help. Also applying pointer-events-none on the button ensures the wrapper catches the hover.
**Action:** Always add a descriptive `title` tooltip wrapper around buttons that can be conditionally disabled to explain the reason to the user.
24 changes: 17 additions & 7 deletions src/features/library/components/UnresolvedFilesSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,24 @@ export function UnresolvedFilesSection({
</button>
) : null}
</div>
<button
className="flex items-center gap-2 rounded-lg bg-accent-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-700 disabled:opacity-50"
disabled={isAttemptMatchPending || isAttemptMatchAllPending || discoveredItems.length === 0}
onClick={onAttemptMatchAll}
<span
className={cx('flex', (isAttemptMatchPending || isAttemptMatchAllPending || discoveredItems.length === 0) && 'cursor-not-allowed')}
title={
isAttemptMatchAllPending ? 'Matching all files...' :
isAttemptMatchPending ? 'Matching in progress...' :
discoveredItems.length === 0 ? 'No unresolved files to match' :
'Attempt to match all files'
}
>
<RefreshCw size={14} className={cx(isAttemptMatchAllPending && 'animate-spin')} />
{isAttemptMatchAllPending ? 'Matching All...' : 'Match All'}
</button>
<button
className="flex items-center gap-2 rounded-lg bg-accent-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:pointer-events-none disabled:opacity-50 dark:focus-visible:ring-offset-slate-900"
disabled={isAttemptMatchPending || isAttemptMatchAllPending || discoveredItems.length === 0}
onClick={onAttemptMatchAll}
>
<RefreshCw size={14} className={cx(isAttemptMatchAllPending && 'animate-spin')} />
{isAttemptMatchAllPending ? 'Matching All...' : 'Match All'}
</button>
</span>
</div>
</div>

Expand Down
22 changes: 22 additions & 0 deletions verification/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<link href="http://localhost:1420/src/index.css" rel="stylesheet">
</head>
<body class="bg-slate-50 p-8">
<div class="flex items-center gap-2">
<span
class="flex cursor-not-allowed"
title="No unresolved files to match"
>
<button
class="flex items-center gap-2 rounded-lg bg-accent-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:pointer-events-none disabled:opacity-50 dark:focus-visible:ring-offset-slate-900"
disabled
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
Match All
</button>
</span>
</div>
</body>
</html>
Binary file added verification/tooltip.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions verification/verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from playwright.sync_api import sync_playwright
import time

def verify():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("file:///app/verification/index.html")

# Start recording trace if needed, wait for load
page.wait_for_load_state('networkidle')

# Hover to trigger the tooltip. force=True is needed because the button is technically inside, but the span catches the hover.
page.locator('span[title]').hover(force=True)
time.sleep(1) # Give time for native tooltip
page.screenshot(path="/app/verification/tooltip.png")

browser.close()

if __name__ == "__main__":
verify()