Conversation
📝 WalkthroughWalkthroughThis pull request introduces form group submission support to the form-core package. A new Changes
Sequence DiagramsequenceDiagram
participant Caller
participant GroupApi as FormGroupApi
participant FieldApis as FieldApi Instances
participant FormApi
Caller->>GroupApi: handleSubmit()
rect rgba(100, 150, 200, 0.5)
Note over GroupApi: Initialize submission state
GroupApi->>GroupApi: Reset flags, increment attempts
GroupApi->>GroupApi: Mark related fields as touched
GroupApi->>GroupApi: Set isSubmitting = true
end
rect rgba(150, 100, 200, 0.5)
Note over GroupApi,FieldApis: Validate group fields
GroupApi->>FieldApis: Validate each field for 'submit'
FieldApis-->>GroupApi: Field validity results
alt Group fields invalid
GroupApi->>GroupApi: Set isSubmitting = false
GroupApi->>GroupApi: Invoke onGroupSubmitInvalid
GroupApi-->>Caller: Early return
end
end
rect rgba(200, 150, 100, 0.5)
Note over GroupApi,FormApi: Validate form (filtered)
GroupApi->>FormApi: validate('submit', {dontUpdateFormErrorMap, filterFieldNames})
FormApi-->>GroupApi: Form validation result
alt Form invalid for group fields
GroupApi->>GroupApi: Set isSubmitting = false
GroupApi->>GroupApi: Invoke onGroupSubmitInvalid
GroupApi-->>Caller: Early return
end
end
rect rgba(100, 200, 150, 0.5)
Note over GroupApi,FieldApis: Execute group submission
GroupApi->>FieldApis: Trigger onGroupSubmit listeners
GroupApi->>GroupApi: Invoke options.listeners.onSubmit
GroupApi->>GroupApi: Execute options.onGroupSubmit (try/catch)
alt Submission succeeds
GroupApi->>GroupApi: Set isSubmitted = true, isSubmitSuccessful = true
else Submission fails
GroupApi->>GroupApi: Set isSubmitSuccessful = false
GroupApi->>GroupApi: Set isSubmitting = false
GroupApi-->>Caller: Rethrow error
end
GroupApi->>GroupApi: Set isSubmitting = false
end
GroupApi-->>Caller: Promise resolved
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit 9edad01
☁️ Nx Cloud last updated this comment at |
🚀 Changeset Version PreviewNo changeset entries found. Merging this PR will not cause a version bump for any packages. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2128 +/- ##
==========================================
- Coverage 90.35% 90.27% -0.09%
==========================================
Files 38 50 +12
Lines 1752 2128 +376
Branches 444 552 +108
==========================================
+ Hits 1583 1921 +338
- Misses 149 187 +38
Partials 20 20 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
packages/form-core/tests/FormGroupApi.spec.ts (1)
73-76: Consider adding assertion for field error state.The test verifies callback invocations but doesn't assert that the field actually has errors after validation. Adding such an assertion would strengthen the test:
expect(step1NameField.state.meta.errors.length).toBeGreaterThan(0)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/tests/FormGroupApi.spec.ts` around lines 73 - 76, Add an assertion to verify the field's error state after validation: after invoking validation callbacks in the test, assert that step1NameField.state.meta.errors contains at least one error (e.g., use an assertion like checking length > 0) so the test not only checks callback invocation but also that FieldApi (step1NameField) actually recorded validation errors.packages/form-core/src/FormGroupApi.ts (2)
124-126: Emptymount()method may need cleanup logic later.The mount method currently does nothing and returns an empty cleanup function. As the implementation matures, consider whether mount should:
- Subscribe to form state changes
- Initialize group-specific state
- Register the group with the form for coordinated lifecycle management
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/src/FormGroupApi.ts` around lines 124 - 126, The mount() method on FormGroupApi is currently a no-op; replace it with initialization and teardown logic: when mounting the group, perform any group-specific state initialization, subscribe to the parent form's change events (or call the form's registration API such as registerGroup or similar), and return a cleanup function that unsubscribes and deregisters the group (e.g., call unregisterGroup/removeGroup) to avoid leaks; ensure you reference the FormGroupApi instance state and the form-level APIs used to subscribe/register so the returned function reverses those actions.
216-220: Placeholder values (value: 0) need to be replaced with actual group values.Multiple callback invocations use
value: 0as a placeholder. This is noted in comments but tracking for completeness. Consider using a more explicit placeholder likeundefinedor extracting the actual group value subset.Also applies to: 234-238, 275-278, 281-285
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/src/FormGroupApi.ts` around lines 216 - 220, The callback invocations currently pass a placeholder value (value: 0) instead of the actual group values; replace those placeholders in the onGroup* calls (e.g., this.options.onGroupSubmitInvalid, the similar calls at the ranges 234-238, 275-278, 281-285) with the real subset of form state for this group by extracting the group's values from this.state.values (for example build a groupValues object by selecting the group's field keys from this.state.values) and pass that object as value, or if no values exist pass undefined explicitly; ensure you reference the group field list used by this class when building the subset so the callbacks receive accurate group data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/form-core/src/FormGroupApi.ts`:
- Around line 274-285: Remove the duplicate invocation of
this.options.onGroupSubmit in FormGroupApi: delete the redundant first call and
keep a single call that passes the real submit payload (use this.state.values
instead of 0 and submitMetaArg instead of {} if those are the intended
variables). Ensure only one this.options.onGroupSubmit is called and it uses the
correct arguments (this.state.values, this.options.form, submitMetaArg).
- Around line 128-131: The startsWith check in _isFieldNamePartOfGroup
incorrectly matches names like "step10" for group "step1"; update
_isFieldNamePartOfGroup (and use this.options.name) to verify a proper boundary
after the prefix: return true only if fieldName === prefix or fieldName
startsWith(prefix + '.') or fieldName startsWith(prefix + '[') (i.e., check that
the character immediately after the prefix is absent or is a separator like '.'
or '[') so unrelated names aren't included.
- Around line 225-241: The code validates group-scoped fields via
this.options.form.validate(..., filterFieldNames: this._isFieldNamePartOfGroup)
but then checks the entire form state with this.options.form.state.isValid;
replace that check with a call to this._isFieldsValid() so the submission
decision uses the same group-scoped validation logic (keeping the done() call
and the this.options.onGroupSubmitInvalid invocation intact), i.e., use
_isFieldsValid() instead of reading form.state.isValid after the filtered
validate call.
In `@packages/form-core/tests/FormGroupApi.spec.ts`:
- Around line 49-60: The validator in the test's onSubmit returns a nested
fields object (step1: { name: { required: true } }) but
GlobalFormValidationError expects flat field paths mapping to ValidationError;
update the onSubmit return to use flat keys like 'step1.name': { required: true
} (i.e., fields: { 'step1.name': { required: true } }) so
Object.keys(fieldErrors) yields the full field path and each value is a proper
ValidationError per the GlobalFormValidationError /
Partial<Record<DeepKeys<TFormData>, ValidationError>> shape.
---
Nitpick comments:
In `@packages/form-core/src/FormGroupApi.ts`:
- Around line 124-126: The mount() method on FormGroupApi is currently a no-op;
replace it with initialization and teardown logic: when mounting the group,
perform any group-specific state initialization, subscribe to the parent form's
change events (or call the form's registration API such as registerGroup or
similar), and return a cleanup function that unsubscribes and deregisters the
group (e.g., call unregisterGroup/removeGroup) to avoid leaks; ensure you
reference the FormGroupApi instance state and the form-level APIs used to
subscribe/register so the returned function reverses those actions.
- Around line 216-220: The callback invocations currently pass a placeholder
value (value: 0) instead of the actual group values; replace those placeholders
in the onGroup* calls (e.g., this.options.onGroupSubmitInvalid, the similar
calls at the ranges 234-238, 275-278, 281-285) with the real subset of form
state for this group by extracting the group's values from this.state.values
(for example build a groupValues object by selecting the group's field keys from
this.state.values) and pass that object as value, or if no values exist pass
undefined explicitly; ensure you reference the group field list used by this
class when building the subset so the callbacks receive accurate group data.
In `@packages/form-core/tests/FormGroupApi.spec.ts`:
- Around line 73-76: Add an assertion to verify the field's error state after
validation: after invoking validation callbacks in the test, assert that
step1NameField.state.meta.errors contains at least one error (e.g., use an
assertion like checking length > 0) so the test not only checks callback
invocation but also that FieldApi (step1NameField) actually recorded validation
errors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ac7e0310-fbaa-4485-b402-aece0b9fb522
📒 Files selected for processing (5)
packages/form-core/src/FieldApi.tspackages/form-core/src/FormApi.tspackages/form-core/src/FormGroupApi.tspackages/form-core/src/index.tspackages/form-core/tests/FormGroupApi.spec.ts
| _isFieldNamePartOfGroup = (fieldName: string) => { | ||
| // TODO: Does this `startWith` capture sub-field names properly? Probably not. :( | ||
| return fieldName.startsWith(this.options.name) | ||
| } |
There was a problem hiding this comment.
startsWith check can incorrectly match unrelated fields.
If the group name is "step1", then "step10.name".startsWith("step1") returns true, incorrectly including step10 fields in the group. Add a boundary check:
_isFieldNamePartOfGroup = (fieldName: string) => {
- // TODO: Does this `startWith` capture sub-field names properly? Probably not. :(
- return fieldName.startsWith(this.options.name)
+ const name = this.options.name
+ return (
+ fieldName === name ||
+ fieldName.startsWith(`${name}.`) ||
+ fieldName.startsWith(`${name}[`)
+ )
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| _isFieldNamePartOfGroup = (fieldName: string) => { | |
| // TODO: Does this `startWith` capture sub-field names properly? Probably not. :( | |
| return fieldName.startsWith(this.options.name) | |
| } | |
| _isFieldNamePartOfGroup = (fieldName: string) => { | |
| const name = this.options.name | |
| return ( | |
| fieldName === name || | |
| fieldName.startsWith(`${name}.`) || | |
| fieldName.startsWith(`${name}[`) | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/form-core/src/FormGroupApi.ts` around lines 128 - 131, The
startsWith check in _isFieldNamePartOfGroup incorrectly matches names like
"step10" for group "step1"; update _isFieldNamePartOfGroup (and use
this.options.name) to verify a proper boundary after the prefix: return true
only if fieldName === prefix or fieldName startsWith(prefix + '.') or fieldName
startsWith(prefix + '[') (i.e., check that the character immediately after the
prefix is absent or is a separator like '.' or '[') so unrelated names aren't
included.
| await this.options.form.validate('submit', { | ||
| dontUpdateFormErrorMap: true, | ||
| filterFieldNames: this._isFieldNamePartOfGroup as never, | ||
| }) | ||
|
|
||
| // Form is invalid, do not submit | ||
| if (!this.options.form.state.isValid) { | ||
| done() | ||
|
|
||
| this.options.onGroupSubmitInvalid?.({ | ||
| value: 0 /* this.state.values */, | ||
| formApi: this.options.form, | ||
| meta: {} as never /* submitMetaArg */, | ||
| }) | ||
|
|
||
| return | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how isValid is computed in FormApi to understand dependencies
ast-grep --pattern 'isValid = $_'Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Find FormApi file
fd -t f "FormApi" --type fRepository: TanStack/form
Length of output: 468
🏁 Script executed:
# Search for how isValid is defined/computed in the codebase
rg "isValid" packages/form-core/src/ -B 2 -A 2 | head -100Repository: TanStack/form
Length of output: 6007
🏁 Script executed:
# Look at the validate method to understand dontUpdateFormErrorMap parameter
rg "dontUpdateFormErrorMap" packages/form-core/src/ -B 5 -A 5Repository: TanStack/form
Length of output: 3499
🏁 Script executed:
# Look at FormApi implementation to see how isValid is determined
cat packages/form-core/src/FormApi.ts | head -300Repository: TanStack/form
Length of output: 8428
🏁 Script executed:
# Search for where isValid state is set/computed in FormApi
rg "state.isValid|isValid:" packages/form-core/src/FormApi.ts -B 5 -A 5Repository: TanStack/form
Length of output: 945
🏁 Script executed:
# Search for the actual computation of form's isValid state
rg "isValid\s*:" packages/form-core/src/FormApi.ts -B 10 -A 2 | head -80Repository: TanStack/form
Length of output: 1027
🏁 Script executed:
# Look for getter or computed property definitions
rg "get isValid|isValid\(\)" packages/form-core/src/FormApi.ts -B 3 -A 5Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Check how field errors affect form-level isValid
rg "fieldErrors|fieldMetaBase" packages/form-core/src/FormApi.ts -B 2 -A 2 | head -100Repository: TanStack/form
Length of output: 3473
🏁 Script executed:
# Search for the FormState type definition and isValid computation
rg "interface FormState|type FormState" packages/form-core/src/FormApi.ts -A 30Repository: TanStack/form
Length of output: 1139
🏁 Script executed:
# Search for the state getter or the store structure
rg "get state|this.state|this.baseStore.state" packages/form-core/src/FormApi.ts | head -40Repository: TanStack/form
Length of output: 1867
🏁 Script executed:
# Look for where isValid is calculated from field metadata
rg "fieldMeta.*every|\.every.*isValid" packages/form-core/src/ -B 3 -A 3Repository: TanStack/form
Length of output: 803
🏁 Script executed:
# Search for how filterFieldNames is used in validate method
rg "filterFieldNames" packages/form-core/src/FormApi.ts -B 5 -A 10Repository: TanStack/form
Length of output: 1773
🏁 Script executed:
# Look at the complete validate method to see the full context
sed -n '800,1000p' packages/form-core/src/FormApi.tsRepository: TanStack/form
Length of output: 5168
🏁 Script executed:
# Look at the FormGroupApi submit method more completely
sed -n '200,300p' packages/form-core/src/FormGroupApi.tsRepository: TanStack/form
Length of output: 2723
🏁 Script executed:
# Check if there are any tests that show the expected behavior
rg "dontUpdateFormErrorMap|FilterFieldNames" packages/form-core/tests/ -B 3 -A 5Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Check the complete validation flow and understand the two checks
sed -n '180,290p' packages/form-core/src/FormGroupApi.tsRepository: TanStack/form
Length of output: 3104
🏁 Script executed:
# Look at DerivedFormState to see exactly how isValid is computed
rg "interface DerivedFormState|type DerivedFormState" packages/form-core/src/ -A 50 | head -100Repository: TanStack/form
Length of output: 3722
🏁 Script executed:
# Check if there are any other fields added to the form alongside the FormGroup
rg "FormGroup|FieldGroup" packages/form-core/tests/ -B 3 -A 10 | head -100Repository: TanStack/form
Length of output: 6601
🏁 Script executed:
# Look at the exact implementation of isValid computation
rg "isFieldsValid = fieldMetaValues.every" packages/form-core/src/FormApi.ts -B 5 -A 15Repository: TanStack/form
Length of output: 800
🏁 Script executed:
# Check how validate updates field errors and if it respects filterFieldNames
rg "allFieldsToProcess|fieldErrors\[field\]" packages/form-core/src/FormApi.ts -B 2 -A 8 | head -80Repository: TanStack/form
Length of output: 767
Use _isFieldsValid() to maintain group-scoped validation consistency.
The second validation check should use _isFieldsValid() instead of form.state.isValid. After calling form.validate('submit', { dontUpdateFormErrorMap: true, filterFieldNames: ... }) to validate only group-filtered fields, checking form.state.isValid will fail if unrelated form fields have errors. Since the first check already validates group fields with _isFieldsValid(), the second check should do the same for consistency:
Suggested change
-if (!this.options.form.state.isValid) {
+if (!this._isFieldsValid()) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await this.options.form.validate('submit', { | |
| dontUpdateFormErrorMap: true, | |
| filterFieldNames: this._isFieldNamePartOfGroup as never, | |
| }) | |
| // Form is invalid, do not submit | |
| if (!this.options.form.state.isValid) { | |
| done() | |
| this.options.onGroupSubmitInvalid?.({ | |
| value: 0 /* this.state.values */, | |
| formApi: this.options.form, | |
| meta: {} as never /* submitMetaArg */, | |
| }) | |
| return | |
| } | |
| await this.options.form.validate('submit', { | |
| dontUpdateFormErrorMap: true, | |
| filterFieldNames: this._isFieldNamePartOfGroup as never, | |
| }) | |
| // Form is invalid, do not submit | |
| if (!this._isFieldsValid()) { | |
| done() | |
| this.options.onGroupSubmitInvalid?.({ | |
| value: 0 /* this.state.values */, | |
| formApi: this.options.form, | |
| meta: {} as never /* submitMetaArg */, | |
| }) | |
| return | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/form-core/src/FormGroupApi.ts` around lines 225 - 241, The code
validates group-scoped fields via this.options.form.validate(...,
filterFieldNames: this._isFieldNamePartOfGroup) but then checks the entire form
state with this.options.form.state.isValid; replace that check with a call to
this._isFieldsValid() so the submission decision uses the same group-scoped
validation logic (keeping the done() call and the
this.options.onGroupSubmitInvalid invocation intact), i.e., use _isFieldsValid()
instead of reading form.state.isValid after the filtered validate call.
| await this.options.onGroupSubmit?.({ | ||
| value: 0, | ||
| formApi: this.options.form, | ||
| meta: {}, | ||
| }) | ||
|
|
||
| // Run the submit code | ||
| await this.options.onGroupSubmit?.({ | ||
| value: 0, // this.state.values, | ||
| formApi: this.options.form, | ||
| meta: {}, // submitMetaArg, | ||
| }) |
There was a problem hiding this comment.
Duplicate onGroupSubmit invocation.
onGroupSubmit is called twice in succession (lines 274-278 and 281-285). This appears to be a copy-paste error. Remove one of the calls:
try {
- await this.options.onGroupSubmit?.({
- value: 0,
- formApi: this.options.form,
- meta: {},
- })
-
// Run the submit code
await this.options.onGroupSubmit?.({
value: 0, // this.state.values,
formApi: this.options.form,
meta: {}, // submitMetaArg,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await this.options.onGroupSubmit?.({ | |
| value: 0, | |
| formApi: this.options.form, | |
| meta: {}, | |
| }) | |
| // Run the submit code | |
| await this.options.onGroupSubmit?.({ | |
| value: 0, // this.state.values, | |
| formApi: this.options.form, | |
| meta: {}, // submitMetaArg, | |
| }) | |
| // Run the submit code | |
| await this.options.onGroupSubmit?.({ | |
| value: 0, // this.state.values, | |
| formApi: this.options.form, | |
| meta: {}, // submitMetaArg, | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/form-core/src/FormGroupApi.ts` around lines 274 - 285, Remove the
duplicate invocation of this.options.onGroupSubmit in FormGroupApi: delete the
redundant first call and keep a single call that passes the real submit payload
(use this.state.values instead of 0 and submitMetaArg instead of {} if those are
the intended variables). Ensure only one this.options.onGroupSubmit is called
and it uses the correct arguments (this.state.values, this.options.form,
submitMetaArg).
| onSubmit: () => { | ||
| return { | ||
| fields: { | ||
| step1: { | ||
| name: { | ||
| required: true, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| }, | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify how field errors are structured in existing tests
rg -n "fields:" packages/form-core/tests/ -A 5 --type=ts | head -60Repository: TanStack/form
Length of output: 4099
🏁 Script executed:
# Find and examine the normalizeError function in FormApi.ts
rg -n "normalizeError" packages/form-core/src/ -A 10 --type=tsRepository: TanStack/form
Length of output: 6024
🏁 Script executed:
# Check the full test context to understand what assertions are made
sed -n '39,87p' packages/form-core/tests/FormGroupApi.spec.tsRepository: TanStack/form
Length of output: 1118
🏁 Script executed:
# Check if nested object error format is used elsewhere in tests
rg -n "required: true" packages/form-core/tests/ --type=tsRepository: TanStack/form
Length of output: 307
🏁 Script executed:
# Check how fieldErrors are processed and matched to fields
rg -n "fieldErrors" packages/form-core/src/FormApi.ts -B 2 -A 3 | head -80Repository: TanStack/form
Length of output: 2834
🏁 Script executed:
# Look at FormGroupApi to see how it handles field errors
cat packages/form-core/src/FormGroupApi.ts | head -100Repository: TanStack/form
Length of output: 2550
🏁 Script executed:
# Check field error assignment logic
rg -n "setFieldMeta\|errorMap" packages/form-core/src/FormApi.ts -B 1 -A 2 | head -50Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Get the full test and see what field is being checked
sed -n '39,90p' packages/form-core/tests/FormGroupApi.spec.tsRepository: TanStack/form
Length of output: 1269
🏁 Script executed:
# Check if step1NameField.state.errors is asserted
rg -n "step1NameField" packages/form-core/tests/FormGroupApi.spec.ts -A 2 -B 1 | head -30Repository: TanStack/form
Length of output: 655
🏁 Script executed:
# Check if there are any assertions on step1NameField.state.meta or errors
rg -n "step1NameField\." packages/form-core/tests/FormGroupApi.spec.ts | head -20Repository: TanStack/form
Length of output: 147
🏁 Script executed:
# Check the type definition of ValidationError to understand what structure is expected
rg -n "type ValidationError" packages/form-core/src/ -A 3Repository: TanStack/form
Length of output: 1049
🏁 Script executed:
# Check how the error is actually processed - look for any recursive processing of nested errors
rg -n "determineFormLevelErrorSourceAndValue" packages/form-core/src/FormApi.ts -A 20 | head -60Repository: TanStack/form
Length of output: 2709
🏁 Script executed:
# Also check if there's any flattening of nested error structures
rg -n "flatten\|flat\|nested" packages/form-core/src/FormApi.ts | head -20Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Check the GlobalFormValidationError type definition
rg -n "GlobalFormValidationError\|isGlobalFormValidationError" packages/form-core/src/ -B 2 -A 5 | head -80Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# See real usage examples in other validators
rg -n "return {" packages/form-core/tests/ -A 8 --type=ts | grep -A 8 "fields:" | head -50Repository: TanStack/form
Length of output: 136
🏁 Script executed:
# Check the GlobalFormValidationError type definition
rg -n "GlobalFormValidationError" packages/form-core/src/utils.ts -B 2 -A 8Repository: TanStack/form
Length of output: 732
🏁 Script executed:
# Search for validator return examples more carefully
rg -n "fields:" packages/form-core/tests/FormGroupApi.spec.ts -B 2 -A 4Repository: TanStack/form
Length of output: 441
🏁 Script executed:
# Check the GlobalFormValidationError type definition in types.ts
rg -n "GlobalFormValidationError" packages/form-core/src/types.ts -B 2 -A 8Repository: TanStack/form
Length of output: 1352
🏁 Script executed:
# Check what the determineFormLevelErrorSourceAndValue function does
rg -n "export.*determineFormLevelErrorSourceAndValue" packages/form-core/src/utils.ts -A 30Repository: TanStack/form
Length of output: 1166
🏁 Script executed:
# Let me verify one more thing - check if field name iteration includes both nested and flat keys
sed -n '1720,1750p' packages/form-core/src/FormApi.tsRepository: TanStack/form
Length of output: 1083
🏁 Script executed:
# Also check test file to see if there are assertions on field.state properties anywhere
rg -n "expect.*step1NameField" packages/form-core/tests/FormGroupApi.spec.tsRepository: TanStack/form
Length of output: 39
Fix the validator error format to use flat field paths with proper ValidationError values.
The validator returns { fields: { step1: { name: { required: true } } } }, but GlobalFormValidationError expects field keys to be flat paths matching field names:
fields: Partial<Record<DeepKeys<TFormData>, ValidationError>>With the current nested structure, the error for step1.name is not properly mapped because:
Object.keys(fieldErrors)yields['step1']instead of['step1.name']- When checking
fieldErrors['step1.name'], it returnsundefined(error is lost) - The field receives the nested object
{ name: { required: true } }instead of a properValidationError
Correct the validator to return:
validators: {
onSubmit: () => {
return {
fields: {
- step1: {
- name: {
- required: true,
- },
- },
+ 'step1.name': 'Name is required',
},
}
},
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/form-core/tests/FormGroupApi.spec.ts` around lines 49 - 60, The
validator in the test's onSubmit returns a nested fields object (step1: { name:
{ required: true } }) but GlobalFormValidationError expects flat field paths
mapping to ValidationError; update the onSubmit return to use flat keys like
'step1.name': { required: true } (i.e., fields: { 'step1.name': { required: true
} }) so Object.keys(fieldErrors) yields the full field path and each value is a
proper ValidationError per the GlobalFormValidationError /
Partial<Record<DeepKeys<TFormData>, ValidationError>> shape.
|
Note to self: We need to extend |
|
Another note to self: Work on the validators for To ensure that this PR is clean enough to review by other maintainers. Will merge these two branches when I'm more confident about that code. |
This PR implements #419 as outlined in the issue.
Core TODOs
corepackage integration testscorepackage type testsFramework support
reactadapter base codereactadapter integration testsreactadapter type testsreactadapter examplesreactadapter docsangularadapter base codeangularadapter integration testsangularadapter type testsangularadapter examplesangularadapter docslitadapter base codelitadapter integration testslitadapter type testslitadapter exampleslitadapter docssolidadapter base codesolidadapter integration testssolidadapter type testssolidadapter examplessolidadapter docssvelteadapter base codesvelteadapter integration testssvelteadapter type testssvelteadapter examplessvelteadapter docsvueadapter base codevueadapter integration testsvueadapter type testsvueadapter examplesvueadapter docsSummary by CodeRabbit
Release Notes
New Features
Tests