From 3d27339ef311b5023b5f9480fbc9fca1f49d9078 Mon Sep 17 00:00:00 2001 From: Josh Adam Date: Fri, 10 Jul 2026 17:20:45 -0500 Subject: [PATCH 1/7] docs(tooltip): align previews with design contract --- .../tooltip_preview/accessibility.html.erb | 112 ++++------ .../advanced_features.html.erb | 203 +++++++++--------- .../tooltip_preview/basic_usage.html.erb | 131 +++++------ .../browser_compatibility.html.erb | 189 ++++++++-------- .../click_through_after_hide.html.erb | 36 ++-- .../tooltip_preview/link_integration.html.erb | 175 +++++++-------- 6 files changed, 397 insertions(+), 449 deletions(-) diff --git a/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb b/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb index 64ae07e2..01790186 100644 --- a/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb @@ -2,45 +2,45 @@
-
-

+
+

Tooltip Accessibility & Keyboard Navigation

-

+

Demonstrating ARIA compliance, keyboard accessibility, and screen reader support

-
-

+
+

Dual Trigger Mechanism

-

+

Mouse Hover

-

+

Tooltips appear when you hover over the trigger element with your mouse.

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip(text: "I appear on mouse hover!", placement: :top) %> Hover over me <% end %> @@ -49,15 +49,15 @@
-

+

Keyboard Focus

-

+

Tooltips also appear when you focus the element using Tab key navigation.

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip(text: "I appear on keyboard focus!", placement: :top) %> Tab to focus me <% end %> @@ -68,7 +68,7 @@
@@ -88,37 +88,31 @@
-
-

+
+

ARIA Compliance

-

+

Tooltips follow W3C ARIA best practices to ensure screen reader compatibility:

-
-

- role="tooltip" +

+ role="tooltip"

-

+

The tooltip element has role="tooltip" to inform screen readers that this is supplementary information.

@@ -127,19 +121,13 @@
-
-

- aria-describedby +

+ aria-describedby

-

+

The trigger element references the tooltip via aria-describedby, creating an accessible relationship between them. Screen readers announce the tooltip content when the trigger receives focus.

@@ -148,19 +136,13 @@
-
-

+

Unique Tooltip IDs

-

+

Each tooltip is automatically assigned a unique ID to ensure proper ARIA connections and avoid conflicts.

@@ -169,15 +151,15 @@
-

+

Example HTML Structure:

-
<a href="/samples" aria-describedby="tooltip-abc123"
+        
<a href="/samples" aria-describedby="tooltip-abc123"
             data-pathogen--tooltip-target="trigger"> Samples </a>
             <div id="tooltip-abc123" role="tooltip"
             data-pathogen--tooltip-target="tooltip"> View all samples
@@ -188,23 +170,23 @@
   
-
-

+
+

Keyboard Navigation Demo

-

- Use the Tab key to +

+ Use the Tab key to navigate through these links. The tooltip will appear when each link receives focus.

-
+
<%= render Pathogen::Link.new(href: "#first") do |link| %> <% link.with_tooltip(text: "First link in the sequence", placement: :top) %> First Link <% end %> @@ -226,7 +208,7 @@
@@ -243,7 +225,7 @@
@@ -262,7 +244,7 @@
-

+

Accessibility Best Practices

@@ -270,7 +252,7 @@
@@ -289,7 +271,7 @@
@@ -308,7 +290,7 @@
@@ -327,7 +309,7 @@
diff --git a/test/components/previews/pathogen/tooltip_preview/advanced_features.html.erb b/test/components/previews/pathogen/tooltip_preview/advanced_features.html.erb index a5e87a7a..69505fc1 100644 --- a/test/components/previews/pathogen/tooltip_preview/advanced_features.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/advanced_features.html.erb @@ -1,36 +1,29 @@ <%# herb-lint-disable erb-no-trailing-whitespace %> -
+
-
-

- <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 inline mr-3") %> Advanced - Features & Edge Cases +
+

+ Advanced Features & Edge Cases

-

- Exploring edge cases, long text handling, animations, and advanced tooltip behaviors +

+ Testing long laboratory labels, viewport boundaries, and overlapping tooltip triggers

-
-

