diff --git a/test/components/previews/pathogen/tooltip_preview.rb b/test/components/previews/pathogen/tooltip_preview.rb index e775bf40..cf69c96a 100644 --- a/test/components/previews/pathogen/tooltip_preview.rb +++ b/test/components/previews/pathogen/tooltip_preview.rb @@ -1,38 +1,28 @@ # frozen_string_literal: true module Pathogen - # ViewComponent preview for demonstrating Pathogen::Tooltip usage - # Showcases placement options, accessibility features, animations, and browser compatibility class TooltipPreview < ViewComponent::Preview include Pathogen::ViewHelper - # @!group Pathogen Tooltip Component + # @!group Pathogen Tooltip - # @label Basic Usage & Getting Started - # Simple examples showing all four placement options and basic tooltip functionality - def basic_usage; end + # @label Overview + # Placement reference, sizing behaviour, and specimen toolbar examples + def overview; end - # @label Accessibility & Keyboard Navigation - # Demonstrates accessibility features, ARIA patterns, hover/focus triggers, and keyboard support - def accessibility; end + # @label In context + # Icon-only actions, collapsed navigation, and grid row utilities + def in_context; end - # @label Link Component Integration - # Shows integration with Pathogen::Link component in various real-world contexts - def link_integration; end + # @label Accessibility patterns + # Hover and focus triggers, ARIA wiring, and keyboard navigation + def accessibility; end - # @label Visual Test: Click-through after hide + # @label Visual test: Click-through after hide # Repro for "tooltip hides but still blocks clicks" issues. # After the tooltip closes, the button underneath should be clickable immediately. def click_through_after_hide; end - # @label Advanced Features & Edge Cases - # Demonstrates long text handling, max-width constraint, animations, and edge cases - def advanced_features; end - - # @label Browser Compatibility - # Information about CSS anchor positioning support and fallback behavior - def browser_compatibility; end - # @!endgroup end end diff --git a/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb b/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb index 64ae07e2..25e64bec 100644 --- a/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb +++ b/test/components/previews/pathogen/tooltip_preview/accessibility.html.erb @@ -1,347 +1,96 @@ <%# herb:formatter ignore %> -
- -
-

- Tooltip Accessibility & Keyboard Navigation -

+
+
+ <%= pathogen_heading(level: 3, class: "mb-2") { "Hover and focus triggers" } %> -

- Demonstrating ARIA compliance, keyboard accessibility, and screen reader support -

-
+ <%= pathogen_supporting(variant: :muted, class: "mb-4") do %> + Tooltips open on mouse hover and keyboard focus. Supporting both keeps supplementary copy available to pointer, + keyboard, and screen reader users. + <% end %> - -
-

- Dual Trigger Mechanism -

+
+
+ <%= pathogen_supporting(tag: :span, variant: :muted, class: "mb-3 block") { "Mouse hover" } %> -
-
- -
-

- 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 %> -
-
- - -
-

- 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 %> -
-
+ <%= render Pathogen::Link.new(href: "#") do |link| %> + <% link.with_tooltip(text: "QC passed on 46 of 48 specimens", placement: :top) %> Hover me + <% end %>
-
-
-
-

- Why Both Triggers? -

+
+ <%= pathogen_supporting(tag: :span, variant: :muted, class: "mb-3 block") { "Keyboard focus" } %> -

- Supporting both hover and focus ensures the tooltip is accessible to all users: mouse users see it on - hover, while keyboard and screen reader users see it on focus. -

-
-
+ <%= render Pathogen::Link.new(href: "#") do |link| %> + <% link.with_tooltip(text: "Opens on Tab focus as well as hover", placement: :top) %> Tab to focus me + <% end %>
-
- - -
-

- ARIA Compliance -

- -
-

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

- -
- -
-
- -
-

- role="tooltip" -

- -

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

-
-
- - -
-
- -
-

- 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. -

-
-
- - -
-
- -
-

- Unique Tooltip IDs -

- -

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

-
-
-
- -
-

- Example HTML Structure: -

