[javascript] Split form and non-form modals and support custom modal footers - #10695
[javascript] Split form and non-form modals and support custom modal footers#10695MaximeBICMTL wants to merge 4 commits into
Conversation
a5cae91 to
b2bd1ae
Compare
| }>; | ||
|
|
||
| export type FormModalProps = Omit<ModalProps, 'footer'> & { | ||
| onSubmit?: () => Promise<any> | any; |
There was a problem hiding this comment.
By splitting it into a specific FormModal I think we should require an onSubmit function to be passed. I can't think of a case where we would want a form with no submit function.
There was a problem hiding this comment.
Hhhhm, I initially kept FormModalProps the exact same to not break backwards compatibility, but I just checked and it does appear that all call sites provide an onSubmit prop, so I think your call is right.
I pushed a new commit that makes onSubmit mandatory and also improves the typing of onSubmit and onSuccess by using a generic type rather than any.
| onSuccess?: (data: any) => void; | ||
| title?: ReactNode; | ||
| width?: string; | ||
| footer?: ReactNode; |
There was a problem hiding this comment.
I'm a little hesitant to accept any ReactNode as a footer, but I do see the need for flexibility based on your examples... can you think of something more structured that we could implement? For example to make sure buttons stay bottom right, things are properly spaced, etc.
Now that I look at it, the same would apply for title. What do you think?
There was a problem hiding this comment.
Hhhhm, I think I disagree here. Even though most modals would use buttons as the footer, that may not always be the case, and I actually have one such footer in my examples. I think using ReactNode is fine, no need to be too restrictive for UI IMO.
e3a813d to
224da4c
Compare
224da4c to
d4f1e11
Compare
|
@HenriRabalais I addressed your suggestions. Do you want to review this PR when you have time or should we assign it to someone else? |
There was a problem hiding this comment.
Thanks for making onSubmit mandatory! Going back through it though I think we've missed a case. In batchEditForm.js the onSubmit is still passed conditionally:
onSubmit={Object.keys(list).length > 1 ? handleSubmit : undefined}The list starts empty and the user scans specimens into it, so this is really "only let them submit once there are at least 2". The old Modal handled that by hiding the Save button when onSubmit was falsy. Now that FormModal always renders the button and requires onSubmit, when the modal opens with fewer than 2 specimens the Save button shows right away and clicking it does nothing (it ends up calling undefined(), which gets swallowed by the catch).
So I think there's a genuine use case here for gating submission, unless we decide we never want to prevent it. As I see it the options are:
- make
onSubmitconditional again, but if it's not passed thenFormModaldisables the Save button instead of hiding it - add a
disabledboolean prop that toggles the Save button - add a string prop for a little message/tooltip explaining why it can't be submitted yet (shown on hover, or at the bottom of the footer by default), where an empty/null value means it's submittable
- or just keep the Save button always enabled and never prevent submission
I honestly don't have a strong preference between them, they all work for me. If I was to design it, I would probably go with 3 with the message displayed in the footer. What do you think?
|
@HenriRabalais Oh good find. I checked that all modals supplied an I implemented option 2 in my last commit, I prefer that over option 3 because I am not sure what should be the ideal validation UI (a tooltip on the button? a more complete error on the appropriate fields?). IMO a more complete reactive validation UI should be left to another PR. I will note that this would have been caught by type checking if the code was typed, we need more TypeScript in LORIS! |
HenriRabalais
left a comment
There was a problem hiding this comment.
Looks great, thanks for the quick turnaround! 🙂
Description
This PR refactors the LORIS modal to make two major changes:
Architecture
Modalcomponent fromjsx/Modals.tsxis split into three components:Modal: The general non-form modal, which allows the consumer to provide the body and optionally the footer.FormModal: The form modal, which is similar to the current modal and provides form and submit logic/controls.ModalFrame: common architecture between the two modals, technicallyFormModalcould also be made to depend directly uponModalbut this would require some higher-order component machinery because the form body and content should both be wrapper in aformelement, and this wrapper is undesirable in the general case.Examples
Showcase of existing modal behavior: (added artificial delay to showcase animations)
2026-06-29.18-08-33.mp4
Showcase of new non-form modals with custom footers:
2026-06-29.18-13-40.mp4