--- phase: 09-md3-components plan: "05" subsystem: ui tags: [react, tailwind, tooltip, accessibility, testing-library] # Dependency graph requires: - phase: 09-md3-components/09-04 provides: TextFieldMD3 component with suffix/helpText props provides: - Tooltip hover+click behavior in FieldRenderer (text and select branches) and PasswordField - helpTextPrefix prop on TextFieldMD3 for inline left-of-helpText slot - Tooltip icon rendered beside helpText below the field (not above the field) - Tests covering hover show/hide, click-pin, click-unpin, and helpTextPrefix rendering affects: [RemoteConfigStep, PasswordField, TextFieldMD3] # Tech tracking tech-stack: added: [] patterns: - "hoverTooltip + showTooltip dual-state: hover shows, click pins, click-unpin clears both" - "helpTextPrefix slot pattern: ReactNode prop for inline content left of helpText in TextFieldMD3" - "Click-to-unpin guard: setShowTooltip(v => { if (v) setHoverTooltip(false); return !v; })" key-files: created: [] modified: - src/components/ui/TextFieldMD3.tsx - src/components/ui/FieldRenderer.tsx - src/components/ui/PasswordField.tsx - src/components/ui/FieldRenderer.test.tsx - src/components/ui/TextFieldMD3.test.tsx key-decisions: - "Tooltip hover uses dual-state (hoverTooltip + showTooltip); click-unpin explicitly clears hoverTooltip to prevent sticky tooltip after dismiss" - "helpTextPrefix guard is (helpText || helpTextPrefix) not just helpText, enabling icon-only rendering when field has tooltipText but no helpText" - "Tooltip content rendered BELOW TextFieldMD3 (after component), not inside it — TextFieldMD3 stays self-contained" patterns-established: - "helpTextPrefix slot: pass ReactNode as inline prefix left of helpText text in TextFieldMD3" - "Hover+click tooltip: mouseEnter shows, mouseLeave hides, click pins, second-click clears both states" requirements-completed: [COMP-04] # Metrics duration: 10min completed: 2026-04-01 --- # Phase 9 Plan 05: Tooltip Placement and Hover Fix Summary **Tooltip icon moved from above field to inline with helpText below it, with hover-to-preview and click-to-pin behavior via dual hoverTooltip+showTooltip state** ## Performance - **Duration:** ~10 min - **Started:** 2026-04-01T09:10:00Z - **Completed:** 2026-04-01T09:20:34Z - **Tasks:** 2 - **Files modified:** 5 ## Accomplishments - Added `helpTextPrefix` prop to TextFieldMD3 for rendering inline content (tooltip icon) left of helpText below the field - Fixed tooltip icon position: moved from above the floating-label input to beside helpText below it - Implemented hover-to-preview (mouseEnter shows, mouseLeave hides) and click-to-pin (stays open through mouseLeave) in FieldRenderer and PasswordField - Fixed click-unpin edge case: second click explicitly clears hoverTooltip so tooltip dismisses even when mouse stays on button - Added 7 new tests covering all tooltip hover/pin/unpin behaviors and helpTextPrefix rendering ## Task Commits Each task was committed atomically: 1. **Task 1: Add helpTextPrefix prop and fix tooltip placement + hover** - `23617c0` (feat) 2. **Task 2: Add tests for tooltip hover behavior and helpTextPrefix rendering** - `d2b6da2` (test) ## Files Created/Modified - `src/components/ui/TextFieldMD3.tsx` - Added `helpTextPrefix?: React.ReactNode` prop; changed helpText guard to `(helpText || helpTextPrefix) && !error`; renders `
` wrapping prefix + helpText - `src/components/ui/FieldRenderer.tsx` - Added `hoverTooltip` state; moved tooltip icon from above-field to `tooltipIcon` element passed as `helpTextPrefix`; tooltip content rendered below TextFieldMD3; select-branch also gains hover support - `src/components/ui/PasswordField.tsx` - Same dual-state tooltip fix as FieldRenderer text-branch - `src/components/ui/FieldRenderer.test.tsx` - Added `fireEvent` import; added 4 hover behavior tests (hover shows, leave hides, click pins, second-click unpins) - `src/components/ui/TextFieldMD3.test.tsx` - Added Tests 9-11 for helpTextPrefix with helpText, without, and prefix-only ## Decisions Made - Click-to-unpin must explicitly clear `hoverTooltip` because `userEvent.click()` simulates hover before click, leaving `hoverTooltip=true` after second click — required `setShowTooltip(v => { if (v) setHoverTooltip(false); return !v; })` - Tooltip content stays outside TextFieldMD3 (rendered after the component in FieldRenderer/PasswordField) — keeps TextFieldMD3 self-contained, tooltip is a consumer concern ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Fixed click-unpin leaving tooltip visible due to hoverTooltip race** - **Found during:** Task 1 verification (RemoteConfigStep.test.tsx "hides tooltip panel when clicked again") - **Issue:** `userEvent.click()` simulates mouseEnter before click; second click set `showTooltip=false` but `hoverTooltip` stayed true, so tooltip remained visible - **Fix:** Changed click handler to `setShowTooltip(v => { if (v) setHoverTooltip(false); return !v; })` — clears hover state when unpinning - **Files modified:** src/components/ui/FieldRenderer.tsx, src/components/ui/PasswordField.tsx - **Verification:** All 186 tests pass including RemoteConfigStep "hides tooltip panel when clicked again" - **Committed in:** 23617c0 (Task 1 commit) --- **Total deviations:** 1 auto-fixed (Rule 1 - bug) **Impact on plan:** Required for correctness — without this fix, tooltip could not be dismissed by click when mouse was over the button. ## Issues Encountered - Click-unpin sticky tooltip: `userEvent.click` triggers hover state before click event. Resolved by clearing `hoverTooltip` in the unpin branch of the click handler. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - UAT test 7 (Tooltip Info Buttons Accessibility) now satisfies all criteria: hover shows, mouseLeave hides, click pins, click again unpins, icon beside helpText below field - Phase 9 (09-md3-components) fully complete — all must-have truths and requirements satisfied - Ready for Phase 10 --- *Phase: 09-md3-components* *Completed: 2026-04-01*