The scope of this task was to create a modal with the following features and their implementation status:
- A button is provided.
- The modal opens when the button is clicked.
- The modal opens with a fade in transition.
- The modal closes with a fade out transition.
- The modal has a button. The modal closes when the button in the modal is clicked.
- If the content of the modal exceeds the size of the modal it is possible to scroll in the modal(optional)
- In mobile view the modal is displayed in fullscreen.(optional)
- In desktop view the modal has a fixed size.(optional)
- In desktop view the mod al can be closed with a click outside the modal.
Commands | Purpose |
---|---|
dev | Run the application in dev mode |
build | Build the application for production |
test | Runs the test using vitest |
coverage | Runs the tests with test coverage |
The project has the following structure:
Folder | Purpose |
---|---|
configs | Holds the configs for the project |
components | Has the resuable components of the application |
hooks | Holds the custom hooks used |
mocks | Mocks for both tests and mock server |
The project is scaffolded using Vite with react and typescript.
The modal is architected using the dialog element. This allows to use the native browser api to meet this tasks requirements. The choice was also made taking into consideration the aspects of accessibility example focus trapping and more. It was also taken into consideration that dialog is now supported by ~ 95% of the browsers as shown here. And to extend its support for older browsers, the polyfill could be used. By using the showModal
method on the dialog, it helps to announce the content as a dialog, similar to that using role="dialog"
and also has aria-modal="true"
. It also adds inert
attribute to all other contents to ignore the other elements. The modal is developed as a standalone component and hence can be reused.
The dialog component is expected to provide with the bare minimals required for each modal and the contents being rendered are hence passed as children. It also provides a mechanism to customise the styling of the modal by having the ability to pass css classes which would help in overriding the default styles. It also has provides controls with a set of props passed to control the functionality of the modal. The following are the accepted props and their purpose:
Props | Type | Purpose |
---|---|---|
children | ReactNode | Contents of the modal |
isOpen | boolean | Flag to show or hide the modal |
shouldCloseOnOverlayClick | boolean | Control the modal behavior on clicking |
shouldCloseOnEsc | boolean | Control the modal behavior on pressing the esc key |
classes | string | CSS classes to ustomise styles |
For testing, vitest is used as the test runner. This comes with a lot of advantages over Jest by allowing to reuse the Vite.js configurations and not needing to provide custom configurations only for testing. Together with the same, Testing Library is used to perform integration testing. To accomodate cases where API calls are involved, MSW is also integrated.
I had assumed that in a real world scenario the filters should be configurable. And hence the data comes from an API. To accomodate these cases, I have used React Query together with React Query Kit to build reusable hooks for fetching the data.
- When opening the modal, the focus goes to the only button at the bottom of the screen and is expected as the focus is trapped.
- Tests fails with happy-dom when testing react hooks and hence JSDOM had to be used as the testing environment while testing the same. Also in JSDOM testing `dialog` is not yet support. The issue discussing the same could be found here. The test environment could be set using `// @vitest-environment jsdom` in the beginning of the test file.