- <%= icon('file-text', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Long Text & - Max-Width Constraint +
+

+ Long Text & Max-Width Constraint

-

+

Tooltips automatically wrap long text and enforce a max-width of 320px (max-w-xs) to prevent excessively wide tooltips.

@@ -38,28 +31,28 @@
-

+

Short Text

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip(text: "Brief tooltip", placement: :top) %> Hover me (short tooltip) <% end %>
-

+

Short tooltips size to fit their content tightly.

-

+

Long Text

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip( text: @@ -70,7 +63,7 @@ <% end %>
-

+

Long text wraps naturally within the 320px max-width.

@@ -79,7 +72,7 @@
@@ -101,106 +94,105 @@
-
-

- <%= icon('play', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Smooth Fade-In/Scale - Animation +
+

+ Smooth Fade-In/Scale Animation

-

+

Tooltips feature a polished 200ms animation combining opacity fade-in and subtle scale transition (0.9 → 1.0).

-

+

Top Placement Animation

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip( text: "Fade in and scale from bottom", placement: :top, ) %> - <%= icon('arrow-up', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-2") %> + <%= icon('arrow-up', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 inline mr-2") %> Hover to see animation <% end %>
-

+

Scales from origin-bottom (appears to grow from trigger)

-

+

Bottom Placement Animation

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip( text: "Fade in and scale from top", placement: :bottom, ) %> - <%= icon('arrow-down', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-2") %> + <%= icon('arrow-down', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 inline mr-2") %> Hover to see animation <% end %>
-

+

Scales from origin-top (appears to grow from trigger)

-

+

Left Placement Animation

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip( text: "Fade in and scale from right", placement: :left, ) %> - <%= icon('arrow-left', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-2") %> + <%= icon('arrow-left', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 inline mr-2") %> Hover to see animation <% end %>
-

+

Scales from origin-right (appears to grow from trigger)

-

+

Right Placement Animation

-
+
<%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip( text: "Fade in and scale from left", ) -%> - <%= icon('arrow-right', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-2") %> + <%= icon('arrow-right', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 inline mr-2") %> Hover to see animation <% end %>
-

+

Scales from origin-left (appears to grow from trigger)

@@ -209,7 +201,7 @@
@@ -231,42 +223,41 @@
-
-

- <%= icon('arrows-out', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Viewport - Boundary Handling +
+

+ Viewport Boundary Handling

-

+

The tooltip component includes sophisticated viewport boundary detection that automatically adjusts tooltip placement when near the edges of the browser window.

<%= icon('info', class: "w-5 h-5 flex-shrink-0 text-blue-500 dark:text-blue-400") %>
-

+

Automatic Collision Detection

-

+

When a tooltip would overflow the viewport, the JavaScript positioning algorithm automatically:

-
    +
    • Flips placement - Switches from top ↔ bottom or left ↔ right when approaching viewport edges @@ -282,7 +273,7 @@
    -

    +

    Note: This feature works automatically in the application. Test it by hovering over links near the edges of your browser window while using IRIDA Next.

    @@ -293,24 +284,23 @@
-
-

- <%= icon('grid-four', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Multiple Tooltips - in Close Proximity +
+

+ Multiple Tooltips in Close Proximity

-

+

Multiple tooltips can coexist on the same page without conflicts. Each has a unique ID for proper ARIA connections.

-
+
<%= render Pathogen::Link.new(href: "#1") do |link| %> <% link.with_tooltip(text: "First tooltip", placement: :top) %> Link 1 <% end %> @@ -348,40 +338,39 @@
-

- <%= icon('gear', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Technical - Implementation +

+ Technical Implementation

-

+

CSS Classes

-
    +
    • - transition-all duration-200 ease-out - - Smooth animation + transition-all duration-200 ease-out + - Smooth animation
    • - opacity-0 scale-90 invisible - Hidden - state + opacity-0 scale-90 invisible - + Hidden state
    • - opacity-100 scale-100 visible - + opacity-100 scale-100 visible - Visible state
    • - max-w-xs inline-block - + max-w-xs inline-block - Content-adaptive sizing
    @@ -389,77 +378,77 @@
    -

    +

    Positioning

    -
      +
      • - getBoundingClientRect() - Gets trigger - element coordinates + getBoundingClientRect() - Gets + trigger element coordinates
      • - position: fixed - Viewport-relative - positioning + position: fixed - + Viewport-relative positioning
      • - Viewport detection - Automatic + Viewport detection - Automatic placement flipping
      • - transform-origin - Scale animation - origin + transform-origin - Scale + animation origin
    -

    +

    Event Listeners

    -
      +
      • - mouseenter - Show on hover + mouseenter - Show on hover
      • - mouseleave - Hide on hover out + mouseleave - Hide on hover out
      • - focusin - Show on focus + focusin - Show on focus
      • - focusout - Hide on blur + focusout - Hide on blur
    -

    +

    Design Philosophy

    -
      +
      • Primer-inspired visual design
      • No arrow indicator (simplified)
      • Dark background for contrast
      • diff --git a/test/components/previews/pathogen/tooltip_preview/basic_usage.html.erb b/test/components/previews/pathogen/tooltip_preview/basic_usage.html.erb index e50cc6c2..e7f2f3f2 100644 --- a/test/components/previews/pathogen/tooltip_preview/basic_usage.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/basic_usage.html.erb @@ -1,38 +1,31 @@ -
        +
        -
        -

        - <%= icon('info', class: "text-blue-500 dark:text-blue-400 w-5 h-5 flex-shrink-0 inline mr-3") %> Pathogen Tooltip - - Basic Usage +
        +

        + Pathogen Tooltip - Basic Usage

        -

        - Accessible tooltips with JavaScript positioning, smooth animations, and Primer-inspired design. +

        + Accessible tooltips for compact clinical interfaces, with keyboard and pointer support.

        -
        -

        - <%= icon('compass', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Four Placement - Options +
        +

        + Four Placement Options

        -

        Top Placement (Default)

        +

        Top Placement (Default)

        <%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip(text: "This tooltip appears above the link", placement: :top) %> Hover or focus me @@ -43,7 +36,7 @@
        -

        Left Placement

        +

        Left Placement

        <%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip(text: "This tooltip appears to the left", placement: :left) %> Hover or focus me @@ -53,7 +46,7 @@
        -

        Right Placement

        +

        Right Placement

        <%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip(text: "This tooltip appears to the right", placement: :right) %> Hover or focus me @@ -64,7 +57,7 @@
        -

        Bottom Placement

        +

        Bottom Placement

        <%= render Pathogen::Link.new(href: "#") do |link| %> <% link.with_tooltip( @@ -79,22 +72,22 @@
        -
        -

        - <%= icon('code', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Simple Examples +
        +

        + Simple Examples

        -

        Default Tooltip

        +

        Default Tooltip

        -
        +
        <%= render Pathogen::Link.new(href: "/samples") do |link| %> <% link.with_tooltip(text: "View all samples") %> Samples <% end %> @@ -104,13 +97,13 @@
        -

        Custom Placement

        +

        Custom Placement

        -
        +
        <%= render Pathogen::Link.new(href: "/projects") do |link| %> <% link.with_tooltip(text: "Manage your projects", placement: :bottom) %> Projects <% end %> @@ -120,13 +113,13 @@
        -

        Left Placement

        +

        Left Placement

        -
        +
        <%= render Pathogen::Link.new(href: "/settings") do |link| %> <% link.with_tooltip(text: "Configure application settings", placement: :left) %> Settings <% end %> @@ -136,13 +129,13 @@
        -

        Right Placement

        +

        Right Placement

        -
        +
        <%= render Pathogen::Link.new(href: "/help") do |link| %> <% link.with_tooltip(text: "Get help and documentation", placement: :right) %> Help <% end %> @@ -152,33 +145,27 @@
        -
        -

        - <%= icon('sliders', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Component - Parameters +
        +

        + Component Parameters

        -
        +
        - - - - - + + + + + - + - + - + - + - + - @@ -221,15 +208,15 @@
        -

        - <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-3") %> Quick Tips +

        + Quick Tips

        @@ -248,7 +235,7 @@
        @@ -266,17 +253,17 @@
        <%= icon('lightning', class: "w-5 h-5 flex-shrink-0 text-amber-500 dark:text-amber-400") %>
        -

        Smooth Animations

        +

        Smooth Animations

        -

        +

        200ms fade-in and scale animation for a polished feel

        @@ -286,7 +273,7 @@
        diff --git a/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb b/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb index 1fb5b0a4..865a5db9 100644 --- a/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb @@ -1,35 +1,28 @@ -
        +
        -
        -

        - <%= icon('globe', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-3") %> Browser - Compatibility +
        +

        + Browser Compatibility

        -

        +

        JavaScript positioning with viewport detection for broad browser compatibility

        -
        -

        - <%= icon('check-circle', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 mr-3") %> Universal - Browser Compatibility +
        +

        + Universal Browser Compatibility

        -

        +

        This tooltip component uses JavaScript-based positioning for maximum browser compatibility. Works in all modern browsers with ES6+ JavaScript support:

        @@ -38,7 +31,7 @@
        @@ -61,7 +54,7 @@
        @@ -84,7 +77,7 @@
        @@ -107,7 +100,7 @@
        @@ -131,7 +124,7 @@
        @@ -152,18 +145,18 @@
        -
        -

        - <%= icon('shield', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Positioning Strategy +
        +

        + Positioning Strategy

        -

        +

        The tooltip component uses JavaScript-based positioning with sophisticated viewport detection:

        @@ -172,28 +165,28 @@
        - <%= icon('code', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('code', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %>
        -

        Precise Position Calculation

        +

        Precise Position Calculation

        -

        +

        Tooltips calculate their optimal position using `getBoundingClientRect()` to get exact trigger element coordinates, then apply calculated positions via inline styles.

        - position: fixed; top: ${top}px; left: ${left}px; + position: fixed; top: ${top}px; left: ${left}px;
        @@ -202,22 +195,22 @@
        - <%= icon('arrows-in', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('arrows-in', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %>
        -

        Automatic Viewport Detection

        +

        Automatic Viewport Detection

        -

        +

        The Stimulus controller detects when tooltips would overflow viewport edges and automatically flips placement:

        -
          +
          • <%= icon('check', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> Top placement flips to bottom when near top edge @@ -245,17 +238,17 @@
            - <%= icon('lock', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('lock', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %>
            -

            Position Clamping

            +

            Position Clamping

            -

            +

            If a tooltip can't fit in its preferred placement even after flipping, the positioning algorithm clamps it to viewport bounds to ensure it remains fully visible with proper padding.

            @@ -266,19 +259,18 @@
            -
            -

            - <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-3") %> Why JavaScript - Positioning? +
            +

            + Why JavaScript Positioning?

            -

            +

            The decision to use custom JavaScript positioning instead of external libraries or cutting-edge CSS features brings several advantages:

            @@ -287,11 +279,11 @@
            - <%= icon('globe', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('globe', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Broad Browser Compatibility

            @@ -306,11 +298,11 @@
            - <%= icon('download', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('download', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Smaller Bundle Size

            @@ -324,17 +316,17 @@
            - <%= icon('arrows-in', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('arrows-in', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %>
            -

            Sophisticated Viewport Detection

            +

            Sophisticated Viewport Detection

            -

            +

            Automatic placement flipping and position clamping ensure tooltips always stay within viewport bounds.

            @@ -344,11 +336,11 @@
            - <%= icon('code', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('code', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Full Control

            @@ -362,17 +354,17 @@
            - <%= icon('puzzle-piece', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('puzzle-piece', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %>
            -

            Lightweight Dependencies

            +

            Lightweight Dependencies

            -

            +

            Uses Floating UI instead of Flowbite, reducing maintenance burden and improving positioning accuracy.

            @@ -382,11 +374,11 @@
            - <%= icon('shield', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('shield', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Reliable & Predictable

            @@ -402,26 +394,25 @@
            -
            -

            - <%= icon('arrows-clockwise', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Migration - from Flowbite +
            +

            + Migration from Flowbite

            -

            +

            This tooltip component was redesigned to remove the Flowbite dependency, which used Popper.js for positioning.

            -

            +

            <%= icon('x', class: "text-red-500 dark:text-red-400 w-5 h-5 flex-shrink-0 mr-2") %> Before: Flowbite

            @@ -460,7 +451,7 @@
            -

            +

            <%= icon('check-circle', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 mr-2") %> After: Custom JavaScript

            @@ -509,23 +500,23 @@
            -

            - <%= icon('books', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Additional Resources +

            + Additional Resources

            -

            - <%= icon('link', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> +

            + <%= icon('link', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 mr-2") %> getBoundingClientRect API

            -

            +

            Learn about the DOM API used for precise element positioning calculations.

            @@ -541,16 +532,16 @@
            -

            +

            <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-2") %> Primer Design System

            -

            +

            Explore GitHub's Primer design system for tooltip design inspiration and patterns.

            @@ -566,15 +557,15 @@
            -

            - <%= icon('code', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> Stimulus Framework +

            + <%= icon('code', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 mr-2") %> Stimulus Framework

            -

            +

            Learn about the Stimulus JavaScript framework used for the tooltip controller.

            @@ -590,16 +581,16 @@
            -

            - <%= icon('wheelchair', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> ARIA Tooltip +

            + <%= icon('wheelchair', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 mr-2") %> ARIA Tooltip Pattern

            -

            +

            W3C WAI-ARIA best practices for implementing accessible tooltip components.

            diff --git a/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb b/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb index 9d45e7d1..4a417106 100644 --- a/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb @@ -1,22 +1,27 @@
            -
            -

            - <%= icon("cursor-click", class: "inline mr-3 size-5 text-blue-600") %> Tooltip Click-through After Hide +
            +

            + Tooltip Click-through After Hide

            -

            +

            This preview is a manual visual regression check for a bug where a tooltip becomes hidden but still blocks clicks on elements underneath it.

            -
            -
            +
            +

            Steps

            -
              +
              1. Hover the “Tooltip trigger” button to show the tooltip.
              2. Move your mouse away to hide the tooltip.
              3. @@ -27,18 +32,23 @@
            -
            +
            -

            Underlying action clicks

            -

            0

            +

            Underlying action clicks

            +

            0

            <%= render Pathogen::Button.new(id: 'pv-tooltip-reset', tone: :neutral, emphasis: :outline, size: :small, text: 'Reset') %>
            -
            +
            -

            +

            Tooltip placement is bottom. While open, it should overlap the button above; after it hides, the button must be clickable immediately.

            @@ -71,7 +81,7 @@
            -
            +
            <%= icon("warning", class: "size-5 text-amber-600 flex-shrink-0") %> diff --git a/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb b/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb index 0cd69b9f..9d97790e 100644 --- a/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb @@ -1,79 +1,67 @@ -
            +
            -
            -

            - <%= icon('link', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-3") %> Link Component - Integration +
            +

            + Link Component Integration

            -

            - Real-world examples of tooltips integrated with Pathogen::Link in various contexts +

            + Tooltip patterns for project navigation, sample actions, breadcrumbs, and compact sidebars

            -
            -

            - <%= icon('squares-four', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Navigation Bar - with Tooltips +
            +

            + Navigation Bar with Tooltips

            -
            -
            -

            - <%= icon('list', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Action List with - Contextual Tooltips +
            +

            + Action List with Contextual Tooltips

            -
            +
            -

            +

            Sample-001

            -

            +

            Collected: 2024-01-15

            @@ -108,28 +100,33 @@
            <%= render Pathogen::Link.new(href: "/samples/1/view") do |link| %> <% link.with_tooltip(text: "View sample details", placement: :top) %> - <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/1/edit") do |link| %> <% link.with_tooltip(text: "Edit sample metadata", placement: :top) %> - <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/1/download") do |link| %> <% link.with_tooltip(text: "Download sample files", placement: :top) %> - <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> <% end %>
            -
            +
            -

            +

            Sample-002

            -

            +

            Collected: 2024-01-16

            @@ -137,17 +134,17 @@
            <%= render Pathogen::Link.new(href: "/samples/2/view") do |link| %> <% link.with_tooltip(text: "View sample details", placement: :top) %> - <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/2/edit") do |link| %> <% link.with_tooltip(text: "Edit sample metadata", placement: :top) %> - <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/2/download") do |link| %> <% link.with_tooltip(text: "Download sample files", placement: :top) %> - <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> + <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> <% end %>
            @@ -156,7 +153,7 @@
            @@ -178,56 +175,55 @@
            -
            -

            - <%= icon('map-trifold', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Breadcrumb - Navigation +
            +

            + Breadcrumb Navigation

            <%= icon('info', class: "w-5 h-5 flex-shrink-0 text-blue-500 dark:text-blue-400") %>
            -

            +

            Breadcrumb Tooltips

            -

            +

            Tooltips in breadcrumbs help users understand where each link will take them, improving navigation confidence.

            @@ -238,57 +234,51 @@
            -
            -

            - <%= icon('sidebar', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Collapsed Sidebar - with Tooltips +
            +

            + Collapsed Sidebar with Tooltips

            -

        ParameterTypeRequiredDescription
        ParameterTypeRequiredDescription
        textStringString The tooltip text contentThe tooltip text content
        placementSymbolSymbol @@ -209,7 +196,7 @@ + Position: :top (default), :bottom, :left, :right
        - - - - - + + + + + - + - + - + - + - + - @@ -208,15 +221,15 @@
        -

        - Quick Tips +

        + <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-3") %> Quick Tips

        @@ -235,7 +248,7 @@
        @@ -253,17 +266,17 @@
        <%= icon('lightning', class: "w-5 h-5 flex-shrink-0 text-amber-500 dark:text-amber-400") %>
        -

        Smooth Animations

        +

        Smooth Animations

        -

        +

        200ms fade-in and scale animation for a polished feel

        @@ -273,7 +286,7 @@
        diff --git a/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb b/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb index 865a5db9..1fb5b0a4 100644 --- a/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb @@ -1,28 +1,35 @@ -
        +
        -
        -

        - Browser Compatibility +
        +

        + <%= icon('globe', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-3") %> Browser + Compatibility

        -

        +

        JavaScript positioning with viewport detection for broad browser compatibility

        -
        -

        - Universal Browser Compatibility +
        +

        + <%= icon('check-circle', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 mr-3") %> Universal + Browser Compatibility

        -

        +

        This tooltip component uses JavaScript-based positioning for maximum browser compatibility. Works in all modern browsers with ES6+ JavaScript support:

        @@ -31,7 +38,7 @@
        @@ -54,7 +61,7 @@
        @@ -77,7 +84,7 @@
        @@ -100,7 +107,7 @@
        @@ -124,7 +131,7 @@
        @@ -145,18 +152,18 @@
        -
        -

        - Positioning Strategy +
        +

        + <%= icon('shield', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Positioning Strategy

        -

        +

        The tooltip component uses JavaScript-based positioning with sophisticated viewport detection:

        @@ -165,28 +172,28 @@
        - <%= icon('code', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('code', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %>
        -

        Precise Position Calculation

        +

        Precise Position Calculation

        -

        +

        Tooltips calculate their optimal position using `getBoundingClientRect()` to get exact trigger element coordinates, then apply calculated positions via inline styles.

        - position: fixed; top: ${top}px; left: ${left}px; + position: fixed; top: ${top}px; left: ${left}px;
        @@ -195,22 +202,22 @@
        - <%= icon('arrows-in', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('arrows-in', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %>
        -

        Automatic Viewport Detection

        +

        Automatic Viewport Detection

        -

        +

        The Stimulus controller detects when tooltips would overflow viewport edges and automatically flips placement:

        -
          +
          • <%= icon('check', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> Top placement flips to bottom when near top edge @@ -238,17 +245,17 @@
            - <%= icon('lock', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('lock', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %>
            -

            Position Clamping

            +

            Position Clamping

            -

            +

            If a tooltip can't fit in its preferred placement even after flipping, the positioning algorithm clamps it to viewport bounds to ensure it remains fully visible with proper padding.

            @@ -259,18 +266,19 @@
            -
            -

            - Why JavaScript Positioning? +
            +

            + <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-3") %> Why JavaScript + Positioning?

            -

            +

            The decision to use custom JavaScript positioning instead of external libraries or cutting-edge CSS features brings several advantages:

            @@ -279,11 +287,11 @@
            - <%= icon('globe', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('globe', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Broad Browser Compatibility

            @@ -298,11 +306,11 @@
            - <%= icon('download', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('download', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Smaller Bundle Size

            @@ -316,17 +324,17 @@
            - <%= icon('arrows-in', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('arrows-in', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %>
            -

            Sophisticated Viewport Detection

            +

            Sophisticated Viewport Detection

            -

            +

            Automatic placement flipping and position clamping ensure tooltips always stay within viewport bounds.

            @@ -336,11 +344,11 @@
            - <%= icon('code', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('code', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Full Control

            @@ -354,17 +362,17 @@
            - <%= icon('puzzle-piece', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('puzzle-piece', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %>
            -

            Lightweight Dependencies

            +

            Lightweight Dependencies

            -

            +

            Uses Floating UI instead of Flowbite, reducing maintenance burden and improving positioning accuracy.

            @@ -374,11 +382,11 @@
            - <%= icon('shield', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 flex-shrink-0") %> + <%= icon('shield', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %>

            Reliable & Predictable

            @@ -394,25 +402,26 @@
            -
            -

            - Migration from Flowbite +
            +

            + <%= icon('arrows-clockwise', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Migration + from Flowbite

            -

            +

            This tooltip component was redesigned to remove the Flowbite dependency, which used Popper.js for positioning.

            -

            +

            <%= icon('x', class: "text-red-500 dark:text-red-400 w-5 h-5 flex-shrink-0 mr-2") %> Before: Flowbite

            @@ -451,7 +460,7 @@
            -

            +

            <%= icon('check-circle', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 mr-2") %> After: Custom JavaScript

            @@ -500,23 +509,23 @@
            -

            - Additional Resources +

            + <%= icon('books', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Additional Resources

            -

            - <%= icon('link', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 mr-2") %> +

            + <%= icon('link', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> getBoundingClientRect API

            -

            +

            Learn about the DOM API used for precise element positioning calculations.

            @@ -532,16 +541,16 @@
            -

            +

            <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-2") %> Primer Design System

            -

            +

            Explore GitHub's Primer design system for tooltip design inspiration and patterns.

            @@ -557,15 +566,15 @@
            -

            - <%= icon('code', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 mr-2") %> Stimulus Framework +

            + <%= icon('code', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> Stimulus Framework

            -

            +

            Learn about the Stimulus JavaScript framework used for the tooltip controller.

            @@ -581,16 +590,16 @@
            -

            - <%= icon('wheelchair', class: "text-[var(--pvc-color-text-muted)] w-5 h-5 flex-shrink-0 mr-2") %> ARIA Tooltip +

            + <%= icon('wheelchair', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> ARIA Tooltip Pattern

            -

            +

            W3C WAI-ARIA best practices for implementing accessible tooltip components.

            diff --git a/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb b/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb index 4a417106..9d45e7d1 100644 --- a/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb @@ -1,27 +1,22 @@
            -
            -

            - Tooltip Click-through After Hide +
            +

            + <%= icon("cursor-click", class: "inline mr-3 size-5 text-blue-600") %> Tooltip Click-through After Hide

            -

            +

            This preview is a manual visual regression check for a bug where a tooltip becomes hidden but still blocks clicks on elements underneath it.

            -
            -
            +
            +

            Steps

            -
              +
              1. Hover the “Tooltip trigger” button to show the tooltip.
              2. Move your mouse away to hide the tooltip.
              3. @@ -32,23 +27,18 @@
            -
            +
            -

            Underlying action clicks

            -

            0

            +

            Underlying action clicks

            +

            0

            <%= render Pathogen::Button.new(id: 'pv-tooltip-reset', tone: :neutral, emphasis: :outline, size: :small, text: 'Reset') %>
            -
            +
            -

            +

            Tooltip placement is bottom. While open, it should overlap the button above; after it hides, the button must be clickable immediately.

            @@ -81,7 +71,7 @@
            -
            +
            <%= icon("warning", class: "size-5 text-amber-600 flex-shrink-0") %> diff --git a/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb b/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb index 9d97790e..0cd69b9f 100644 --- a/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/link_integration.html.erb @@ -1,67 +1,79 @@ -
            +
            -
            -

            - Link Component Integration +
            +

            + <%= icon('link', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-3") %> Link Component + Integration

            -

            - Tooltip patterns for project navigation, sample actions, breadcrumbs, and compact sidebars +

            + Real-world examples of tooltips integrated with Pathogen::Link in various contexts

            -
            -

            - Navigation Bar with Tooltips +
            +

            + <%= icon('squares-four', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Navigation Bar + with Tooltips

            -
            -
            -

            - Action List with Contextual Tooltips +
            +

            + <%= icon('list', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Action List with + Contextual Tooltips

            -
            +
            -

            +

            Sample-001

            -

            +

            Collected: 2024-01-15

            @@ -100,33 +108,28 @@
            <%= render Pathogen::Link.new(href: "/samples/1/view") do |link| %> <% link.with_tooltip(text: "View sample details", placement: :top) %> - <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/1/edit") do |link| %> <% link.with_tooltip(text: "Edit sample metadata", placement: :top) %> - <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/1/download") do |link| %> <% link.with_tooltip(text: "Download sample files", placement: :top) %> - <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> <% end %>
            -
            +
            -

            +

            Sample-002

            -

            +

            Collected: 2024-01-16

            @@ -134,17 +137,17 @@
            <%= render Pathogen::Link.new(href: "/samples/2/view") do |link| %> <% link.with_tooltip(text: "View sample details", placement: :top) %> - <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('eye', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/2/edit") do |link| %> <% link.with_tooltip(text: "Edit sample metadata", placement: :top) %> - <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('pencil', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> <% end %> <%= render Pathogen::Link.new(href: "/samples/2/download") do |link| %> <% link.with_tooltip(text: "Download sample files", placement: :top) %> - <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-[var(--pvc-color-text-muted)]") %> + <%= icon('download', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> <% end %>
            @@ -153,7 +156,7 @@
            @@ -175,55 +178,56 @@
            -
            -

            - Breadcrumb Navigation +
            +

            + <%= icon('map-trifold', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Breadcrumb + Navigation

            <%= icon('info', class: "w-5 h-5 flex-shrink-0 text-blue-500 dark:text-blue-400") %>
            -

            +

            Breadcrumb Tooltips

            -

            +

            Tooltips in breadcrumbs help users understand where each link will take them, improving navigation confidence.

            @@ -234,51 +238,57 @@
            -
            -

            - Collapsed Sidebar with Tooltips +
            +

            + <%= icon('sidebar', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Collapsed Sidebar + with Tooltips

            -

        ParameterTypeRequiredDescription
        ParameterTypeRequiredDescription
        textStringString The tooltip text contentThe tooltip text content
        placementSymbolSymbol @@ -196,7 +209,7 @@ + Position: :top (default), :bottom, :left, :right
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        ParameterTypeRequiredDescription
        textString - - Required - - The tooltip text content
        placementSymbol - - Optional - - - Position: :top (default), :bottom, :left, :right -
        -
        -
        -
        - - -
        -

        - <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-3") %> Quick Tips -

        - -
        -
        -
        - <%= icon('info', class: "w-5 h-5 flex-shrink-0 text-blue-500 dark:text-blue-400") %> - -
        -

        Default Placement is Top

        - -

        - If you don't specify a placement, tooltips will appear above the trigger element -

        -
        -
        -
        - -
        -
        - <%= icon('check-circle', class: "w-5 h-5 flex-shrink-0 text-green-500 dark:text-green-400") %> - -
        -

        Automatic Sizing

        - -

        - Tooltips automatically size to fit content with a max width of 320px -

        -
        -
        -
        - -
        -
        - <%= icon('lightning', class: "w-5 h-5 flex-shrink-0 text-amber-500 dark:text-amber-400") %> - -
        -

        Smooth Animations

        - -

        - 200ms fade-in and scale animation for a polished feel -

        -
        -
        -
        - -
        -
        - <%= icon('warning', class: "w-5 h-5 flex-shrink-0 text-amber-500 dark:text-amber-400") %> - -
        -

        Lightweight Dependencies

        - -

        - Uses Floating UI for precise positioning instead of Flowbite/Popper.js -

        -
        -
        -
        -
        -
        -
        diff --git a/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb b/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb deleted file mode 100644 index 1fb5b0a4..00000000 --- a/test/components/previews/pathogen/tooltip_preview/browser_compatibility.html.erb +++ /dev/null @@ -1,617 +0,0 @@ -
        - -
        -

        - <%= icon('globe', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 inline mr-3") %> Browser - Compatibility -

        - -

        - JavaScript positioning with viewport detection for broad browser compatibility -

        -
        - - -
        -

        - <%= icon('check-circle', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 mr-3") %> Universal - Browser Compatibility -

        - -
        -

        - This tooltip component uses JavaScript-based positioning for maximum browser compatibility. Works in all modern - browsers with ES6+ JavaScript support: -

        - -
        -
        -
        -
        - <%= icon('check', class: "w-5 h-5 flex-shrink-0 text-green-500 dark:text-green-400") %> -
        - -
        -
        Chrome
        -
        Modern versions
        -
        -
        -
        - -
        -
        -
        - <%= icon('check', class: "w-5 h-5 flex-shrink-0 text-green-500 dark:text-green-400") %> -
        - -
        -
        Firefox
        -
        Modern versions
        -
        -
        -
        - -
        -
        -
        - <%= icon('check', class: "w-5 h-5 flex-shrink-0 text-green-500 dark:text-green-400") %> -
        - -
        -
        Safari
        -
        Modern versions
        -
        -
        -
        - -
        -
        -
        - <%= icon('check', class: "w-5 h-5 flex-shrink-0 text-green-500 dark:text-green-400") %> -
        - -
        -
        Edge
        -
        Modern versions
        -
        -
        -
        -
        - -
        -
        - <%= icon('info', class: "w-5 h-5 flex-shrink-0 text-blue-500 dark:text-blue-400") %> - -
        -

        JavaScript Positioning

        - -

        - The tooltip uses JavaScript-based positioning with `getBoundingClientRect()` for precise placement. This - approach provides sophisticated features like viewport boundary detection and automatic placement flipping - that work consistently across all modern browsers. -

        -
        -
        -
        -
        -
        - - -
        -

        - <%= icon('shield', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Positioning Strategy -

        - -
        -

        - The tooltip component uses JavaScript-based positioning with sophisticated viewport detection: -

        - -
        - -
        -
        - <%= icon('code', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> -
        - -
        -

        Precise Position Calculation

        - -

        - Tooltips calculate their optimal position using `getBoundingClientRect()` to get exact trigger element - coordinates, then apply calculated positions via inline styles. -

        - -
        - position: fixed; top: ${top}px; left: ${left}px; -
        -
        -
        - - -
        -
        - <%= icon('arrows-in', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> -
        - -
        -

        Automatic Viewport Detection

        - -

        - The Stimulus controller detects when tooltips would overflow viewport edges and automatically flips - placement: -

        - -
          -
        • - <%= icon('check', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - Top placement flips to bottom when near top edge -
        • - -
        • - <%= icon('check', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - Bottom placement flips to top when near bottom edge -
        • - -
        • - <%= icon('check', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - Left/right placements flip when near corresponding edges -
        • - -
        • - <%= icon('check', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - Maintains 8px padding from viewport boundaries -
        • -
        -
        -
        - - -
        -
        - <%= icon('lock', class: "w-5 h-5 flex-shrink-0 text-slate-500 dark:text-slate-400") %> -
        - -
        -

        Position Clamping

        - -

        - If a tooltip can't fit in its preferred placement even after flipping, the positioning algorithm clamps it - to viewport bounds to ensure it remains fully visible with proper padding. -

        -
        -
        -
        -
        -
        - - -
        -

        - <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-3") %> Why JavaScript - Positioning? -

        - -
        -

        - The decision to use custom JavaScript positioning instead of external libraries or cutting-edge CSS features - brings several advantages: -

        - -
        -
        -
        - <%= icon('globe', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - -
        -

        Broad Browser Compatibility

        - -

        - Works in all modern browsers (Chrome, Firefox, Safari, Edge) without feature flags or polyfills. -

        -
        -
        -
        - -
        -
        - <%= icon('download', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - -
        -

        Smaller Bundle Size

        - -

        - No need to include Popper.js, Floating UI, or other positioning libraries, reducing JavaScript payload. -

        -
        -
        -
        - -
        -
        - <%= icon('arrows-in', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - -
        -

        Sophisticated Viewport Detection

        - -

        - Automatic placement flipping and position clamping ensure tooltips always stay within viewport bounds. -

        -
        -
        -
        - -
        -
        - <%= icon('code', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - -
        -

        Full Control

        - -

        - Custom implementation allows precise control over positioning logic and edge cases. -

        -
        -
        -
        - -
        -
        - <%= icon('puzzle-piece', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - -
        -

        Lightweight Dependencies

        - -

        - Uses Floating UI instead of Flowbite, reducing maintenance burden and improving positioning accuracy. -

        -
        -
        -
        - -
        -
        - <%= icon('shield', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 flex-shrink-0") %> - -
        -

        Reliable & Predictable

        - -

        - JavaScript positioning provides consistent behavior across all browsers without browser-specific quirks. -

        -
        -
        -
        -
        -
        -
        - - -
        -

        - <%= icon('arrows-clockwise', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Migration - from Flowbite -

        - -
        -

        - This tooltip component was redesigned to remove the Flowbite dependency, which used Popper.js for positioning. -

        - -
        - -
        -

        - <%= icon('x', class: "text-red-500 dark:text-red-400 w-5 h-5 flex-shrink-0 mr-2") %> Before: Flowbite -

        - -
        -
        -
        External Dependency
        -
        Required Flowbite + Popper.js libraries
        -
        - -
        -
        JavaScript Positioning
        -
        Complex JS-based positioning logic
        -
        - -
        -
        Larger Bundle
        -
        Additional JavaScript payload
        -
        -
        -
        - - -
        -

        - <%= icon('check-circle', class: "text-green-500 dark:text-green-400 w-5 h-5 flex-shrink-0 mr-2") %> After: - Custom JavaScript -

        - -
        -
        -
        Zero External Dependencies
        - -
        - Custom Stimulus controller with no positioning libraries -
        -
        - -
        -
        JavaScript Positioning
        - -
        - Sophisticated viewport detection with automatic placement flipping -
        -
        - -
        -
        Smaller Bundle
        -
        Reduced JavaScript footprint (no Popper.js)
        -
        -
        -
        -
        -
        -
        - - -
        -

        - <%= icon('books', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> Additional Resources -

        - -
        -
        -

        - <%= icon('link', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> - getBoundingClientRect API -

        - -

        - Learn about the DOM API used for precise element positioning calculations. -

        - - - MDN: getBoundingClientRect() → - -
        - -
        -

        - <%= icon('lightbulb', class: "text-yellow-500 dark:text-yellow-400 w-5 h-5 flex-shrink-0 mr-2") %> Primer - Design System -

        - -

        - Explore GitHub's Primer design system for tooltip design inspiration and patterns. -

        - - - Primer Tooltip Component → - -
        - -
        -

        - <%= icon('code', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> Stimulus Framework -

        - -

        - Learn about the Stimulus JavaScript framework used for the tooltip controller. -

        - - - Stimulus Documentation → - -
        - -
        -

        - <%= icon('wheelchair', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-2") %> ARIA Tooltip - Pattern -

        - -

        - W3C WAI-ARIA best practices for implementing accessible tooltip components. -

        - - - WAI-ARIA: Tooltip Pattern → - -
        -
        -
        -
        diff --git a/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb b/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb index 9d45e7d1..687a1a41 100644 --- a/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/click_through_after_hide.html.erb @@ -1,22 +1,14 @@ -
        -
        -

        - <%= icon("cursor-click", class: "inline mr-3 size-5 text-blue-600") %> Tooltip Click-through After Hide -

        - -

        - This preview is a manual visual regression check for a bug where a tooltip becomes hidden but still blocks clicks - on elements underneath it. -

        -
        - -
        -
        -

        - Steps -

        - -
          +
          +
          + <%= pathogen_heading(level: 3, class: "mb-2") { "Click-through after hide" } %> + + <%= pathogen_supporting(variant: :muted, class: "mb-4") do %> + Manual visual regression check for a bug where a tooltip becomes hidden but still blocks clicks on elements + underneath it. + <% end %> + +
          +
          1. Hover the “Tooltip trigger” button to show the tooltip.
          2. Move your mouse away to hide the tooltip.
          3. @@ -26,68 +18,72 @@
          +
          -
          +
          +
          -

          Underlying action clicks

          -

          0

          + <%= pathogen_supporting(tag: :span, variant: :muted) { "Underlying action clicks" } %> + +

          0

          - <%= render Pathogen::Button.new(id: 'pv-tooltip-reset', tone: :neutral, emphasis: :outline, size: :small, text: 'Reset') %> + <%= render Pathogen::Button.new(id: "pv-tooltip-reset", tone: :neutral, emphasis: :outline, size: :small, text: "Reset") %>
          -
          -
          -
          -
          - <%= render Pathogen::Button.new( - tone: :primary, - emphasis: :solid, - size: :small, - text: 'Tooltip trigger', - aria: { describedby: 'pv-tooltip-1' }, - data: { 'pathogen--tooltip-target': 'trigger' } - ) %> - - <%= render Pathogen::Tooltip.new( - id: "pv-tooltip-1", - text: "I should not block clicks after I hide.", - placement: :bottom, - ) %> -
          - - <%= render Pathogen::Button.new(id: 'pv-underlying-action', tone: :primary, emphasis: :solid, text: 'Underlying action') %> - -

          - Tooltip placement is bottom. While open, it should overlap the button above; after it hides, - the button must be clickable immediately. -

          +
          +
          +
          + <%= render Pathogen::Button.new( + tone: :primary, + emphasis: :solid, + size: :small, + text: "Tooltip trigger", + aria: { describedby: "pv-tooltip-1" }, + data: { "pathogen--tooltip-target": "trigger" } + ) %> + + <%= render Pathogen::Tooltip.new( + id: "pv-tooltip-1", + text: "I should not block clicks after I hide.", + placement: :bottom, + ) %>
          -
          -
          -
          -
          - <%= icon("warning", class: "size-5 text-amber-600 flex-shrink-0") %> + <%= render Pathogen::Button.new(id: "pv-underlying-action", tone: :primary, emphasis: :solid, text: "Underlying action") %> -
          -

          What “broken” looks like

          - -

          - If the tooltip is visually hidden but still sitting on top of the page (e.g. due to not being truly hidden - or still catching pointer events), clicks on “Underlying action” won’t increment the counter even though - the tooltip looks gone. -

          -
          + <%= pathogen_supporting(tag: :span, variant: :muted, class: "max-w-xl text-center") do %> + Tooltip placement is <%= pathogen_code { ":bottom" } %>. While open, it overlaps the button above; after it + hides, the button must be clickable immediately. + <% end %>
          + +
          + <%= pathogen_supporting(tag: :span, class: "font-semibold text-[var(--pvc-color-text)]") { "What “broken” looks like" } %> + + <%= pathogen_supporting(variant: :muted, class: "mt-2") do %> + If the tooltip is visually hidden but still intercepting pointer events, clicks on “Underlying action” will + not increment the counter even though the tooltip looks gone. + <% end %> +
          -
          +