This repository has been archived by the owner on Dec 20, 2024. It is now read-only.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Changes
components
directory and added aModal
component in there._document.tsx
on which theModal
component depends.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 thediv#contra_modal_container
. If more than oneModal
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 modalonClose
: 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.style
,className
,ref
,onMouseEnter
, etc that are passed to theModal
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 theModal
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 thediv#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 theautofocus
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.