-
Notifications
You must be signed in to change notification settings - Fork 1
Add optional portal rendering for modals #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Add a `portal` attribute to the modal component allowing users to opt out
of portal rendering. By default, modals continue to use portals (portal={true})
for proper z-index stacking, but can now render inline with portal={false}.
Changes:
- Add portal attribute (boolean, default: true) to modal component
- Extract modal container to private function for reusability
- Update JavaScript hook to detect and handle both portal and non-portal modes
- Add comprehensive test coverage for non-portal functionality
- Add documentation on portal behavior and CSS considerations
The implementation is fully backward compatible - existing modals continue
to use portals by default.
04a22e3 to
a9a0355
Compare
Pull Request ReviewThis PR adds optional portal rendering for modals. The implementation is well-structured and maintains backward compatibility. Here's my feedback: ✅ Strengths
🐛 Potential Issues1. JavaScript Reference Error (High Priority)Location: The hook references // Line 116 - should use this.modalEl
this.el.removeAttribute('aria-hidden')
// Line 155 - should use this.modalEl
this.el.setAttribute('aria-hidden', 'true')
// Line 202 - should use this.modalEl
this.el.setAttribute('aria-labelledby', titleElement.id)Impact: In portal mode, ARIA attributes are being set on the portal wrapper instead of the actual dialog element, which breaks accessibility. Fix: Replace these with 2. Element Lookup Inconsistency (Medium Priority)Location: The ref(ref) {
return this.el.querySelector(`[data-prima-ref="${ref}"]`);
}Impact: In portal mode, queries won't find child elements since they're inside Fix: Change to 3. Event Listener Target (Medium Priority)Location: DOM event listeners are registered on this.listeners = [
[this.el, "prima:modal:open", this.handleModalOpen.bind(this)],
[this.el, "prima:modal:close", this.handleModalClose.bind(this)],
// ...
]Impact: Custom events in the JS API target Recommendation: Consider registering these listeners on 💡 Code Quality Suggestions4. Missing Edge Case in Element DetectionLocation: The detection logic assumes portals always have this.modalEl = document.getElementById(this.el.id.replace('-portal', ''))Suggestion: Add a comment referencing where this convention is established ( 5. Fixture References Wrong Modal IDLocation: The button uses <.button type="button" phx-click={Prima.Modal.JS.open("no-portal-modal")}>Impact: While it may work due to aliasing, using the documented API is clearer. 📋 Test Coverage ObservationsThe tests are comprehensive, but there's one missing scenario: Missing Test: Focus restoration when closing via close button (not just escape key) Current tests check:
Missing:
Suggestion: Add a test similar to 🔒 Security & Performance
📚 DocumentationThe documentation is clear and helpful. One minor suggestion: Location: The warning about parent containers could be more actionable: **Important:** When `portal={false}`, the modal's parent container must not have
`overflow: hidden`, `overflow: clip`, or restrictive `position` values. These CSS
properties will prevent the modal overlay from covering the entire viewport. If you
cannot control parent styles, use the default portal mode (`portal={true}`).
🎯 RecommendationStatus: Request Changes The ARIA attribute bugs (#1) are critical for accessibility and should be fixed before merging. The ref() method issue (#2) should also be addressed to prevent future bugs. The other suggestions are nice-to-haves that can be addressed in follow-up work. ✅ After FixesOnce the critical issues are resolved, this will be an excellent addition to Prima. The feature is well-designed, thoroughly tested, and solves a real use case while maintaining backward compatibility. Great work! 🚀 |
The phx-hook is always attached directly to the modal dialog element (which has role="dialog"), never to the portal wrapper. Therefore, this.el already refers to the correct element and the modalEl variable was redundant. Also updated modal_without_portal_fixture to use the new Prima.Modal.JS.open/close API.
Code Review for PR #19: Add optional portal rendering for modalsI've completed a thorough review of this PR. Overall, this is a well-implemented feature with excellent test coverage and backward compatibility. Here's my detailed feedback: ✅ Strengths1. Excellent Backward Compatibility
2. Comprehensive Test Coverage
3. Code Quality
4. Documentation
🔍 Issues & Suggestions1. Missing
|
Summary
This PR adds a
portalattribute to the modal component, allowing users to opt out of portal rendering while maintaining backward compatibility.Changes
portalattribute - Boolean attribute (default:true) to control portal usagemodal_container/1functionsetupElements()to detect and handle both portal and non-portal modes by checking forrole="dialog"attributeUsage
Test Results
Breaking Changes
None - fully backward compatible. All existing modals continue to use portals by default.