Skip to content

Conversation

@pixelflips
Copy link
Member

@pixelflips pixelflips commented Nov 5, 2025

Description

Refactors pds-popover to use a slot-based approach for the trigger element, improving flexibility and developer experience. The trigger is now passed via a slot="trigger" instead of using popover-target, allowing any element (buttons, links, custom components) to activate the popover.

Key changes:

  • Implement slot-based trigger pattern with <slot name="trigger"> for better flexibility
  • Add portal rendering to document.body for correct positioning in all CSS contexts
  • Add debounced scroll/resize listeners to keep popover positioned with trigger
  • Add new "WithContent" story showcasing rich content using Pine components

Motivation: The slot-based approach provides more flexibility for consumers and aligns with modern web component patterns. The portal implementation ensures the component works correctly in all usage contexts, regardless of parent container styles.

Fixes https://kajabi.atlassian.net/browse/DSS-1567

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • unit tests - Comprehensive spec tests with fake timers for portal lifecycle, node movement, focus management, debouncing, and edge cases
  • e2e tests - Updated E2E tests to query portal elements and validate visibility/positioning
  • tested manually - Verified in Storybook across all placement options, scroll/resize behavior, and light dismiss functionality

Test Configuration:

  • Pine versions:
  • OS:
  • Browsers:
  • Screen readers:
  • Misc:

Checklist:

If not applicable, leave options unchecked.

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes
  • Design has QA'ed and approved this PR

- Move popover content to document.body using portal approach to escape CSS stacking contexts
- Add scroll and resize listeners with debouncing for dynamic positioning updates
- Implement proper focus management with Pine design system focus ring styles
- Move actual DOM nodes instead of cloning to preserve event handlers and component state
- Add timestamp-based light dismiss to prevent immediate closure on open
- Add guards for repositioning and zero-dimension edge cases
- Ensure proper cleanup of event listeners and portal elements on disconnect
- Document unused shadow DOM styles (replaced by inline portal styles)
@pixelflips pixelflips self-assigned this Nov 5, 2025
@netlify
Copy link

netlify bot commented Nov 5, 2025

Deploy Preview for pine-design-system ready!

Name Link
🔨 Latest commit f071ed6
🔍 Latest deploy log https://app.netlify.com/projects/pine-design-system/deploys/690bf3410d4d2b000867004f
😎 Deploy Preview https://deploy-preview-574--pine-design-system.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions bot added bug Something isn't working package: core Changes have been made to the Core package labels Nov 5, 2025
@pixelflips pixelflips marked this pull request as ready for review November 5, 2025 19:55
@pixelflips pixelflips changed the title fix(pds-popover): convert popover to slot based approach for trigger and portal rendering fix(pds-popover): convert to slot for trigger and portal rendering Nov 5, 2025
@pixelflips
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

📝 Walkthrough

Walkthrough

Refactors pds-popover from an inline, text-prop-driven component to a portal-based popover with slot-driven triggers. Removes the text property from PdsPopover and PdsFilter. Popover content is rendered into a body-level portal, with new trigger and content slots, positioning logic for placements, debounced repositioning, and light-dismiss/keyboard handling. Updates include SCSS adjustments, TypeScript interface changes (event/detail updates and removal of ToggleEvent), documentation and story examples switched to trigger slots, and extensive rewritten unit and E2E tests to validate portal and slot behavior.

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main refactoring: converting pds-popover to use slots for triggers and portal-based rendering, which is the primary change across all modified files.
Description check ✅ Passed The description is comprehensive and well-structured, covering the motivation, key changes, type of change, testing approaches, and providing a complete checklist with appropriate selections. It includes all required sections.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/convert-popover-to-slot-trigger

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1f3ddd and 4a825df.

