Files
Ready2Blob/.planning/phases/09-md3-components/09-05-PLAN.md
T
2026-04-01 11:03:10 +02:00

12 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, gap_closure, requirements, must_haves
phase plan type wave depends_on files_modified autonomous gap_closure requirements must_haves
09-md3-components 05 execute 1
09-04
src/components/ui/TextFieldMD3.tsx
src/components/ui/TextFieldMD3.test.tsx
src/components/ui/FieldRenderer.tsx
src/components/ui/FieldRenderer.test.tsx
src/components/ui/PasswordField.tsx
true true
COMP-04
truths artifacts key_links
Hovering the tooltip info button reveals tooltip text without clicking
Clicking the tooltip info button pins it open; clicking again dismisses it
Tooltip icon appears beside the helpText below the field, not above the field
path provides contains
src/components/ui/TextFieldMD3.tsx helpTextPrefix prop for rendering inline content left of helpText helpTextPrefix
path provides contains
src/components/ui/FieldRenderer.tsx Tooltip icon passed as helpTextPrefix, hover+click behavior onMouseEnter
path provides contains
src/components/ui/PasswordField.tsx Same tooltip fix as FieldRenderer text-branch onMouseEnter
from to via pattern
src/components/ui/FieldRenderer.tsx src/components/ui/TextFieldMD3.tsx helpTextPrefix prop helpTextPrefix.*ⓘ
from to via pattern
src/components/ui/PasswordField.tsx src/components/ui/TextFieldMD3.tsx helpTextPrefix prop helpTextPrefix.*ⓘ
Fix two UAT-reported tooltip issues: (1) tooltip doesn't show on hover, only on click; (2) tooltip icon is positioned above the field instead of beside the helpText below it.

Purpose: Close the last remaining UAT gap in Phase 9 (test 7: Tooltip Info Buttons Accessibility). Output: Tooltip icon renders inline with helpText below the field, and tooltip text appears on hover (pinnable via click).

<execution_context> @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/09-md3-components/09-03-SUMMARY.md From src/components/ui/TextFieldMD3.tsx: ```typescript interface TextFieldMD3Props { id: string; label: string; error?: FieldError; registration: UseFormRegisterReturn; type?: 'text' | 'password'; helpText?: string; required?: boolean; suffix?: React.ReactNode; } ```

From src/components/ui/FieldRenderer.tsx:

// text (default) — uses TextFieldMD3 for floating label
return (
  <div className="flex flex-col gap-1">
    {field.tooltipText && (
      <button type="button" onClick={() => setShowTooltip(v => !v)}
        aria-label={`More info about ${field.label}`}
        className="text-primary hover:text-primary text-xs leading-none self-start">
        
      </button>
    )}
    {field.tooltipText && showTooltip && (
      <p className="text-xs text-primary bg-primary/10 border border-primary/30 rounded px-2 py-1.5">
        {field.tooltipText}
      </p>
    )}
    <TextFieldMD3 id={field.key} label={field.label} error={error}
      registration={register(field.key)} helpText={field.helpText} required={field.required} />
  </div>
);
{helpText && !error && (
  <p className="text-xs text-on-surface-container/70">{helpText}</p>
)}
Task 1: Add helpTextPrefix prop to TextFieldMD3 and fix tooltip placement + hover in FieldRenderer and PasswordField src/components/ui/TextFieldMD3.tsx, src/components/ui/FieldRenderer.tsx, src/components/ui/PasswordField.tsx **TextFieldMD3.tsx — Add helpTextPrefix prop:**
  1. Add helpTextPrefix?: React.ReactNode to TextFieldMD3Props interface.
  2. Destructure helpTextPrefix in the component function.
  3. Change the helpText rendering block (lines 65-67) from:
    {helpText && !error && (
      <p className="text-xs text-on-surface-container/70">{helpText}</p>
    )}
    
    To:
    {helpText && !error && (
      <div className="flex items-start gap-1">
        {helpTextPrefix}
        <p className="text-xs text-on-surface-container/70">{helpText}</p>
      </div>
    )}
    
    This renders optional inline content (the tooltip icon) to the left of helpText. When helpTextPrefix is undefined/null, the flex container still renders correctly with just the <p>.

FieldRenderer.tsx — Fix text-branch tooltip (lines 83-109):

  1. Remove the tooltip <button> from ABOVE TextFieldMD3 (lines 85-93). Remove the tooltip content <p> from above as well (lines 95-98).
  2. Add onMouseEnter / onMouseLeave state: add const [hoverTooltip, setHoverTooltip] = useState(false) alongside existing showTooltip state.
  3. Create a tooltip icon element that will be passed as helpTextPrefix:
    const tooltipIcon = field.tooltipText ? (
      <button
        type="button"
        onClick={() => setShowTooltip(v => !v)}
        onMouseEnter={() => setHoverTooltip(true)}
        onMouseLeave={() => setHoverTooltip(false)}
        aria-label={`More info about ${field.label}`}
        className="text-primary hover:text-primary/80 text-xs leading-none mt-px shrink-0"
      >
        
      </button>
    ) : undefined;
    
  4. Compute tooltip visibility: const tooltipVisible = showTooltip || hoverTooltip.
  5. Render the tooltip content BELOW TextFieldMD3 (after the component, inside the flex-col wrapper), conditioned on tooltipVisible:
    {field.tooltipText && tooltipVisible && (
      <p className="text-xs text-primary bg-primary/10 border border-primary/30 rounded px-2 py-1.5">
        {field.tooltipText}
      </p>
    )}
    
  6. Pass helpTextPrefix={tooltipIcon} to TextFieldMD3.
  7. The final text-branch return should look like:
    return (
      <div className="flex flex-col gap-1">
        <TextFieldMD3
          id={field.key}
          label={field.label}
          error={error}
          registration={register(field.key)}
          helpText={field.helpText}
          required={field.required}
          helpTextPrefix={tooltipIcon}
        />
        {field.tooltipText && tooltipVisible && (
          <p className="text-xs text-primary bg-primary/10 border border-primary/30 rounded px-2 py-1.5">
            {field.tooltipText}
          </p>
        )}
      </div>
    );
    

FieldRenderer.tsx — Fix select-branch tooltip (lines 40-78):

The select branch also has a tooltip button. Apply the same hover fix to the select branch:

  1. Add onMouseEnter={() => setHoverTooltip(true)} and onMouseLeave={() => setHoverTooltip(false)} to the existing select-branch tooltip button (line 51).
  2. Change the tooltip content visibility condition from showTooltip to tooltipVisible (line 59).
  3. The select branch does NOT use TextFieldMD3, so no helpTextPrefix needed there — keep the icon in its current position above the select (it already has a visible label, not a floating label).

PasswordField.tsx — Same fix as FieldRenderer text-branch:

  1. Add const [hoverTooltip, setHoverTooltip] = useState(false) alongside existing showTooltip.
  2. Remove the tooltip button from above TextFieldMD3 (lines 31-40). Remove the tooltip content <p> from above (lines 41-44).
  3. Create the same tooltipIcon element with onClick, onMouseEnter, onMouseLeave, aria-label={More info about ${label}}.
  4. Compute const tooltipVisible = showTooltip || hoverTooltip.
  5. Pass helpTextPrefix={tooltipIcon} to TextFieldMD3.
  6. Render tooltip content after TextFieldMD3, conditioned on tooltipVisible.

IMPORTANT: Preserve existing aria-label format exactly as More info about ${field.label} (or ${label} in PasswordField) — existing tests assert this. cd C:/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vitest run --reporter=verbose 2>&1 | tail -30 - Tooltip icon renders inline to the left of helpText below the field (not above the field) - Hovering the tooltip icon shows tooltip text - Clicking the tooltip icon pins tooltip open; clicking again dismisses - Mouse-leaving the icon hides tooltip (unless pinned via click) - All existing tests pass including FieldRenderer aria-label tests - PasswordField has identical fix

