Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ export default [
// As we improve documentation, remove directories from the ignore path
'src/components/**',
'src/contexts/**',
'src/helpers/**',
'src/partner-hook-utils/**',
'src/shared/**',
'src/types/**',
Expand All @@ -191,6 +190,7 @@ export default [
{
files: [
'src/components/Base/**/*.{ts,tsx}',
'src/components/Common/Fields/**/*.{ts,tsx}',
'src/components/Flow/**/*.{ts,tsx}',
'src/contexts/ApiProvider/**/*.{ts,tsx}',
'src/contexts/LocaleProvider/**/*.{ts,tsx}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { useField, type UseFieldProps } from '@/components/Common/Fields/hooks/u
import type { CheckboxProps } from '@/components/Common/UI/Checkbox/CheckboxTypes'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'

/** @internal */
export interface CheckboxFieldProps
extends Omit<CheckboxProps, 'name' | 'value' | 'isInvalid'>, UseFieldProps<boolean> {
FieldComponent?: ComponentType<CheckboxProps>
}

/** @internal */
export const CheckboxField: React.FC<CheckboxFieldProps> = ({
rules,
defaultValue,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/Fields/CheckboxField/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { CheckboxField, type CheckboxFieldProps } from './CheckboxField'
export { CheckboxField } from './CheckboxField'
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {

type GenericCheckboxGroupOption<TValue> = OptionWithGenericValue<TValue, CheckboxGroupOption>

/** @internal */
export interface CheckboxGroupFieldProps<TValue>
extends
Omit<CheckboxGroupProps, 'value' | 'onChange' | 'options' | 'isInvalid'>,
Expand All @@ -17,6 +18,7 @@ export interface CheckboxGroupFieldProps<TValue>
convertValueToString?: (value: TValue) => string
}

/** @internal */
export const CheckboxGroupField = <TValue = string,>({
rules,
defaultValue,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/Fields/CheckboxGroupField/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { CheckboxGroupField, type CheckboxGroupFieldProps } from './CheckboxGroupField'
export { CheckboxGroupField } from './CheckboxGroupField'
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {

type GenericComboBoxOption<TValue> = OptionWithGenericValue<TValue, ComboBoxOption>

/** @internal */
export interface ComboBoxFieldProps<TValue>
extends
Omit<
Expand All @@ -20,6 +21,7 @@ export interface ComboBoxFieldProps<TValue>
allowsCustomValue?: TValue extends string ? boolean : never
}

/** @internal */
export const ComboBoxField = <TValue = string,>({
rules,
defaultValue,
Expand Down
1 change: 0 additions & 1 deletion src/components/Common/Fields/ComboBoxField/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { ComboBoxField } from './ComboBoxField'
export type { ComboBoxFieldProps } from './ComboBoxField'
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import {

type DateFieldValue = string | Date | null

/** @internal */
export interface DatePickerFieldProps<TValue extends DateFieldValue = Date | null>
extends Omit<DatePickerProps, 'name' | 'onChange' | 'isInvalid'>, UseFieldProps<TValue> {
FieldComponent?: ComponentType<DatePickerProps>
}

/** @internal */
export const DatePickerField = <TValue extends DateFieldValue = Date | null>({
rules,
defaultValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { useField, type UseFieldProps } from '@/components/Common/Fields/hooks/u
import type { FileInputProps } from '@/components/Common/UI/FileInput/FileInputTypes'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'

/** @internal */
export interface FileInputFieldProps
extends
Omit<FileInputProps, 'name' | 'value' | 'onChange' | 'isInvalid'>,
UseFieldProps<File | null> {}

/** @internal */
export const FileInputField: React.FC<FileInputFieldProps> = ({
rules,
defaultValue,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/Fields/FileInputField/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { FileInputField, type FileInputFieldProps } from './FileInputField'
export { FileInputField } from './FileInputField'
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { useField, type UseFieldProps } from '@/components/Common/Fields/hooks/u
import type { MultiSelectComboBoxProps } from '@/components/Common/UI/MultiSelectComboBox/MultiSelectComboBoxTypes'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'

/** @internal */
export interface MultiSelectComboBoxFieldProps
extends
Omit<MultiSelectComboBoxProps, 'name' | 'value' | 'onChange' | 'isInvalid'>,
UseFieldProps<string[]> {}

/** @internal */
export const MultiSelectComboBoxField: React.FC<MultiSelectComboBoxFieldProps> = ({
rules,
defaultValue,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
export {
MultiSelectComboBoxField,
type MultiSelectComboBoxFieldProps,
} from './MultiSelectComboBoxField'
export { MultiSelectComboBoxField } from './MultiSelectComboBoxField'
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { useField, type UseFieldProps } from '@/components/Common/Fields/hooks/u
import type { NumberInputProps } from '@/components/Common/UI/NumberInput/NumberInputTypes'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'

/** @internal */
export interface NumberInputFieldProps
extends Omit<NumberInputProps, 'name' | 'value' | 'isInvalid'>, UseFieldProps<number> {
FieldComponent?: ComponentType<NumberInputProps>
}

/** @internal */
export const NumberInputField: React.FC<NumberInputFieldProps> = ({
rules: providedRules,
defaultValue,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/Fields/NumberInputField/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { NumberInputField, type NumberInputFieldProps } from './NumberInputField'
export { NumberInputField } from './NumberInputField'
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useField } from '@/components/Common/Fields/hooks/useField'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'
import { decimalToPercent, percentToDecimal } from '@/helpers/percentageConversion'

/** @internal */
export interface PercentageFieldProps extends Pick<
NumberInputProps,
'isDisabled' | 'className' | 'label' | 'isRequired' | 'description' | 'errorMessage'
Expand All @@ -20,6 +21,7 @@ function toDefaultString(value?: string | number | boolean | null): string | und
return String(value)
}

/** @internal */
export function PercentageField({
decimalValue,
decimalMin,
Expand Down
1 change: 0 additions & 1 deletion src/components/Common/Fields/PercentageField/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {

type GenericRadioGroupOption<TValue> = OptionWithGenericValue<TValue, RadioGroupOption>

/** @internal */
export interface RadioGroupFieldProps<TValue>
extends
Omit<RadioGroupProps, 'value' | 'onChange' | 'options' | 'isInvalid' | 'defaultValue'>,
Expand All @@ -21,6 +22,7 @@ export interface RadioGroupFieldProps<TValue>
FieldComponent?: ComponentType<RadioGroupProps>
}

/** @internal */
export const RadioGroupField = <TValue = string,>({
rules,
defaultValue,
Expand Down
1 change: 0 additions & 1 deletion src/components/Common/Fields/RadioGroupField/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { RadioGroupField } from './RadioGroupField'
export type { RadioGroupFieldProps } from './RadioGroupField'
2 changes: 2 additions & 0 deletions src/components/Common/Fields/SelectField/SelectField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {

type GenericSelectOption<TValue> = OptionWithGenericValue<TValue, SelectOption>

/** @internal */
export interface SelectFieldProps<TValue>
extends
Omit<SelectProps, 'name' | 'value' | 'onChange' | 'options' | 'isInvalid'>,
Expand All @@ -18,6 +19,7 @@ export interface SelectFieldProps<TValue>
FieldComponent?: ComponentType<SelectProps>
}

/** @internal */
export const SelectField = <TValue = string,>({
rules,
defaultValue,
Expand Down
1 change: 0 additions & 1 deletion src/components/Common/Fields/SelectField/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { SelectField } from './SelectField'
export type { SelectFieldProps } from './SelectField'
2 changes: 2 additions & 0 deletions src/components/Common/Fields/SwitchField/SwitchField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import type { ComponentType } from 'react'
import { useField, type UseFieldProps } from '@/components/Common/Fields/hooks/useField'
import type { SwitchProps } from '@/components/Common/UI/Switch/SwitchTypes'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'
/** @internal */
export interface SwitchFieldProps
extends Omit<SwitchProps, 'name' | 'checked' | 'onChange' | 'isInvalid'>, UseFieldProps<boolean> {
FieldComponent?: ComponentType<SwitchProps>
}

/** @internal */
export const SwitchField: React.FC<SwitchFieldProps> = ({
rules,
defaultValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { useField, type UseFieldProps } from '@/components/Common/Fields/hooks/u
import type { TextAreaProps } from '@/components/Common/UI/TextArea/TextAreaTypes'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'

/** @internal */
export interface TextAreaFieldProps
extends
Omit<TextAreaProps, 'name' | 'value' | 'isInvalid'>,
UseFieldProps<string, HTMLTextAreaElement> {}

/** @internal */
export const TextAreaField: React.FC<TextAreaFieldProps> = ({
rules,
defaultValue,
Expand Down
1 change: 0 additions & 1 deletion src/components/Common/Fields/TextAreaField/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { TextAreaField } from './TextAreaField'
export type { TextAreaFieldProps } from './TextAreaField'
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { useField, type UseFieldProps } from '@/components/Common/Fields/hooks/u
import type { TextInputProps } from '@/components/Common/UI/TextInput/TextInputTypes'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'

/** @internal */
export interface TextInputFieldProps
extends Omit<TextInputProps, 'name' | 'value' | 'isInvalid'>, UseFieldProps {
FieldComponent?: ComponentType<TextInputProps>
}

/** @internal */
export const TextInputField: React.FC<TextInputFieldProps> = ({
rules,
defaultValue,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/Fields/TextInputField/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { TextInputField, type TextInputFieldProps } from './TextInputField'
export { TextInputField } from './TextInputField'
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ interface FieldElementRegistryProviderProps {
* Publishes a `FieldElementRegistry` via context so `useField` can populate it.
* `SDKFormProvider` wires this automatically; partners who build their own form
* surface can wrap with this provider directly to opt into cross-form focus.
*
* @internal
*/
export function FieldElementRegistryProvider({
registry,
Expand Down
6 changes: 6 additions & 0 deletions src/components/Common/Fields/hooks/fieldElementRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { createContext, useContext, useRef } from 'react'
* Lives here (next to `useField`) rather than in `partner-hook-utils` so the
* established module-boundary direction (`partner-hook-utils` → `Common`) is
* preserved. Partner code consumes it via re-exports from `partner-hook-utils/form`.
*
* @internal
*/
export interface FieldElementRegistry {
register: (name: string, element: HTMLElement | null) => void
Expand Down Expand Up @@ -37,6 +39,8 @@ function createFieldElementRegistry(): FieldElementRegistry {
/**
* Creates a stable `FieldElementRegistry` scoped to the caller's lifetime.
* Intended to be called once per form hook (via `useHookFormInternals`).
*
* @internal
*/
export function useFieldElementRegistry(): FieldElementRegistry {
const ref = useRef<FieldElementRegistry | null>(null)
Expand All @@ -46,8 +50,10 @@ export function useFieldElementRegistry(): FieldElementRegistry {
return ref.current
}

/** @internal */
export const FieldElementRegistryContext = createContext<FieldElementRegistry | null>(null)

/** @internal */
export function useFieldElementRegistryContext(): FieldElementRegistry | null {
return useContext(FieldElementRegistryContext)
}
3 changes: 3 additions & 0 deletions src/components/Common/Fields/hooks/useField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { useFieldElementRegistryContext } from './fieldElementRegistry'
import { createMarkup } from '@/helpers/formattedStrings'
import { useForkRef } from '@/hooks/useForkRef/useForkRef'

/** @internal */
export type Transform<TValue> = (value: TValue) => TValue

/** @internal */
export interface UseFieldProps<TValue = string, TRef = HTMLInputElement> {
name: string
control?: Control
Expand All @@ -32,6 +34,7 @@ const processDescription = (description: React.ReactNode): React.ReactNode => {
})
}

/** @internal */
export function useField<TValue = string, TRef = HTMLInputElement>({
name,
control: controlProp,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useCallback, useMemo } from 'react'

/** @internal */
export type ConvertValueToString<TValue> = (value: TValue) => string

/** @internal */
export type OptionWithGenericValue<TValue, TOption> = Omit<TOption, 'value'> & {
value: TValue
}
Expand Down Expand Up @@ -43,6 +45,7 @@ interface UseStringifyGenericFieldValueProps<TValue, TOption> {
convertValueToString?: ConvertValueToString<TValue>
}

/** @internal */
export function useStringifyGenericFieldValue<TValue, TOption>({
options,
value,
Expand Down Expand Up @@ -81,6 +84,7 @@ interface UseStringifyGenericFieldValueArrayProps<TValue, TOption> {
convertValueToString?: ConvertValueToString<TValue>
}

/** @internal */
export function useStringifyGenericFieldValueArray<TValue, TOption>({
options,
value,
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/LRUCache.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/**
* Least-recently-used cache keyed by string with thunk values producing translation tables.
*
* @remarks Backs the i18n translation cache so loaded language bundles can be evicted in
* insertion-recency order once the configured capacity is reached. Reading or writing a key
* promotes it to the most-recently-used position.
*
* @internal
*/
export class LRUCache {
capacity: number
map: Map<string, () => Record<string, string>>
Expand Down
22 changes: 19 additions & 3 deletions src/helpers/apiErrorToList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import type { EntityErrorObject } from '@gusto/embedded-api-v-2025-11-15/models/components/entityerrorobject'
import { snakeCaseToCamelCase } from './formattedStrings'

/**Traverses errorList and finds items with message properties */
/**
* Renders each error in the list that carries a `message` as a `<span>` keyed by `errorKey`.
*
* @param errorList - Flat list of entity errors returned by the API.
* @returns React nodes for messaged entries; entries without a message yield `null`.
* @internal
*/
export const renderErrorList = (errorList: Array<EntityErrorObject>): React.ReactNode[] => {
return errorList.map(errorFromList => {
if (errorFromList.message) {
Expand All @@ -10,8 +16,18 @@ export const renderErrorList = (errorList: Array<EntityErrorObject>): React.Reac
return null
})
}
/**Recuresively parses error list and constructs an array of objects containing attribute value error messages associated with form fields. Nested errors construct '.' separated keys
* metadata.state is a special case for state taxes validation errors
/**
* Recursively flattens a nested API error tree into a list keyed for form-field display.
*
* @remarks Builds dot-separated `errorKey` paths from nested errors and converts each segment
* from snake_case to camelCase so the resulting keys match react-hook-form field names.
* `metadata.key` is used as the path segment for nested errors when present; `metadata.state`
* is a special case for state-tax validation errors, falling back to the node's own `errorKey`.
*
* @param error - The root entity error to flatten.
* @param parentKey - Accumulated dot-path prefix used during recursion. Omit at the top level.
* @returns A flat list of `{ errorKey, message, category }` entries ready to bind to form fields.
* @internal
*/
export const getFieldErrors = (
error: EntityErrorObject,
Expand Down
13 changes: 11 additions & 2 deletions src/helpers/applyMissingDefaults.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
/**
* Applies default values only to properties that are undefined in the source object.
* This is more efficient than object spread as it only sets values that are actually missing.
* Returns a copy of `rawProps` with `defaults` filled in only where the source value is `undefined`.
*
* @remarks Cheaper than `{ ...defaults, ...rawProps }` because it copies the source once and only
* writes the keys that are actually missing. Properties explicitly set to `null` or `false` are
* preserved as-is.
*
* @typeParam T - Shape of the props object.
* @param rawProps - Caller-supplied props; values set here always win.
* @param defaults - Fallback values applied only when the corresponding `rawProps` value is `undefined`.
* @returns A new object combining `rawProps` with defaults filled in for missing keys.
* @internal
*/
export function applyMissingDefaults<T>(rawProps: T, defaults: Partial<T>): T {
const result = { ...rawProps }
Expand Down
Loading
Loading