- -
<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
-            </div>
-                                          
-
+ + +
+ <%= pathogen_heading(level: 3, class: "mb-2") { "ARIA wiring" } %> + + <%= pathogen_supporting(variant: :muted, class: "mb-4") do %> + Each tooltip exposes <%= pathogen_code { 'role="tooltip"' } %> and is referenced from the trigger with + <%= pathogen_code { "aria-describedby" } %>. IDs are generated per instance so multiple tooltips can coexist on one + surface. + <% end %> + +
+
<a href="/specimens" aria-describedby="tooltip-abc123"
+   data-pathogen--tooltip-target="trigger">Specimens</a>
+<div id="tooltip-abc123" role="tooltip"
+   data-pathogen--tooltip-target="tooltip">Browse submitted specimens</div>
-
+ - -
-

- Keyboard Navigation Demo -

+
+ <%= pathogen_heading(level: 3, class: "mb-2") { "Keyboard navigation" } %> -
-

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

+ <%= pathogen_supporting(variant: :muted, class: "mb-4") do %> + Tab through the links below. Each trigger should announce its tooltip when focused. + <% end %> -
- <%= render Pathogen::Link.new(href: "#first") do |link| %> - <% link.with_tooltip(text: "First link in the sequence", placement: :top) %> First Link +
+
+ <%= render Pathogen::Link.new(href: "#runs") do |link| %> + <% link.with_tooltip(text: "Open sequencing runs", placement: :top) %> Runs <% end %> - <%= render Pathogen::Link.new(href: "#second") do |link| %> - <% link.with_tooltip(text: "Second link in the sequence", placement: :top) %> Second Link + <%= render Pathogen::Link.new(href: "#specimens") do |link| %> + <% link.with_tooltip(text: "Browse specimen records", placement: :top) %> Specimens <% end %> - <%= render Pathogen::Link.new(href: "#third") do |link| %> - <% link.with_tooltip(text: "Third link in the sequence", placement: :top) %> Third Link + <%= render Pathogen::Link.new(href: "#protocols") do |link| %> + <% link.with_tooltip(text: "Review analysis protocols", placement: :top) %> Protocols <% end %> - <%= render Pathogen::Link.new(href: "#fourth") do |link| %> - <% link.with_tooltip(text: "Fourth link in the sequence", placement: :top) %> Fourth Link + <%= render Pathogen::Link.new(href: "#exports") do |link| %> + <% link.with_tooltip(text: "Download export history", placement: :top) %> Exports <% end %>
- -
-
-
- Tab - - Move Forward -
- -

- Press Tab to move to the next focusable element -

-
- -
-
- Shift+Tab - - Move Backward -
- -

- Press Shift+Tab to move to the previous element -

-
-
-
- - -
-

- Accessibility Best Practices -

+
-
-
-
-
-

- Keep Text Concise -

- -

- Tooltips should provide brief, supplementary information. Avoid lengthy descriptions. -

-
-
-
+
+ <%= pathogen_heading(level: 3, class: "mb-2") { "Writing guidance" } %> -
-
-
-

- Supplement, Don't Replace -

+ <%= pathogen_supporting(variant: :muted, class: "mb-4") do %> + Tooltips supplement visible UI. Keep copy short, avoid duplicating the trigger label, and never hide required + workflow information behind hover-only text. + <% end %> -

- Tooltips should enhance understanding, not be the sole source of critical information. -

-
-
+
+
+ <%= pathogen_supporting(tag: :span, class: "font-semibold text-[var(--pvc-color-text)]") { "Prefer" } %> + <%= pathogen_supporting(variant: :muted, class: "mt-2") { "Distinct names on repeated icon-only actions." } %>
-
-
-
-

- Keyboard Accessible -

- -

- Always ensure tooltips appear on focus, not just hover, for keyboard users. -

-
-
-
- -
-
-
-

- Screen Reader Friendly -

- -

- Proper ARIA attributes ensure screen readers announce tooltip content naturally. -

-
-
+
+ <%= pathogen_supporting(tag: :span, class: "font-semibold text-[var(--pvc-color-text)]") { "Avoid" } %> + <%= pathogen_supporting(variant: :muted, class: "mt-2") { "Long paragraphs or instructions that belong in help text or dialogs." } %>
-
+
diff --git a/test/components/previews/pathogen/tooltip_preview/advanced_features.html.erb b/test/components/previews/pathogen/tooltip_preview/advanced_features.html.erb deleted file mode 100644 index a5e87a7a..00000000 --- a/test/components/previews/pathogen/tooltip_preview/advanced_features.html.erb +++ /dev/null @@ -1,471 +0,0 @@ -<%# 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 -

