From 1d3db95a801df54d2678b031615bbd84fcbc5029 Mon Sep 17 00:00:00 2001 From: Gianmarco Manni Date: Mon, 14 Oct 2024 08:04:29 +0200 Subject: [PATCH 1/7] x --- .github/workflows/pkg.pr.new.yml | 29 +++++++++++++++++++++++++++++ src/lib/FormGroupLayout.tsx | 10 +++++----- src/lib/FormGroupLayoutLabel.tsx | 4 ++-- src/lib/Input.tsx | 9 ++++----- src/lib/InputInternal.tsx | 28 ++++++++++++++++------------ src/lib/context/FormContext.tsx | 5 +++++ src/lib/types/CommonInputProps.ts | 3 ++- 7 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/pkg.pr.new.yml diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml new file mode 100644 index 0000000..5577633 --- /dev/null +++ b/.github/workflows/pkg.pr.new.yml @@ -0,0 +1,29 @@ +name: PKG PR New +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, ready_for_review] + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - run: yarn --frozen-lockfile + + # append the git commit to the package.json version. + # We do this because some cache mechanisms (like nextjs) don't work well with the same version and ignore the changes + # until you manually delete the cache + - run: jq '.version = .version + "-" + env.GITHUB_SHA' package.json > package.json.tmp && mv package.json.tmp package.json + + - run: npx pkg-pr-new publish \ No newline at end of file diff --git a/src/lib/FormGroupLayout.tsx b/src/lib/FormGroupLayout.tsx index 2b05bbb..5659580 100644 --- a/src/lib/FormGroupLayout.tsx +++ b/src/lib/FormGroupLayout.tsx @@ -5,7 +5,7 @@ import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { CommonInputProps, MergedAddonProps } from "./types/CommonInputProps"; import "./styles/FormGroupLayout.css"; import { FormGroupLayoutLabel } from "./FormGroupLayoutLabel"; -import { useFormContext } from "./context/FormContext"; +import { useFormContextInternal } from "./context/FormContext"; interface FormGroupLayoutProps extends PropsWithChildren< @@ -36,11 +36,11 @@ const FormGroupLayout = (props: F } = props; const { name, id } = useSafeNameId(props.name, props.id); const { - formState: { errors }, - hideValidationMessages, - } = useFormContext(); + formState, + hideValidationMessages = false, + } = useFormContextInternal() ?? {}; - const fieldError = get(errors, name) as FieldError | undefined; + const fieldError = formState ? get(formState.errors, name) as FieldError | undefined : undefined; const errorMessage = String(fieldError?.message); const switchLayout = layout === "switch"; diff --git a/src/lib/FormGroupLayoutLabel.tsx b/src/lib/FormGroupLayoutLabel.tsx index f0d795d..64d9c98 100644 --- a/src/lib/FormGroupLayoutLabel.tsx +++ b/src/lib/FormGroupLayoutLabel.tsx @@ -1,5 +1,5 @@ import { ReactNode } from "react"; -import { useFormContext } from "./context/FormContext"; +import { useFormContextInternal } from "./context/FormContext"; import { FieldPath, FieldValues } from "react-hook-form"; import { Label, UncontrolledTooltip } from "reactstrap"; @@ -13,7 +13,7 @@ interface FormGroupLayoutLabelProps { const FormGroupLayoutLabel = (props: FormGroupLayoutLabelProps) => { const { label, tooltip, fieldName, layout, fieldId } = props; - const { requiredFields } = useFormContext(); + const { requiredFields = [] } = useFormContextInternal() ?? {}; if (!label && !!tooltip) { throw new Error("You can't have a tooltip without a label"); diff --git a/src/lib/Input.tsx b/src/lib/Input.tsx index a53dec7..a3011b3 100644 --- a/src/lib/Input.tsx +++ b/src/lib/Input.tsx @@ -7,12 +7,12 @@ import { FormGroupLayout } from "./FormGroupLayout"; import { InputInternal } from "./InputInternal"; import { CommonInputProps } from "./types/CommonInputProps"; import { LabelValueOption } from "./types/LabelValueOption"; -import { useFormContext } from "./context/FormContext"; +import { useFormContextInternal } from "./context/FormContext"; import { MutableRefObject } from "react"; const invalidAddonTypes = ["switch", "radio", "checkbox"]; -interface InputProps extends CommonInputProps { +interface InputProps extends CommonInputProps { type?: InputType; options?: LabelValueOption[]; multiple?: boolean; @@ -26,7 +26,7 @@ interface InputProps extends CommonInputProps { innerRef?: MutableRefObject; } -const Input = (props: InputProps) => { +const Input = (props: InputProps) => { const { type, options, addonLeft, name, addonRight, rangeMin, rangeMax, textAreaRows, multiple, id, value, disabled, step } = props; if (type === "radio" && !options) { @@ -68,8 +68,7 @@ const Input = (props: InputProps) => { })(); const { id: safeId } = useSafeNameId(name, id); - const { disabled: formDisabled } = useFormContext(); - + const { disabled: formDisabled = false } = useFormContextInternal() ?? {}; const isDisabled = formDisabled || disabled; return ( diff --git a/src/lib/InputInternal.tsx b/src/lib/InputInternal.tsx index 254a26a..1c28a1d 100644 --- a/src/lib/InputInternal.tsx +++ b/src/lib/InputInternal.tsx @@ -1,14 +1,14 @@ -import { FieldValues, get, FieldError } from "react-hook-form"; +import { FieldValues, get, FieldError, UseFormRegisterReturn } from "react-hook-form"; import { Input } from "reactstrap"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { InputProps } from "./Input"; import { useMarkOnFocusHandler } from "./hooks/useMarkOnFocusHandler"; -import { useFormContext } from "./context/FormContext"; +import { useFormContextInternal } from "./context/FormContext"; // This is two random guids concatenated. It is used to set the value of the option to undefined. const UNDEFINED_OPTION_VALUE = "CABB7A27DB754DA58C89D43ADB03FE0EC5EE3E25A6624D749F35CF2E92CFA784"; -const InputInternal = (props: InputProps) => { +const InputInternal = (props: InputProps) => { const { disabled, type, @@ -32,15 +32,15 @@ const InputInternal = (props: InputProps) => { const focusHandler = useMarkOnFocusHandler(markAllOnFocus); const { register, - formState: { errors }, - disabled: formDisabled, - } = useFormContext(); + formState, + disabled: formDisabled = false, + } = useFormContextInternal() ?? {}; - const { ref, ...rest } = register(name, { + const { ref, ...rest } =register ? register(name, { setValueAs: (value: string) => (value === UNDEFINED_OPTION_VALUE ? undefined : value), - }); + }) : {} as Partial; // probably to write better - const fieldError = get(errors, name) as FieldError | undefined; + const fieldError = formState ? get(formState.errors, name) as FieldError | undefined : undefined; const hasError = !!fieldError; return ( @@ -53,7 +53,7 @@ const InputInternal = (props: InputProps) => { if (innerRef) { innerRef.current = elem; } - ref(elem); + ref && ref(elem); }} min={rangeMin} max={rangeMax} @@ -72,7 +72,9 @@ const InputInternal = (props: InputProps) => { onBlur(e); } - await rest.onBlur(e); + if (rest?.onBlur) { + await rest.onBlur(e); + } })(); }} onChange={(e) => { @@ -81,7 +83,9 @@ const InputInternal = (props: InputProps) => { onChange(e); } - await rest.onChange(e); + if (rest?.onChange) { + await rest.onChange(e); + } })(); }} onFocus={focusHandler} diff --git a/src/lib/context/FormContext.tsx b/src/lib/context/FormContext.tsx index 626673f..c762579 100644 --- a/src/lib/context/FormContext.tsx +++ b/src/lib/context/FormContext.tsx @@ -17,3 +17,8 @@ export const useFormContext = () => { } return context as FormContextProps; }; + +export const useFormContextInternal = () => { + const context = useContext(FormContext); + return context as Partial> | undefined; +}; \ No newline at end of file diff --git a/src/lib/types/CommonInputProps.ts b/src/lib/types/CommonInputProps.ts index fd9fbad..4494749 100644 --- a/src/lib/types/CommonInputProps.ts +++ b/src/lib/types/CommonInputProps.ts @@ -7,7 +7,7 @@ interface DefaultAddonProps { export type MergedAddonProps = TRenderAddon & DefaultAddonProps; -interface CommonInputProps { +interface CommonInputProps { onChange?: (e: React.ChangeEvent) => void; onBlur?: (e: React.FocusEvent) => void; label?: ReactNode; @@ -18,6 +18,7 @@ interface CommonInputProps { labelToolTip?: string; markAllOnFocus?: boolean; inputOnly?: boolean; + defaultValue?: T extends never ? "string" : never; /** * Component prop that represents an additional className attribute From 566998ab88c874a533b78a04b6ab0b9d8e82f272 Mon Sep 17 00:00:00 2001 From: Gianmarco Manni Date: Mon, 14 Oct 2024 08:10:26 +0200 Subject: [PATCH 2/7] fix error --- src/lib/types/CommonInputProps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/types/CommonInputProps.ts b/src/lib/types/CommonInputProps.ts index 4494749..cefbba9 100644 --- a/src/lib/types/CommonInputProps.ts +++ b/src/lib/types/CommonInputProps.ts @@ -18,7 +18,7 @@ interface CommonInputProps Date: Mon, 14 Oct 2024 09:22:17 +0200 Subject: [PATCH 3/7] x --- src/lib/AsyncTypeaheadInput.tsx | 4 ++-- src/lib/DatePickerInput.tsx | 4 ++-- src/lib/FormGroupLayout.tsx | 23 ++++++++++------------- src/lib/FormGroupLayoutLabel.tsx | 13 +++++++------ src/lib/FormattedInput.tsx | 21 +++++++++++---------- src/lib/Input.tsx | 10 +++++----- src/lib/InputInternal.tsx | 6 +++--- src/lib/StaticTypeaheadInput.tsx | 4 ++-- src/lib/context/FormContext.tsx | 2 ++ src/lib/types/CommonInputProps.ts | 10 +++++++--- src/lib/types/Typeahead.ts | 2 +- 11 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index 4b23909..a62f859 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -10,7 +10,7 @@ import { useFormContext } from "./context/FormContext"; import TypeheadRef from "react-bootstrap-typeahead/types/core/Typeahead"; import { LabelValueOption } from "./types/LabelValueOption"; -interface AsyncTypeaheadProps extends CommonTypeaheadProps { +type AsyncTypeaheadProps = CommonTypeaheadProps & { queryFn: (query: string) => Promise; reactBootstrapTypeaheadProps?: Partial; } @@ -38,7 +38,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadProps(null); const { diff --git a/src/lib/DatePickerInput.tsx b/src/lib/DatePickerInput.tsx index ad5f9f9..a64c683 100644 --- a/src/lib/DatePickerInput.tsx +++ b/src/lib/DatePickerInput.tsx @@ -14,7 +14,7 @@ interface DatePickerRenderAddonProps { toggleDatePicker: () => void; } -interface DatePickerInputProps extends Omit, "onChange" | "style"> { +type DatePickerInputProps = Omit, "onChange" | "style"> & { /** * The props for the date picker component: https://reactdatepicker.com/ */ @@ -68,7 +68,7 @@ const DatePickerInput = (props: DatePickerInputProps) hideValidationMessage = false, } = props; - const { id, name } = useSafeNameId(initialName, initialId); + const { id, name } = useSafeNameId(initialName ?? "", initialId); const { control, getValues, setValue, disabled: formDisabled } = useFormContext(); const internalDatePickerRef = useRef(); const formGroupId = useRef(guidGen()); diff --git a/src/lib/FormGroupLayout.tsx b/src/lib/FormGroupLayout.tsx index 5659580..d8e1ce1 100644 --- a/src/lib/FormGroupLayout.tsx +++ b/src/lib/FormGroupLayout.tsx @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ import { PropsWithChildren, ReactNode, CSSProperties, useMemo } from "react"; import { FieldError, FieldValues, get } from "react-hook-form"; import { FormGroup, FormFeedback, FormText, InputGroup } from "reactstrap"; @@ -5,21 +6,20 @@ import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { CommonInputProps, MergedAddonProps } from "./types/CommonInputProps"; import "./styles/FormGroupLayout.css"; import { FormGroupLayoutLabel } from "./FormGroupLayoutLabel"; -import { useFormContextInternal } from "./context/FormContext"; +import { UnknownType, useFormContextInternal } from "./context/FormContext"; -interface FormGroupLayoutProps - extends PropsWithChildren< - Pick, "helpText" | "label" | "name" | "id" | "labelToolTip" | "inputOnly" | "hideValidationMessage"> - > { +type FormGroupLayoutProps = PropsWithChildren< + Pick, "helpText" | "label" | "name" | "id" | "labelToolTip" | "inputOnly" | "hideValidationMessage"> +> & { layout?: "checkbox" | "switch"; addonLeft?: ReactNode | ((props: TRenderAddon) => ReactNode); addonRight?: ReactNode | ((props: TRenderAddon) => ReactNode); addonProps?: MergedAddonProps; inputGroupStyle?: CSSProperties; formGroupId?: string; -} +}; -const FormGroupLayout = (props: FormGroupLayoutProps) => { +const FormGroupLayout = (props: FormGroupLayoutProps) => { const { label, helpText, @@ -34,13 +34,10 @@ const FormGroupLayout = (props: F addonProps, hideValidationMessage = false, } = props; - const { name, id } = useSafeNameId(props.name, props.id); - const { - formState, - hideValidationMessages = false, - } = useFormContextInternal() ?? {}; + const { name, id } = useSafeNameId(props?.name ?? "", props.id); + const { formState, hideValidationMessages = false } = useFormContextInternal() ?? {}; - const fieldError = formState ? get(formState.errors, name) as FieldError | undefined : undefined; + const fieldError = formState ? (get(formState.errors, name) as FieldError | undefined) : undefined; const errorMessage = String(fieldError?.message); const switchLayout = layout === "switch"; diff --git a/src/lib/FormGroupLayoutLabel.tsx b/src/lib/FormGroupLayoutLabel.tsx index 64d9c98..68dd20c 100644 --- a/src/lib/FormGroupLayoutLabel.tsx +++ b/src/lib/FormGroupLayoutLabel.tsx @@ -1,17 +1,18 @@ import { ReactNode } from "react"; -import { useFormContextInternal } from "./context/FormContext"; -import { FieldPath, FieldValues } from "react-hook-form"; +import { UnknownType, useFormContextInternal } from "./context/FormContext"; +import { FieldValues, Path } from "react-hook-form"; import { Label, UncontrolledTooltip } from "reactstrap"; +import { CommonInputProps } from "./types/CommonInputProps"; -interface FormGroupLayoutLabelProps { +interface FormGroupLayoutLabelProps { label: ReactNode; tooltip?: ReactNode; - fieldName: FieldPath; + fieldName: CommonInputProps["name"]; fieldId: string; layout?: "checkbox" | "switch"; } -const FormGroupLayoutLabel = (props: FormGroupLayoutLabelProps) => { +const FormGroupLayoutLabel = (props: FormGroupLayoutLabelProps) => { const { label, tooltip, fieldName, layout, fieldId } = props; const { requiredFields = [] } = useFormContextInternal() ?? {}; @@ -23,7 +24,7 @@ const FormGroupLayoutLabel = (props: FormGroupLayoutLabel return null; } - const fieldIsRequired = typeof label == "string" && requiredFields.includes(fieldName); + const fieldIsRequired = typeof label == "string" && requiredFields.includes(fieldName as Path); const finalLabel = fieldIsRequired ? `${String(label)} *` : label; const switchLayout = layout === "switch"; diff --git a/src/lib/FormattedInput.tsx b/src/lib/FormattedInput.tsx index a6f5bee..f564d0b 100644 --- a/src/lib/FormattedInput.tsx +++ b/src/lib/FormattedInput.tsx @@ -5,9 +5,9 @@ import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { FormGroupLayout } from "./FormGroupLayout"; import { CommonInputProps } from "./types/CommonInputProps"; import { useMarkOnFocusHandler } from "./hooks/useMarkOnFocusHandler"; -import { useFormContext } from "./context/FormContext"; +import { useFormContextInternal } from "./context/FormContext"; -interface FormattedInputProps extends CommonInputProps { +type FormattedInputProps = CommonInputProps & { patternFormat?: PatternFormatProps; numericFormat?: NumericFormatProps; } @@ -34,8 +34,8 @@ const FormattedInput = (props: FormattedInputProps) => className = "", hideValidationMessage = false, } = props; - const { name, id } = useSafeNameId(props.name, props.id); - const { control, disabled: formDisabled } = useFormContext(); + const { name, id } = useSafeNameId(props?.name ?? "", props.id); + const { control, disabled: formDisabled = false } = useFormContextInternal() ?? {}; const focusHandler = useMarkOnFocusHandler(markAllOnFocus); const isDisabled = formDisabled || disabled; @@ -44,18 +44,19 @@ const FormattedInput = (props: FormattedInputProps) => { + render={({ field, fieldState }) => { + const error = fieldState ? fieldState.error : undefined; const commonProps: NumericFormatProps = { name: name, // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - value: value, - getInputRef: ref, + value: field?.value, + getInputRef: field?.ref, className: classnames("form-control", { "is-invalid": error }, className), "aria-invalid": !!error, id, onBlur: (e) => { if (propsOnBlur) propsOnBlur(e); - onBlur(); + field?.onBlur(); }, disabled: isDisabled, }; @@ -85,7 +86,7 @@ const FormattedInput = (props: FormattedInputProps) => if (propsOnChange) propsOnChange(e); }} onValueChange={(values) => { - onChange(values.value); + field?.onChange(values.value); }} onFocus={focusHandler} style={style} @@ -93,7 +94,7 @@ const FormattedInput = (props: FormattedInputProps) => )} {patternFormat && ( - + )} diff --git a/src/lib/Input.tsx b/src/lib/Input.tsx index a3011b3..16d70fb 100644 --- a/src/lib/Input.tsx +++ b/src/lib/Input.tsx @@ -7,12 +7,12 @@ import { FormGroupLayout } from "./FormGroupLayout"; import { InputInternal } from "./InputInternal"; import { CommonInputProps } from "./types/CommonInputProps"; import { LabelValueOption } from "./types/LabelValueOption"; -import { useFormContextInternal } from "./context/FormContext"; +import { UnknownType, useFormContextInternal } from "./context/FormContext"; import { MutableRefObject } from "react"; const invalidAddonTypes = ["switch", "radio", "checkbox"]; -interface InputProps extends CommonInputProps { +type InputProps = CommonInputProps & { type?: InputType; options?: LabelValueOption[]; multiple?: boolean; @@ -26,7 +26,7 @@ interface InputProps extends CommonInputProps innerRef?: MutableRefObject; } -const Input = (props: InputProps) => { +const Input = (props: InputProps) => { const { type, options, addonLeft, name, addonRight, rangeMin, rangeMax, textAreaRows, multiple, id, value, disabled, step } = props; if (type === "radio" && !options) { @@ -67,7 +67,7 @@ const Input = (props: InputProps) => { return undefined; })(); - const { id: safeId } = useSafeNameId(name, id); + const { id: safeId } = useSafeNameId(name ?? "", id); const { disabled: formDisabled = false } = useFormContextInternal() ?? {}; const isDisabled = formDisabled || disabled; @@ -101,7 +101,7 @@ const Input = (props: InputProps) => { ) : ( <> - + )} diff --git a/src/lib/InputInternal.tsx b/src/lib/InputInternal.tsx index 1c28a1d..20e6043 100644 --- a/src/lib/InputInternal.tsx +++ b/src/lib/InputInternal.tsx @@ -3,12 +3,12 @@ import { Input } from "reactstrap"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { InputProps } from "./Input"; import { useMarkOnFocusHandler } from "./hooks/useMarkOnFocusHandler"; -import { useFormContextInternal } from "./context/FormContext"; +import { UnknownType, useFormContextInternal } from "./context/FormContext"; // This is two random guids concatenated. It is used to set the value of the option to undefined. const UNDEFINED_OPTION_VALUE = "CABB7A27DB754DA58C89D43ADB03FE0EC5EE3E25A6624D749F35CF2E92CFA784"; -const InputInternal = (props: InputProps) => { +const InputInternal = (props: InputProps) => { const { disabled, type, @@ -28,7 +28,7 @@ const InputInternal = (props: InputProps) => { style, innerRef, } = props; - const { name, id } = useSafeNameId(props.name, props.id); + const { name, id } = useSafeNameId(props.name ?? "", props.id); const focusHandler = useMarkOnFocusHandler(markAllOnFocus); const { register, diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index eaff897..63e5b56 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -11,7 +11,7 @@ import { useFormContext } from "./context/FormContext"; import { useRef } from "react"; import { LabelValueOption } from "./types/LabelValueOption"; -interface StaticTypeaheadInputProps extends CommonTypeaheadProps { +type StaticTypeaheadInputProps = CommonTypeaheadProps & { options: TypeaheadOptions; reactBootstrapTypeaheadProps?: Partial; } @@ -39,7 +39,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput inputRef, useGroupBy = false, } = props; - const { name, id } = useSafeNameId(props.name, props.id); + const { name, id } = useSafeNameId(props?.name ?? "", props.id); const { control, disabled: formDisabled, diff --git a/src/lib/context/FormContext.tsx b/src/lib/context/FormContext.tsx index c762579..06724f8 100644 --- a/src/lib/context/FormContext.tsx +++ b/src/lib/context/FormContext.tsx @@ -7,6 +7,8 @@ export interface FormContextProps extends UseFormReturn; + // eslint-disable-next-line @typescript-eslint/no-explicit-any export const FormContext = createContext | null>(null); diff --git a/src/lib/types/CommonInputProps.ts b/src/lib/types/CommonInputProps.ts index cefbba9..89c9e2c 100644 --- a/src/lib/types/CommonInputProps.ts +++ b/src/lib/types/CommonInputProps.ts @@ -1,5 +1,6 @@ import { ReactNode, CSSProperties } from "react"; import { FieldPath, FieldValues } from "react-hook-form"; +import { UnknownType } from "../context/FormContext"; interface DefaultAddonProps { isDisabled?: boolean; @@ -7,18 +8,18 @@ interface DefaultAddonProps { export type MergedAddonProps = TRenderAddon & DefaultAddonProps; -interface CommonInputProps { +interface CommonInputPropsInternal { onChange?: (e: React.ChangeEvent) => void; onBlur?: (e: React.FocusEvent) => void; label?: ReactNode; - name: FieldPath; + name: T extends UnknownType ? string : FieldPath; id?: string; helpText?: ReactNode; disabled?: boolean; labelToolTip?: string; markAllOnFocus?: boolean; inputOnly?: boolean; - defaultValue?: T extends never ? string : never; + defaultValue?: T extends UnknownType ? string : never; /** * Component prop that represents an additional className attribute @@ -77,4 +78,7 @@ interface CommonInputProps = T extends UnknownType + ? Omit, "name"> & { name?: string } + : CommonInputPropsInternal; export { CommonInputProps }; diff --git a/src/lib/types/Typeahead.ts b/src/lib/types/Typeahead.ts index 66fd8b6..d72e4a8 100644 --- a/src/lib/types/Typeahead.ts +++ b/src/lib/types/Typeahead.ts @@ -6,7 +6,7 @@ import TypeheadRef from "react-bootstrap-typeahead/types/core/Typeahead"; export type TypeaheadOptions = LabelValueOption[] | string[]; -interface CommonTypeaheadProps extends Omit, "onChange"> { +type CommonTypeaheadProps = Omit, "onChange"> & { multiple?: boolean; emptyLabel?: string; invalidErrorMessage?: string; From 366074bad2af2c33b011a9f42043ae04203dcbf0 Mon Sep 17 00:00:00 2001 From: Gianmarco Manni Date: Mon, 14 Oct 2024 09:27:37 +0200 Subject: [PATCH 4/7] x --- src/lib/FormattedInput.tsx | 2 ++ src/lib/InputInternal.tsx | 2 ++ src/lib/types/CommonInputProps.ts | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/FormattedInput.tsx b/src/lib/FormattedInput.tsx index f564d0b..4256da1 100644 --- a/src/lib/FormattedInput.tsx +++ b/src/lib/FormattedInput.tsx @@ -33,6 +33,7 @@ const FormattedInput = (props: FormattedInputProps) => addonRight, className = "", hideValidationMessage = false, + defaultValue } = props; const { name, id } = useSafeNameId(props?.name ?? "", props.id); const { control, disabled: formDisabled = false } = useFormContextInternal() ?? {}; @@ -89,6 +90,7 @@ const FormattedInput = (props: FormattedInputProps) => field?.onChange(values.value); }} onFocus={focusHandler} + defaultValue={defaultValue} style={style} > )} diff --git a/src/lib/InputInternal.tsx b/src/lib/InputInternal.tsx index 20e6043..2cac783 100644 --- a/src/lib/InputInternal.tsx +++ b/src/lib/InputInternal.tsx @@ -27,6 +27,7 @@ const InputInternal = (props: InputProps className, style, innerRef, + defaultValue } = props; const { name, id } = useSafeNameId(props.name ?? "", props.id); const focusHandler = useMarkOnFocusHandler(markAllOnFocus); @@ -64,6 +65,7 @@ const InputInternal = (props: InputProps style={plainText ? { color: "black", marginLeft: 10, ...style } : { ...style }} placeholder={placeholder} step={step} + defaultValue={defaultValue} {...rest} {...(value ? { value } : {})} onBlur={(e) => { diff --git a/src/lib/types/CommonInputProps.ts b/src/lib/types/CommonInputProps.ts index 89c9e2c..1fc1e57 100644 --- a/src/lib/types/CommonInputProps.ts +++ b/src/lib/types/CommonInputProps.ts @@ -19,7 +19,7 @@ interface CommonInputPropsInternal Date: Mon, 14 Oct 2024 09:46:02 +0200 Subject: [PATCH 5/7] x --- src/lib/FormattedInput.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/FormattedInput.tsx b/src/lib/FormattedInput.tsx index 4256da1..fc0e9db 100644 --- a/src/lib/FormattedInput.tsx +++ b/src/lib/FormattedInput.tsx @@ -5,14 +5,14 @@ import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { FormGroupLayout } from "./FormGroupLayout"; import { CommonInputProps } from "./types/CommonInputProps"; import { useMarkOnFocusHandler } from "./hooks/useMarkOnFocusHandler"; -import { useFormContextInternal } from "./context/FormContext"; +import { UnknownType, useFormContextInternal } from "./context/FormContext"; -type FormattedInputProps = CommonInputProps & { +type FormattedInputProps = CommonInputProps & { patternFormat?: PatternFormatProps; numericFormat?: NumericFormatProps; } -const FormattedInput = (props: FormattedInputProps) => { +const FormattedInput = (props: FormattedInputProps) => { if (props.patternFormat && props.numericFormat) { throw new Error("FormattedInput cannot have both patternFormat and numericFormat"); } From 31b3d429777504c30e022066fd49c35c3600c0ef Mon Sep 17 00:00:00 2001 From: Gianmarco Manni Date: Mon, 14 Oct 2024 10:10:58 +0200 Subject: [PATCH 6/7] x --- src/lib/FormattedInput.tsx | 159 +++++++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 59 deletions(-) diff --git a/src/lib/FormattedInput.tsx b/src/lib/FormattedInput.tsx index fc0e9db..ea828b8 100644 --- a/src/lib/FormattedInput.tsx +++ b/src/lib/FormattedInput.tsx @@ -1,5 +1,5 @@ import classnames from "classnames"; -import { Controller, FieldValues } from "react-hook-form"; +import { Controller, ControllerRenderProps, FieldValues } from "react-hook-form"; import { NumericFormat, NumericFormatProps, PatternFormat, PatternFormatProps } from "react-number-format"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { FormGroupLayout } from "./FormGroupLayout"; @@ -7,18 +7,14 @@ import { CommonInputProps } from "./types/CommonInputProps"; import { useMarkOnFocusHandler } from "./hooks/useMarkOnFocusHandler"; import { UnknownType, useFormContextInternal } from "./context/FormContext"; -type FormattedInputProps = CommonInputProps & { - patternFormat?: PatternFormatProps; - numericFormat?: NumericFormatProps; -} - -const FormattedInput = (props: FormattedInputProps) => { - if (props.patternFormat && props.numericFormat) { - throw new Error("FormattedInput cannot have both patternFormat and numericFormat"); - } - +type FormattedInputInternalProps = Omit, "name" | "disabled"> & { + name: string; + isDisabled?: boolean; + commonProps?: NumericFormatProps; + fieldOnChange?: ControllerRenderProps["onChange"]; +}; +const FormattedInputInternal = (props: FormattedInputInternalProps) => { const { - disabled, label, helpText, numericFormat, @@ -33,76 +29,121 @@ const FormattedInput = (props: FormattedInp addonRight, className = "", hideValidationMessage = false, - defaultValue + defaultValue, + name, + id, + isDisabled, + commonProps, + fieldOnChange, } = props; + + const focusHandler = useMarkOnFocusHandler(markAllOnFocus); + const commonPropsInternal = commonProps ?? { + onBlur: (e) => { + if (propsOnBlur) { + propsOnBlur(e); + } + }, + disabled: isDisabled, + className, + }; + + return ( + + <> + {numericFormat && ( + { + if (propsOnChange) propsOnChange(e); + }} + onValueChange={(values) => { + fieldOnChange && fieldOnChange(values.value); + }} + onFocus={focusHandler} + style={style} + > + )} + + {patternFormat && ( + + )} + + + ); +}; + +type FormattedInputProps = CommonInputProps & { + patternFormat?: PatternFormatProps; + numericFormat?: NumericFormatProps; +}; + +const FormattedInput = (props: FormattedInputProps) => { + if (props.patternFormat && props.numericFormat) { + throw new Error("FormattedInput cannot have both patternFormat and numericFormat"); + } + + const { + disabled, + onBlur: propsOnBlur, + className = "", + } = props; + const { name, id } = useSafeNameId(props?.name ?? "", props.id); const { control, disabled: formDisabled = false } = useFormContextInternal() ?? {}; - const focusHandler = useMarkOnFocusHandler(markAllOnFocus); const isDisabled = formDisabled || disabled; - return ( + return control ? ( { - const error = fieldState ? fieldState.error : undefined; + render={({ field: { name, onBlur, onChange, ref, value }, fieldState: { error } }) => { const commonProps: NumericFormatProps = { name: name, // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - value: field?.value, - getInputRef: field?.ref, + value: value, + getInputRef: ref, className: classnames("form-control", { "is-invalid": error }, className), "aria-invalid": !!error, id, onBlur: (e) => { if (propsOnBlur) propsOnBlur(e); - field?.onBlur(); + onBlur(); + }, + onValueChange: (values) => { + onChange(values.value); }, disabled: isDisabled, }; - return ( - - <> - {numericFormat && ( - { - if (propsOnChange) propsOnChange(e); - }} - onValueChange={(values) => { - field?.onChange(values.value); - }} - onFocus={focusHandler} - defaultValue={defaultValue} - style={style} - > - )} - - {patternFormat && ( - - )} - - - ); + return ; }} /> + ) : ( + ); }; From ab3a1761a4451e16b085ababb1996fa5d9584b35 Mon Sep 17 00:00:00 2001 From: Gianmarco Manni Date: Mon, 14 Oct 2024 10:19:09 +0200 Subject: [PATCH 7/7] x --- src/lib/FormattedInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/FormattedInput.tsx b/src/lib/FormattedInput.tsx index ea828b8..4b9ec11 100644 --- a/src/lib/FormattedInput.tsx +++ b/src/lib/FormattedInput.tsx @@ -45,7 +45,7 @@ const FormattedInputInternal = (props: Form } }, disabled: isDisabled, - className, + className: classnames("form-control", className), }; return ( @@ -110,7 +110,7 @@ const FormattedInput = (props: FormattedInp onBlur: propsOnBlur, className = "", } = props; - + const { name, id } = useSafeNameId(props?.name ?? "", props.id); const { control, disabled: formDisabled = false } = useFormContextInternal() ?? {};