From e4a267d665311e992ef608774b7f83f4c5589001 Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Fri, 8 Jul 2022 17:27:21 +0200 Subject: [PATCH] fix(tailwind-formio): fix form ref events --- .../src/components/form/form.component.tsx | 2 +- .../src/components/form/form.stories.tsx | 160 +++++++++++++----- .../src/components/form/useForm.hook.ts | 65 +++---- packages/storybook/.storybook/preview.js | 1 - 4 files changed, 155 insertions(+), 73 deletions(-) diff --git a/packages/react-formio/src/components/form/form.component.tsx b/packages/react-formio/src/components/form/form.component.tsx index ec946df2..5a953db3 100755 --- a/packages/react-formio/src/components/form/form.component.tsx +++ b/packages/react-formio/src/components/form/form.component.tsx @@ -14,7 +14,7 @@ export interface FormProps extends UseFormHookProps { className?: string; } -export function Form(props: Partial) { +export function Form(props: Partial>) { const { element } = useForm(props); return
; diff --git a/packages/react-formio/src/components/form/form.stories.tsx b/packages/react-formio/src/components/form/form.stories.tsx index 6bcd3c39..46b0588a 100644 --- a/packages/react-formio/src/components/form/form.stories.tsx +++ b/packages/react-formio/src/components/form/form.stories.tsx @@ -11,21 +11,112 @@ export default { control: { type: "object" } - } - }, - parameters: {} + }, + onPrevPage: {action: "onPrevPage"}, + onNextPage: {action: "onNextPage"}, + onCancel: {action: "onCancel"}, + onChange: {action: "onChange"}, + onCustomEvent: {action: "onCustomEvent"}, + onComponentChange: {action: "onComponentChange"}, + onSubmit: {action: "onSubmit"}, + onAsyncSubmit: {action: "onAsyncSubmit"}, + onSubmitDone: {action: "onSubmitDone"}, + onFormLoad: {action: "onFormLoad"}, + onError: {action: "onError"}, + onRender: {action: "onRender"}, + onAttach: {action: "onAttach"}, + onBuild: {action: "onBuild"}, + onFocus: {action: "onFocus"}, + onBlur: {action: "onBlur"}, + onInitialized: {action: "onInitialized"}, + onFormReady: {action: "onFormReady"} + } }; +function filter(args: any[]) { + return args + .map((item) => { + if (item && (item._form)) { + return "FormioInstance"; + } + + if (item && item.component) { + return ["Component", item.component.type, item.component.key].filter(Boolean).join(":"); + } + + if (item && item.changed) { + return `${item.changed.component.key}(${item.changed.value})`; + } + + return item; + }); +} + +function wrap(args: any) { + return { + ...args, + onPrevPage: (...list: any[]) => { + return args.onPrevPage(...filter(list)); + }, + onNextPage: (...list: any[]) => { + return args.onNextPage(...filter(list)); + }, + onCancel: (...list: any[]) => { + return args.onCancel(...filter(list)); + }, + onChange: (...list: any[]) => { + return args.onChange(...filter(list)); + }, + onCustomEvent: (...list: any[]) => { + return args.onCustomEvent(...filter(list)); + }, + onComponentChange: (...list: any[]) => { + return args.onComponentChange(...filter(list)); + }, + onSubmit: (...list: any[]) => { + return args.onSubmit(...filter(list)); + }, + onAsyncSubmit: (...list: any[]) => { + return args.onAsyncSubmit(...filter(list)); + }, + onSubmitDone: (...list: any[]) => { + return args.onSubmitDone(...filter(list)); + }, + onFormLoad: (...list: any[]) => { + return args.onFormLoad(...filter(list)); + }, + onError: (...list: any[]) => { + return args.onError(...filter(list)); + }, + onRender: (...list: any[]) => { + return args.onRender(...filter(list)); + }, + onAttach: (...list: any[]) => { + return args.onAttach(...filter(list)); + }, + onBuild: (...list: any[]) => { + return args.onBuild(...filter(list)); + }, + onFocus: (...list: any[]) => { + return args.onFocus(...filter(list)); + }, + onBlur: (...list: any[]) => { + return args.onBlur(...filter(list)); + }, + onInitialized: (...list: any[]) => { + return args.onInitialized(...filter(list)); + }, + onFormReady: (...list: any[]) => { + return args.onFormReady(...filter(list)); + } + }; +} + export const Sandbox = (args: any) => { - delete args.onRender; - delete args.onComponentChange; return (
{ - console.log("ready", formio); - }} options={{template: "tailwind", iconset: "bx"}} /> ); @@ -36,34 +127,27 @@ Sandbox.args = { }; export const TriggerError = (args: any) => { - delete args.onRender; - delete args.onComponentChange; + const onAsyncSubmit = (submission: Submission) => { + return new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error("server error")); + }, 500); + }).catch((error) => { + error.errors = { + "message": "My custom message about this field", + "type": "custom", + "path": ["firstName"], + "level": "error" + }; + throw error; + }); + }; return ( - + {...wrap(args)} form={args.form} - onAsyncSubmit={(submission: Submission) => { - return new Promise((resolve, reject) => { - setTimeout(() => { - reject(new Error("server error")); - }, 500); - }).catch((error) => { - error.errors = { - "message": "My custom message about this field", - "type": "custom", - "path": ["firstName"], - "level": "error" - } - throw error - }); - }} - options={{ - hooks: { - template: "tailwind", - iconset: "bx" - } - }} + onAsyncSubmit={onAsyncSubmit} /> ); }; @@ -100,20 +184,18 @@ TriggerError.args = { export const ReadOnly = (args: any) => { - delete args.onRender; - delete args.onComponentChange; return ( ); }; diff --git a/packages/react-formio/src/components/form/useForm.hook.ts b/packages/react-formio/src/components/form/useForm.hook.ts index a4fe6804..99eb366e 100644 --- a/packages/react-formio/src/components/form/useForm.hook.ts +++ b/packages/react-formio/src/components/form/useForm.hook.ts @@ -34,6 +34,8 @@ export interface UseFormHookProps extends Record { * Data submission */ submission?: Submission; + + /// events onPrevPage?: (obj: FormPageChangeProps) => void; onNextPage?: (obj: FormPageChangeProps) => void; onCancel?: Function; @@ -41,7 +43,7 @@ export interface UseFormHookProps extends Record { onCustomEvent?: (obj: { type: string; event: string; component: ExtendedComponentSchema; data: any }) => void; onComponentChange?: (component: ExtendedComponentSchema) => void; onSubmit?: (submission: Submission) => void; - onAsyncSubmit?: (submission: Submission) => Promise; + onAsyncSubmit?: (submission: Submission) => Promise; onSubmitDone?: (submission: Submission) => void; onFormLoad?: Function; onError?: (errors: any) => void; @@ -52,7 +54,30 @@ export interface UseFormHookProps extends Record { onBlur?: Function; onInitialized?: Function; onFormReady?: (formio: Form) => void; - formioform?: any; +} + +function useDebounce(event: string, callback: any, events: Map) { + useEffect(() => { + callback && events.set(event, callLast(callback, 100)); + }, [callback, event, events]); +} + +function useEvents(funcs: any) { + const events = useRef>(new Map()); + + const hasEvent = (event: string) => { + return funcs.hasOwnProperty(event) && typeof funcs[event] === "function" + } + const emit = (event: string, ...args: any[]) => { + if (hasEvent(event)) { + const fn = events.current.has(event) ? events.current.get(event) : funcs[event] + return fn(...args); + } + }; + + useDebounce("onChange", funcs.onChange, events.current); + + return {events, emit, hasEvent}; } export function useForm(props: UseFormHookProps) { @@ -60,12 +85,12 @@ export function useForm(props: UseFormHookProps) { const element = useRef(); const isLoaded = useRef(); const instance = useRef(); - const events = useRef>(new Map()); + const {emit, hasEvent} = useEvents(funcs); async function customValidation(submission: Submission, callback: (err: Error | null) => void) { - if (events.current.has("onAsyncSubmit")) { + if (hasEvent("onAsyncSubmit")) { try { - await events.current.get("onAsyncSubmit")(submission); + await emit("onAsyncSubmit", submission, instance.current); } catch (err) { callback(err?.errors || err); } @@ -93,27 +118,15 @@ export function useForm(props: UseFormHookProps) { } if (event.startsWith("formio.")) { - const funcName = `on${event.charAt(7).toUpperCase()}${event.slice(8)}`; + const eventName = `on${event.charAt(7).toUpperCase()}${event.slice(8)}`; - if (funcName === "onChange") { + if (eventName === "onChange") { if (isEqual(get(submission, "data"), args[0].data)) { return; } } - if ( - // eslint-disable-next-line no-prototype-builtins - props.hasOwnProperty(funcName) && - typeof funcs[funcName] === "function" - ) { - if (!events.current.has(funcName)) { - const fn = callLast(funcs[funcName], 100); - events.current.set(funcName, fn); - } - - instance.current.instance.setAlert("success", ""); - events.current.get(funcName)(...args, instance.current); - } + emit(eventName, ...args, instance.current) } }); @@ -172,18 +185,6 @@ export function useForm(props: UseFormHookProps) { }; }, []); - useEffect(() => { - props.onSubmit && events.current.set("onSubmit", props.onSubmit); - }, [props.onSubmit, events]); - - useEffect(() => { - props.onAsyncSubmit && events.current.set("onAsyncSubmit", props.onAsyncSubmit); - }, [props.onAsyncSubmit, events]); - - useEffect(() => { - props.onSubmitDone && events.current.set("onSubmitDone", props.onSubmitDone); - }, [props.onSubmitDone, events]); - return { element }; diff --git a/packages/storybook/.storybook/preview.js b/packages/storybook/.storybook/preview.js index 7bf22a11..09a5a434 100644 --- a/packages/storybook/.storybook/preview.js +++ b/packages/storybook/.storybook/preview.js @@ -7,7 +7,6 @@ Formio.use(tailwind); Templates.framework = "tailwind"; export const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, controls: { matchers: { color: /(background|color)$/i,