📒 Files selected for processing (9)
  • libs/core/src/components.d.ts (0 hunks)
  • libs/core/src/components/pds-popover/docs/pds-popover.mdx (6 hunks)
  • libs/core/src/components/pds-popover/pds-popover.scss (1 hunks)
  • libs/core/src/components/pds-popover/pds-popover.tsx (4 hunks)
  • libs/core/src/components/pds-popover/popover-interface.ts (0 hunks)
  • libs/core/src/components/pds-popover/readme.md (1 hunks)
  • libs/core/src/components/pds-popover/stories/pds-popover.stories.js (2 hunks)
  • libs/core/src/components/pds-popover/test/pds-popover.e2e.ts (1 hunks)
  • libs/core/src/components/pds-popover/test/pds-popover.spec.tsx (2 hunks)
💤 Files with no reviewable changes (2)
  • libs/core/src/components/pds-popover/popover-interface.ts
  • libs/core/src/components.d.ts

@pixelflips
Copy link
Member Author

@coderabbitai addressed issue. please review again

@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

@pixelflips: I'll review the changes now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/core/src/components/pds-popover/pds-popover.tsx (1)

620-620: Add missing semicolon for consistency.

The offset constant declaration is missing a trailing semicolon, which is inconsistent with the rest of the codebase.

-    const offset = 8
+    const offset = 8;
🧹 Nitpick comments (1)
libs/core/src/components/pds-popover/stories/pds-popover.stories.js (1)

68-76: Consider a more defensive close handler.

The inline onclick handler assumes the element exists and is a pds-popover. While this works for the story, a more robust approach would prevent potential errors if the pattern is copied elsewhere.

Consider this alternative:

 <pds-button
   variant="unstyled"
   icon-only
-  onclick="document.getElementById('${args.componentId || 'popover-rich'}').hide()"
+  onclick="this.closest('pds-popover')?.hide()"
   aria-label="Close"
 >

This uses closest() to find the parent popover, eliminating the need for ID matching and making the code more portable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a825df and 7808e41.

📒 Files selected for processing (3)
  • libs/core/src/components/pds-popover/pds-popover.tsx (4 hunks)
  • libs/core/src/components/pds-popover/stories/pds-popover.stories.js (2 hunks)
  • libs/core/src/components/pds-popover/test/pds-popover.spec.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test-specs (20)
  • GitHub Check: test-specs (18)
  • GitHub Check: test-specs (22)
🔇 Additional comments (4)
libs/core/src/components/pds-popover/pds-popover.tsx (3)

114-221: Excellent lifecycle management.

The component properly:

  • Binds event handlers once for efficient cleanup
  • Creates the portal on mount with a fallback trigger initialization
  • Cleans up all listeners, timers, and DOM elements on disconnect

This prevents memory leaks and ensures reliable teardown.


308-356: Well-designed portal content synchronization.

The implementation correctly:

  • Preserves node identity, handlers, and state when moving to portal
  • Uses placeholder comments to track original positions
  • Prevents duplicate moves with isAlreadyInPortal and isAlreadyTracked checks
  • Restores content to original positions on hide

Combined with the content slot change handler (line 690), this ensures the portal stays in sync with dynamic slot updates while the popover is visible.


509-559: Robust light dismiss implementation.

The light dismiss logic properly:

  • Prevents the opening click from immediately closing via timestamp comparison
  • Handles the DOMHighResTimeStamp to epoch conversion correctly (well-commented)
  • Stops Escape key propagation to exclusively handle popover closure
  • Returns focus to trigger on Escape, matching native Popover API behavior
libs/core/src/components/pds-popover/test/pds-popover.spec.tsx (1)

1-825: Excellent comprehensive test coverage.

The test suite thoroughly covers:

  • Portal lifecycle (creation, removal, singleton behavior)
  • All 12 positioning placements with mocked geometry
  • Trigger and content slot handling including dynamic changes
  • Public API methods (show, hide, toggle)
  • Light dismiss behavior with timing considerations
  • Edge cases (null elements, unmounted state, repositioning guards, zero-dimension triggers)
  • Event emissions and ARIA attribute updates

This level of coverage ensures the portal-based refactor works correctly across all scenarios.

@pixelflips pixelflips merged commit 8ee29ec into main Nov 7, 2025
46 of 53 checks passed
@pixelflips pixelflips deleted the fix/convert-popover-to-slot-trigger branch November 7, 2025 00:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working package: core Changes have been made to the Core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants