React Modal Component with TypeScript
This challenge asks you to build a reusable modal component in React using TypeScript. Modals are essential for displaying important information, confirmations, or forms in a non-intrusive way, and a well-designed, reusable component can significantly improve the user experience and code maintainability of your applications.
Problem Description
You need to create a Modal component that can be opened and closed programmatically. The component should overlay the main content of the page, dimming the background and displaying the modal content in a centered position. The modal should accept a title, a children prop (containing the content to be displayed within the modal), and a function to be called when the modal is closed. The component should also handle keyboard presses (Escape key) to close the modal.
Key Requirements:
- Overlay: A semi-transparent overlay should cover the background when the modal is open.
- Centering: The modal content should be centered both horizontally and vertically.
- Close Function: The component must accept a
onCloseprop, which is a function that will be called when the modal is closed (either by clicking the close button or pressing Escape). - Open/Closed State: The component should manage its own open/closed state.
- Keyboard Accessibility: Pressing the Escape key should close the modal.
- TypeScript: The component must be written in TypeScript, with appropriate type definitions for props and state.
Expected Behavior:
- When the
isOpenprop istrue, the overlay and modal content should be visible and centered. - When the
isOpenprop isfalse, the overlay and modal content should be hidden. - Clicking outside the modal content (on the overlay) should trigger the
onClosefunction. - Pressing the Escape key should trigger the
onClosefunction. - The modal content should be rendered within the modal container.
Edge Cases to Consider:
- What happens if
childrenis null or undefined? The modal should still render correctly (without content). - How should the component handle different screen sizes? (Consider responsiveness, though a full responsive design is not required for this challenge).
- What happens if
onCloseis not provided? (The component should still function, perhaps by logging a warning).
Examples
Example 1:
Input:
<Modal isOpen={true} onClose={() => console.log("Modal closed")}>
<h2>Confirmation</h2>
<p>Are you sure you want to proceed?</p>
<button>Confirm</button>
<button>Cancel</button>
</Modal>
Output:
A modal window appears with the title "Confirmation", the message "Are you sure you want to proceed?", and two buttons labeled "Confirm" and "Cancel". The background is dimmed.
Explanation: The modal is opened and displays the provided content. Clicking "Cancel" or pressing Escape will call the `onClose` function.
Example 2:
Input:
<Modal isOpen={false} onClose={() => console.log("Modal closed")}>
<h2>Information</h2>
<p>This is some important information.</p>
</Modal>
Output:
The modal is not visible. The overlay is not present.
Explanation: The modal is closed because `isOpen` is false.
Example 3: (Edge Case)
Input:
<Modal isOpen={true} onClose={() => console.log("Modal closed")}>
</Modal>
Output:
A modal window appears with a title (empty) and no content. The background is dimmed.
Explanation: The modal renders correctly even when no children are provided.
Constraints
- The component should be implemented as a functional component using React Hooks.
- The overlay should have an opacity of 0.5.
- The modal content should have a maximum width of 500px.
- The component should be reusable and easily configurable.
- The solution must be written in TypeScript.
Notes
- Consider using CSS modules or styled-components for styling. Plain CSS is also acceptable.
- Think about how to handle focus management when the modal opens and closes (though perfect focus management is not required for this challenge).
- Focus on creating a clean, well-structured, and maintainable component.
- The
isOpenprop is the primary control for showing/hiding the modal. You don't need to implement a separate "open" button within the component itself.