Task 2: Add tests for tooltip hover behavior and helpTextPrefix rendering src/components/ui/FieldRenderer.test.tsx, src/components/ui/TextFieldMD3.test.tsx - Test: Hovering tooltip button shows tooltip text (fireEvent.mouseEnter on button, then expect tooltipText visible) - Test: Mouse-leaving tooltip button hides tooltip text (fireEvent.mouseLeave, then expect tooltipText not visible) - Test: Clicking tooltip button pins tooltip open, mouseLeave does NOT hide it - Test: TextFieldMD3 renders helpTextPrefix inline with helpText when prop provided - Test: TextFieldMD3 renders helpText normally when helpTextPrefix is not provided **FieldRenderer.test.tsx — Add hover behavior tests:**

Add a new describe block 'FieldRenderer - tooltip hover behavior' with these tests:

  1. 'hovering tooltip button shows tooltip text':

    • Render FieldRenderer with textFieldWithTooltip (already defined in test file)
    • Get tooltip button via screen.getByRole('button', { name: /more info about bucket name/i })
    • fireEvent.mouseEnter(tooltipBtn)
    • expect(screen.getByText('The name of your storage bucket')).toBeDefined()
  2. 'mouse-leaving tooltip button hides tooltip text':

    • Render, mouseEnter, verify visible, then fireEvent.mouseLeave(tooltipBtn)
    • expect(screen.queryByText('The name of your storage bucket')).toBeNull()
  3. 'clicking tooltip button pins it open through mouseLeave':

    • Render, fireEvent.click(tooltipBtn), fireEvent.mouseLeave(tooltipBtn)
    • expect(screen.getByText('The name of your storage bucket')).toBeDefined() (still visible because pinned)
  4. 'clicking pinned tooltip button again dismisses it':

    • Render, click (pin), click again (unpin)
    • expect(screen.queryByText('The name of your storage bucket')).toBeNull()

Import fireEvent from @testing-library/react (add to existing import).

TextFieldMD3.test.tsx — Add helpTextPrefix tests:

Add tests in existing file (or create new describe block):

  1. 'renders helpTextPrefix inline with helpText':

    • Render TextFieldMD3 with helpText="Some help" and helpTextPrefix={<span data-testid="prefix">icon</span>}
    • expect(screen.getByTestId('prefix')).toBeDefined()
    • expect(screen.getByText('Some help')).toBeDefined()
  2. 'renders helpText without wrapper issues when helpTextPrefix is undefined':

    • Render TextFieldMD3 with helpText="Some help" and no helpTextPrefix
    • expect(screen.getByText('Some help')).toBeDefined() cd C:/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vitest run src/components/ui/FieldRenderer.test.tsx src/components/ui/TextFieldMD3.test.tsx --reporter=verbose 2>&1 | tail -40
    • All new tooltip hover tests pass (mouseEnter shows, mouseLeave hides, click pins, click again unpins)
    • helpTextPrefix rendering test passes
    • All pre-existing FieldRenderer and TextFieldMD3 tests still pass
1. `npx vitest run --reporter=verbose` — all tests pass (179+ existing + new tooltip tests) 2. Manual spot-check: tooltip icon appears beside helpText below field, not above field 3. Hover behavior: mouseEnter shows tooltip, mouseLeave hides it, click pins it

<success_criteria>

  • UAT test 7 passes: tooltip info buttons show tooltip text on hover AND icon is positioned beside helpText below the field
  • Zero test regressions
  • Both FieldRenderer (text-branch + select-branch) and PasswordField have hover support
  • TextFieldMD3 has helpTextPrefix prop for extensibility </success_criteria>
After completion, create `.planning/phases/09-md3-components/09-05-SUMMARY.md`