- -

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

-
- - -
-

- <%= 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 -

- -
-

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

- -
- -
-

- 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: - "This is a very long tooltip text that demonstrates how the tooltip component automatically wraps text and enforces a maximum width constraint to prevent excessively wide tooltips that would be hard to read.", - placement: :top, - ) %> - Hover me (long tooltip) - <% end %> -
- -

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

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

- Content-Adaptive Design -

- -

- Tooltips use inline-block display to wrap tightly around short content, while - max-w-xs ensures long text doesn't create unwieldy wide tooltips. -

-
-
-
-
-
- - -
-

- <%= icon('play', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> 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") %> - 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") %> - 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") %> - 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") %> - Hover to see animation - <% end %> -
- -

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

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

- Smooth & Natural Animation -

- -

- The ease-out timing function creates a natural deceleration, while the subtle scale (0.9 to - 1.0) combined with opacity fade creates a polished, professional feel inspired by Primer's design system. -

-
-
-
-
-
- - -
-

- <%= icon('arrows-out', class: "text-slate-500 dark:text-slate-400 w-5 h-5 flex-shrink-0 mr-3") %> 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 -
  • - -
  • - Clamps position - Keeps tooltips within viewport bounds with 8px padding to prevent - overflow -
  • - -
  • - Maintains readability - Ensures tooltip content remains fully visible and accessible -
  • -
- -

- 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. -

-
-
-
-
-
- - -
-

- <%= 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 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 %> - - <%= render Pathogen::Link.new(href: "#2") do |link| %> - <% link.with_tooltip(text: "Second tooltip", placement: :top) %> Link 2 - <% end %> - - <%= render Pathogen::Link.new(href: "#3") do |link| %> - <% link.with_tooltip(text: "Third tooltip", placement: :top) %> Link 3 - <% end %> - - <%= render Pathogen::Link.new(href: "#4") do |link| %> - <% link.with_tooltip(text: "Fourth tooltip", placement: :top) %> Link 4 - <% end %> - - <%= render Pathogen::Link.new(href: "#5") do |link| %> - <% link.with_tooltip(text: "Fifth tooltip", placement: :top) %> Link 5 - <% end %> - - <%= render Pathogen::Link.new(href: "#6") do |link| %> - <% link.with_tooltip(text: "Sixth tooltip", placement: :top) %> Link 6 - <% end %> - - <%= render Pathogen::Link.new(href: "#7") do |link| %> - <% link.with_tooltip(text: "Seventh tooltip", placement: :top) %> Link 7 - <% end %> - - <%= render Pathogen::Link.new(href: "#8") do |link| %> - <% link.with_tooltip(text: "Eighth tooltip", placement: :top) %> Link 8 - <% end %> -
-
-
- - -
-

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

- -
-
-

- CSS Classes -

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

- Positioning -

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

- Event Listeners -

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

- Design Philosophy -

- -
    -
  • Primer-inspired visual design
  • -
  • No arrow indicator (simplified)
  • -
  • Dark background for contrast
  • -
  • Content-adaptive width
  • -
-
-
-
-
diff --git a/test/components/previews/pathogen/tooltip_preview/basic_usage.html.erb b/test/components/previews/pathogen/tooltip_preview/basic_usage.html.erb deleted file mode 100644 index e50cc6c2..00000000 --- a/test/components/previews/pathogen/tooltip_preview/basic_usage.html.erb +++ /dev/null @@ -1,306 +0,0 @@ -
- -
-

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

- -

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

-
- - -
-

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

- -
-
- -
-

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 - (top) - <% end %> -
- -
- -
-

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 - (left) - <% end %> -
- - -
-

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 - (right) - <% end %> -
-
- - -
-

Bottom Placement

- - <%= render Pathogen::Link.new(href: "#") do |link| %> - <% link.with_tooltip( - text: "This tooltip appears below the link", - placement: :bottom, - ) %> - Hover or focus me (bottom) - <% end %> -
-
-
-
- - -
-

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

- -
- -
-

Default Tooltip

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

Custom Placement

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

Left Placement

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

Right Placement

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

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

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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..f514d235 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: :neutral, + emphasis: :outline, + 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 %> +
    -
    +