Skip to content

Commit

Permalink
FIX #92
Browse files Browse the repository at this point in the history
  • Loading branch information
quentinovega committed Jul 18, 2022
1 parent 5ec7318 commit 692a608
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 164 deletions.
62 changes: 62 additions & 0 deletions src/basicWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import { useFormContext } from "react-hook-form";
import ReactToolTip from 'react-tooltip';
import classNames from 'classnames';
import { v4 as uuid } from 'uuid';
// @ts-ignore
import HelpCircle from 'react-feather/dist/icons/help-circle.js';
import { Informations, SchemaEntry, TFunctionalProperty } from "./form";

export const BasicWrapper = ({ entry, children, render, functionalProperty, step, informations, className }:
{
entry: object | string,
className?: string,
children: JSX.Element,
render?: ({ entry, label, error, help, children }: { entry: string, label: React.ReactNode, error: object, help: React.ReactNode, children: React.ReactNode }) => JSX.Element,
functionalProperty: TFunctionalProperty,
step?: SchemaEntry,
informations?: Informations
}) => {
const { formState } = useFormContext();

if (typeof entry === 'object') {
return children
}

// FIXME not sure it works as intended with more two or more parts
const error = entry.split('.').reduce((acc, curr) => acc && acc[curr], formState.errors)
const isDirty = entry.split('.').reduce((acc, curr) => acc && acc[curr], formState.dirtyFields)
const isTouched = entry.split('.').reduce((acc, curr) => acc && acc[curr], formState.touchedFields)
const errorDisplayed = formState.isSubmitted || isDirty || isTouched

const visibleStep = functionalProperty(entry, step?.visible === undefined ? true : step.visible, informations, error)

if (!visibleStep) {
return null;
}

const computedLabel = functionalProperty(entry, step?.label === null ? null : step?.label || entry, informations)

const id = uuid();

if (render) {
return render({ entry, label: computedLabel, error, help: step?.help, children })
}

return (
<div className={`mrf-mt_10 ${className || ""}`} style={{ position: 'relative' }}>
{computedLabel && <label className='mrf-flex mrf-ai_center mrf-mb_5' htmlFor={entry}>
<span>{computedLabel}</span>
{step?.help && <>
<ReactToolTip html={true} place={'bottom'} id={id} />
<span className='mrf-flex mrf-ai_center' data-html={true} data-tip={step?.help} data-for={id}>
<HelpCircle style={{ color: 'gray', width: 17, marginLeft: '.5rem', cursor: 'help' }} />
</span>
</>}
</label>}

{children}
{error && <div className={classNames('mrf-feedback', { ['mrf-txt_red']: !!errorDisplayed })}>{error.message}</div>}
</div>
)
}
40 changes: 23 additions & 17 deletions src/controlledInput.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import * as React from "react";
import { ChangeEvent } from 'react';
import { useController, useFormContext } from 'react-hook-form';
import { BasicWrapper } from "./basicWrapper";
import { ConditionnalSchema, Informations, SchemaEntry, SchemaRenderType } from "./form";
import { option } from './Option';
import { type } from './type';
import { isDefined, cleanHash } from './utils';

const CustomizableInput = React.memo(
(props: {
field: { rawValues?: any, value?: any, onChange?: (param: object) => void, error?: boolean, getValue: (entry: string) => any, setValue?: (key: string, data: any) => void},
field: { rawValues?: any, value?: any, onChange?: (param: object) => void, error?: boolean, getValue: (entry: string) => any, setValue?: (key: string, data: any) => void },
step: SchemaEntry,
error: any, errorDisplayed: boolean,
render?: SchemaRenderType,
children: JSX.Element,
conditionalSchema?: ConditionnalSchema,
informations?: Informations
informations?: Informations,
deactivateReactMemo: boolean
}) => {
if (props.render) {
return (
Expand All @@ -23,24 +25,26 @@ const CustomizableInput = React.memo(
}
return props.children
}, (prev, next) => {
if (next.render || next.conditionalSchema) {
return false
if (next.deactivateReactMemo) {
return false;
}
return (prev.field.value === next.field.value && next.errorDisplayed === prev.errorDisplayed && cleanHash(next.step) === cleanHash(prev.step))
return (prev.field.value === next.field.value && next.errorDisplayed === prev.errorDisplayed && cleanHash(next.step) === cleanHash(prev.step))
}
)

interface BaseProps {
step: SchemaEntry,
entry: string,
errorDisplayed?: boolean,
component?: (field: {value: any, onChange: (e: ChangeEvent<HTMLInputElement>) => void}, props: object) => JSX.Element,
component?: (field: { value: any, onChange: (e: ChangeEvent<HTMLInputElement>) => void }, props: object) => JSX.Element,
children?: JSX.Element,
informations?: Informations
informations?: Informations,
deactivateReactMemo: boolean,
inputWrapper?: (props: object) => JSX.Element,
}

interface ComponentProps extends BaseProps {
component: (field: {value: any, onChange: (e: ChangeEvent<HTMLInputElement>) => void}, props: object) => JSX.Element,
component: (field: { value: any, onChange: (e: ChangeEvent<HTMLInputElement>) => void }, props: object) => JSX.Element,
}

interface ChildrenProps extends BaseProps {
Expand All @@ -50,20 +54,20 @@ interface ChildrenProps extends BaseProps {
type Props = ComponentProps | ChildrenProps

export const ControlledInput = (inputProps: Props) => {
const { step, entry, children, component, errorDisplayed = false, informations } = inputProps;
const { step, entry, children, component, errorDisplayed = false, informations, deactivateReactMemo, inputWrapper } = inputProps;
const { field } = useController({
defaultValue: isDefined(step.defaultValue) ? step.defaultValue : null,
name: entry
})

const { getValues, setValue, formState: { errors } } = useFormContext();

const error = entry.split('.').reduce((acc, curr) => acc && acc[curr], errors)


const functionalProperty = (entry: string, prop: any) => {
if (typeof prop === 'function') {
return prop({ rawValues: getValues(), value: getValues(entry), informations, error, getValue: (key:string) => getValues(key) });
return prop({ rawValues: getValues(), value: getValues(entry), informations, error, getValue: (key: string) => getValues(key) });
} else {
return prop;
}
Expand Down Expand Up @@ -97,10 +101,12 @@ export const ControlledInput = (inputProps: Props) => {
value: field.value,
}

return <CustomizableInput
render={step.render} step={step}
field={{ setValue: (key: string, value: any) => setValue(key, value), rawValues: getValues(), getValue: (key:string) => getValues(key), ...field }}
error={error} errorDisplayed={errorDisplayed} informations={informations}>
{component ? component(field, props) : React.cloneElement(children!, { ...props })}
</CustomizableInput>
return <BasicWrapper key={`collapse-${entry}`} entry={entry} functionalProperty={functionalProperty} step={step} render={inputWrapper} informations={informations}>
<CustomizableInput
render={step.render} step={step}
field={{ setValue: (key: string, value: any) => setValue(key, value), rawValues: getValues(), getValue: (key: string) => getValues(key), ...field }}
error={error} errorDisplayed={errorDisplayed} informations={informations} deactivateReactMemo={deactivateReactMemo}>
{component ? component(field, props) : React.cloneElement(children!, { ...props })}
</CustomizableInput>
</BasicWrapper>
}
Loading

0 comments on commit 692a608

Please sign in to comment.