Skip to content
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

Added Modal component + example usage #98

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

kasvtv
Copy link

@kasvtv kasvtv commented Apr 14, 2023

Changes

  • Created a components directory and added a Modal component in there.
  • Added a container div and style definitions to the _document.tsx on which the Modal component depends.
  • Changed the index page to provide some example modal functionality

Modal component

The Modal component is an easy-to-use component that can be rendered anywhere in the React component tree. Because it uses portals, it doesn't matter where in the React component tree it is rendered, it will always render within the div#contra_modal_container. If more than one Modal component is rendered at a time, the one rendered last will always be on top, regardless of its position in the React component tree.

Modal component props

  • children: A React node defining the content of the modal
  • onClose: An optional function that is run when the X-shaped close button of the modal is pressed. Omitting this argument will not render the close button at all, allowing you to have modals that force the user to interact with them without being able to close them.
  • any valid HTMLElement props/attributes: Any props such as style, className, ref, onMouseEnter, etc that are passed to the Modal component are relayed to the <div /> that holds the modal content. This makes the component very modular and extensible, allowing a plethora of customizations to a specific modal. If desired, this could also be done for the close button, but I chose not to, as to keep the component simple for the sake of demonstration.

Closing the modal

Pressing the X-shaped close button doesn't automatically get rid of the modal, it only fires the onClose callback. This is intentional, because modals are usually shown as a result of some event, and thus tend to be rendered conditionally depending on some state. As such, I figured that changing said state so the Modal is no longer rendered, causing it to unmount, is the most natural way to close them. This also conveniently allows the consumer of the component to programmatically control closing the modal.

Modal component prerequisites

The Modal component requires a small amount of global styling and a container div for the modals to be rendered into. See the added <style> tag and the div#contra_modal_container. In a client-side rendered React app, this would go into the .html file, while in Next.js this goes in the _document.tsx. Of course, the container div has some DOM mutations happening to it, but this should be fine, since the _document.tsx is only rendered once on the server, and not on the client, and is thus guaranteed to never rerender.

Examples on the index page

I added a couple of example buttons on the index page to demonstrate stacking and focus management. Check out modal 3 to see the autofocus attribute working properly. I'll walk through these in the Loom video as well.

Accessibility and tab navigation

The Modal uses a custom hook (defined in src/components/Modal/utils.ts) that takes care of setting the background, focus management, and tab navigation. Once a modal opens, if focus is on an element outside of the modal, that focus is lost, effectively making the rest of the page inoperable. The next time Tab is pressed, the first element in the modal gains focus. When Tab is pressed repeatedly, focus cycles through only the current modal's elements. Note that the autofocus attribute of React still works inside the modal (See example modal 3 on the index page).

Notes

I assumed visual styling doesn't matter for this test, because it's not part of the list of requirements in the doc. I applied some very basic styling to the modals themselves (background darkening, close button) just for the purposes of demonstration. My main priority was to create a reusable component that was well-written, simple, and easy to use.

@kasvtv
Copy link
Author

kasvtv commented Apr 17, 2023

It seems that the ESLint plugin of my editor did not activate properly, so I had missed the linting errors.
I just pushed another commit fixing those errors, the code should be functionally equivalent.

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

Successfully merging this pull request may close these issues.

None yet

1 participant