Skip to content

Conversation

@RyukTheCoder
Copy link
Contributor

@RyukTheCoder RyukTheCoder commented Aug 23, 2024

Summary

Use Radix UI to improve modal component.
In result of this change:

  • Modal will close by pressing escape button
  • Focus will be trapped in modal

Fixes # 1777

Notes:
Currently, we are handling the logic related to showing animation on opening and closing modal and positioning the content of modal based on anchor prop. Before exploring the Radix UI document, we were optimistic about that it can handle these animation logics and positioning the content of modal, but after checking the documentation I didn't find such solutions so I kept it as it is.

How did you test this change?

Tested this change by checking most of modals.

Checklist:

  • 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
  • I have added tests that prove my fix is effective or that my feature works
  • Implemented a user interface (UI) change, referencing our Figma design to ensure pixel-perfect precision.

@RyukTheCoder RyukTheCoder changed the title chore: use radix to improve modal component WIP! chore: use radix to improve modal component Aug 23, 2024
@RyukTheCoder RyukTheCoder changed the title WIP! chore: use radix to improve modal component WIP!: chore: use radix to improve modal component Aug 23, 2024
@RyukTheCoder RyukTheCoder changed the title WIP!: chore: use radix to improve modal component WIP! chore: use radix to improve modal component Aug 23, 2024
@RyukTheCoder RyukTheCoder changed the title WIP! chore: use radix to improve modal component chore: use radix to improve modal component Aug 26, 2024
@RyukTheCoder RyukTheCoder force-pushed the chore/rf-1777-use-radix-to-improve-modal-component branch from 721a7c1 to 8765327 Compare September 14, 2024 07:06
@yeager-eren
Copy link
Collaborator

yeager-eren commented Sep 14, 2024

Thanks. Your changes looks good to me. For handling animation Radix seems using data attributes. Here is an example from Shadcn doing samething on top of Radix. Please take a look at the approach and see is there anyway to remove our js approach to handle animations.

@RyukTheCoder
Copy link
Contributor Author

Thanks. Your changes looks good to me. For handling animation Radix seems using data attributes. Here is an example from Shadcn doing samething on top of Radix. Please take a look at the approach and see is there anyway to remove our js approach to handle animations.

Please check my latest changes. I used data attributes to handle animations.

@RyukTheCoder
Copy link
Contributor Author

In this commit I tried to prevent an error related to radix which indicates that "DialogContent requires a DialogTitle for the component to be accessible for screen reader users.". It still exits for modals with a custom header. This error will completely be removed by removing custom headers and using ModalHeader component.

});

export const DialogTitle = styled(Dialog.DialogTitle, {
margin: 0,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this will affect a11y and they did the trick for that. We can ignore 1px difference.

Copy link
Contributor Author

@RyukTheCoder RyukTheCoder Sep 30, 2024

Choose a reason for hiding this comment

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

This was not added to handle 1px difference. Actually with position set to absolute, there is no 1px difference. When we wrap title with Dialog.DialogTitle it will be wrapped in a h2 element which have a default margin so I added this to remove the unwanted margin.

Copy link
Collaborator

@yeager-eren yeager-eren left a comment

Choose a reason for hiding this comment

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

LGTM.

Copy link
Collaborator

@yeager-eren yeager-eren left a comment

Choose a reason for hiding this comment

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

Thanks.
Since we've opened this PR some time ago, please make sure the dependencies are in their latest version.

// Restrict the overlay click area to just the overlay element, rather than the entire page
const handleOverlayClick: MouseEventHandler = (event) => {
if (event.target === overlayRef.current) {
handleBackDropClick(!open);
Copy link
Collaborator

Choose a reason for hiding this comment

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

handleBackDropClick is only used here, so you can remove the function and move its code block to here.


if (event.target === event.currentTarget && dismissible) {
// The escape key functionality only works when the focus is within the overlay (modal).
const handleScapeKeyDown = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const handleScapeKeyDown = () => {
const handleEscapeKeyDown = () => {

<Dialog.Portal container={container}>
<DialogOverlay ref={overlayRef} onClick={handleOverlayClick}>
<DialogContent
onClick={handleContentClick}
Copy link
Collaborator

@yeager-eren yeager-eren Dec 2, 2024

Choose a reason for hiding this comment

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

Instead of hacking with onClick and :focus, you can consider using onInteractOutside.
ref

It will be like:

onInteractOutside={handleOverlayClick}

Ensure renaming the function and also remove the onClicks.

@Ikari-Shinji-re
Copy link
Member

Currently, we have a Modal component in our design system package. However, our implementation works differently depending on its container.

Based on our requirements for our widget and other applications, we have identified two types of modals:

A variant that occupies the entire viewport, blocking user interaction outside its content.
A variant that behaves like a modal but can receive a specific container, blocking interaction only within that container when open.
In this pull request, we aimed to integrate @radix-ui/react-dialog into our modal component to better handle animation, state management, and accessibility. However, due to limitations in their API and implementation, some issues couldn't be resolved cleanly. I also evaluated other libraries like MUI and Headless UI, but they align with Radix’s modal implementation, adhering to the W3C dialog modal pattern:
https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/

These libraries position the modal as fixed, blocking the entire viewport, which does not meet our requirement for container-based positioning (e.g., bottom sheets in our widget).

To address this, I propose splitting the Modal component into two separate components:

Modal: A standard modal built with external packages like Radix UI, incorporating most of the changes made in this pull request.
BottomSheet: A new component based on our current implementation. This would allow absolute positioning within a container while maintaining the behavior required for bottom sheets. We can enhance its accessibility by managing focus and adding keyboard shortcuts.
While reviewing other UI libraries, I noticed their drawer components function similarly to modals, blocking the entire viewport. Therefore, keeping our existing implementation for BottomSheet seems to be the most practical approach to meet our specific needs.

@yeager-eren
Copy link
Collaborator

@Ikari-Shinji-re Thanks. I will close this issue and we will address this in our upcoming design system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants