-
Notifications
You must be signed in to change notification settings - Fork 91
(feat): Enable external form Submission #550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,34 @@ | ||||||
import { useEffect } from 'react'; | ||||||
|
||||||
/** | ||||||
* useExternalSubmitListener | ||||||
* | ||||||
* A custom React hook that enables triggering form submission externally via a global custom event. | ||||||
* | ||||||
* This is particularly useful in environments where the FormEngine | ||||||
* is embedded inside another application or UI shell, and you need to trigger submission from outside | ||||||
* the form (e.g., from a toolbar button, modal footer, or iframe parent). | ||||||
* | ||||||
* Registers a global `window` event listener that listens for a specific custom event. | ||||||
* When the custom event is dispatched, the provided `submitFn` is invoked. | ||||||
* Ensures proper cleanup on unmount to avoid memory leaks or duplicate submissions. | ||||||
* | ||||||
* @param submitFn - A function that triggers the form submission. This will be called when the event is received. | ||||||
* @param eventName - (Optional) The name of the custom event to listen for. Defaults to `'triger-form-engine-submit'`. | ||||||
* | ||||||
* @example | ||||||
* // Inside your form component | ||||||
* useExternalSubmitListener(() => handleSubmit()); | ||||||
* | ||||||
* // Somewhere else (e.g., external button or shell app) | ||||||
* window.dispatchEvent(new Event('triger-form-engine-submit')); | ||||||
*/ | ||||||
export function useExternalSubmitListener(submitFn: () => void, eventName = 'triger-form-engine-submit') { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we are to make this hook more specific, I would change its signature slightly:
Suggested change
If the hook is defining a specific listener, I suggest defining the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||||||
useEffect(() => { | ||||||
const handleSubmitWrapper = () => submitFn(); | ||||||
window.addEventListener(eventName, handleSubmitWrapper); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should expect the event to provide more details about the target form session ie. provide the target form and patient UUID. This would help us map the dispatched event to the correct form instance: const { formUuid, patientUuid } = useFormContext();
const handleSubmit = (event) => {
const { formUuid: targetForm, patientUuid: targetPatient, encounterUuid /**while in edit mode **/} = event.details;
if (targetForm === formUuid && targetPatient === patientUuid) {
// invoke submission
internalSubmitHandler();
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated |
||||||
return () => { | ||||||
window.removeEventListener(eventName, handleSubmitWrapper); | ||||||
}; | ||||||
}, [submitFn, eventName]); | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { useEffect, useRef, useState } from 'react'; | ||
|
||
type Action = () => void; | ||
|
||
/** | ||
* usePostSubmissionCallback | ||
* | ||
* A custom React hook that enables deferring an action until after a form is submitted. | ||
* | ||
* This hook listens for a custom `form-submission-complete` event (dispatched on the `window`) | ||
* and allows registering a one-time callback (action) to be executed once the form has been submitted. | ||
* | ||
* If the form has **already been submitted** when the event is received, the action is executed immediately. | ||
* If the form has **not yet been submitted**, the action is stored and executed only once `setIsFormSubmitted(true)` is called. | ||
* | ||
* This is useful when you want to register a follow-up action (like redirecting or showing a notification), | ||
* but you need to ensure the form has finished submitting before it runs. | ||
* | ||
* @returns {{ | ||
* setIsFormSubmitted: (submitted: boolean) => void; | ||
* }} - Returns a setter to mark the form as submitted. | ||
* | ||
* @example | ||
* const { setIsFormSubmitted } = usePostSubmissionCallback(); | ||
* | ||
* // Later, after form submission: | ||
* setIsFormSubmitted(true); | ||
* | ||
* // Somewhere else in the app, dispatch a custom event: | ||
* window.dispatchEvent( | ||
* new CustomEvent('form-submission-complete', { | ||
* detail: { action: () => console.log('Post-submission logic executed') } | ||
* }) | ||
* ); | ||
*/ | ||
export function usePostSubmissionCallback() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we achieve the same utility from onSubmit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @samuelmale , |
||
const [isFormSubmitted, setIsFormSubmitted] = useState(false); | ||
|
||
const pendingActionRef = useRef<Action | null>(null); | ||
const hasRunRef = useRef(false); | ||
|
||
// Listen for the 'form-submission-complete' event and capture or invoke the action | ||
useEffect(() => { | ||
const handler = (event: CustomEvent) => { | ||
const action = event.detail?.action; | ||
|
||
if (typeof action === 'function') { | ||
if (isFormSubmitted && !hasRunRef.current) { | ||
hasRunRef.current = true; | ||
action(); | ||
} else { | ||
pendingActionRef.current = action; | ||
} | ||
} | ||
}; | ||
|
||
window.addEventListener('form-submission-complete', handler as EventListener); | ||
|
||
return () => { | ||
window.removeEventListener('form-submission-complete', handler as EventListener); | ||
}; | ||
}, [isFormSubmitted]); | ||
|
||
// Execute any pending action once form is marked as submitted | ||
useEffect(() => { | ||
if (isFormSubmitted && pendingActionRef.current && !hasRunRef.current) { | ||
hasRunRef.current = true; | ||
pendingActionRef.current(); | ||
pendingActionRef.current = null; | ||
} | ||
}, [isFormSubmitted]); | ||
|
||
return { setIsFormSubmitted }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would name this something like
hideControls
orhideInternalButtons
which defaults tofalse
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed to