App.tsx, App.css
- App entrypoint and where an instance of theConfirmDeleteModal
is used for testingbuttons.tsx
- Establish some baseline buttons with proper stylesindex.css
- Styles taken from the@prefecthq/prefect-design
librarymain.tsx
- Root entrypoint for app, simply used to mount the ReactApp
to the DOM nodeindex.html
tailwind.config.ts
- Additional tailwind config taken from the@prefecthq/prefect-design
libraryConfirmDeleteModal
- contains the component along with an additional styles fileuseClickOutside.ts
- React hook for running some functionality if the user clicks outside the component
tailwind
- The existing component library leverages tailwind, so I thought I should follow suit.styled-components
- This ensured I could use intended brand variables for various colors.font-awesome
- Get some standard icons into the project
Upon building out the component there were a few considerations I made up front.
- I wanted to ensure the styling matched the brand as close as possible along with using the tools that the open source library was using for styling
- While I have some experience developing in Vue, I still wanted to leverage ChatGPT to see if it could bring to light any syntax/conceptual functionality misunderstandings I might of had with just reading the component itself. It would also give me a decent starting point with the component.
With those 2 things in mind. I started taking a look at how the existing component library was leveraging tailwind and any css to declare global variables. Come to find a lot of work has already been done there so I was able to pull in a lot of that pre-existing color palette. However, I wasn't sure if there was a way to leverage the globally declared variables within the tailwind class usage (this was at least an issue with a company I used to work at). Took a quick look through the tailwind configs but I didn't want to take too much time diving into configuration. Therefore I reached for the styled-components
library which is relatively standard for the React ecosystem. Ultimately I just used it to establish some baseline Button styles (buttons.tsx
).
When it came to using tailwind itself, the open source library made use of the @apply
directive to supply tailwind shorthand to developer defined classes. Seemed like a very reasonable method, but again without wanting to figure out how to make that work within the styled-component
configuration, I opted to just supply raw tailwind classes to the components where I could.
It's nice that Vue has a standard convention for styling because there are a lot of architectural choices that can be made when working with React and can supply some decision fatigue initially.
After I established some baseline styles and components, it was time to convert the ConfirmDeleteModal
. I submitted the Vue code to ChatGPT to see what kind of React template it would give me. It delivered on most of the functionality but there were 4 primary issues with it in my opinion.
- It did not properly translate the Vue
slots
fortitle
,actions
, andmessage
to props - It made use of the
useState
anduseEffect
hooks to track how theshowModal
prop changed over time. - It did not leverage
React.createPortal
in any way to ensure the browser's stacking context would not interfere with displaying the modal at the correct DOM level - It did not generate a proper typescript template
To address the slots
issue, I ensured that those could be passed in as additional props with a type of React.ReactNode
so the developer can use components, strings, etc as it seems that was the intended API from the Vue template.
I addressed the overuse of useState
and useEffect
by ensuring the showModal
prop would always be the source of truth to determine if the modal is open. This way, the state can be lifted out of the component and the business logic can live outside of a more functional component. Originally, the showModal
prop serving only as the default state for the internal useState
. Therefore a useEffect
was needed to be a listener for that prop to ensure the internal useState
was always accurate. And because useEffect
runs after a render from a state or prop change, it would always trigger an unnecessary re-render which could lead to performance problems down the road.
Once I established the intended functionality and a more "pixel perfect" styling of the modal as represented by the video, I started to make use of the React.createPortal
API as I would consider this a best practice when creating Modals or Tooltips. The developer can create an instance of the Modal anywhere within their component hierarchy without needing to worry about the browser's stacking context. The Modal will always appear above any content surrounding it. I think one opportunity for improvement here is actually to pass into the modal a reference to the intended mounting node as a prop.
I also added in a custom react hook that allows the user to "click outside" the modal in order to close it. This is another fairly standard piece of expected functionality when it comes to Modals. Given more time, I would have also like to address some accessibility needs when it comes to Modals in terms of focus management and labeling.
Additionally, I do work with Github Copilot locally for some help with speed and auto complete