Skip to content
Open
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
60 changes: 59 additions & 1 deletion packages/form-core/src/FieldGroupApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,29 @@ export class FieldGroupApi<
return this.form.deleteField(this.getFormFieldName(field))
}

/**
* Delete all fields that belong to this field group.
*/
deleteAllFields = () => {
if (typeof this.fieldsMap === 'string') {
const subFieldsToDelete = Object.keys(this.form.fieldInfo).filter((f) => {
const fieldStr = this.fieldsMap.toString()
return f !== fieldStr && f.startsWith(fieldStr)
})

subFieldsToDelete.forEach((field) => {
this.form.deleteField(field)
})
return
}

const fieldsMap = this.fieldsMap as FieldsMap<TFormData, TFieldGroupData>

for (const key in fieldsMap) {
this.deleteField(key)
}
}

/**
* Pushes a value into an array field.
*/
Expand Down Expand Up @@ -459,6 +482,20 @@ export class FieldGroupApi<
)
}

/**
* Replaces all field values in this field group with the provided values.
*/
replaceAllFields = (fields: TFieldGroupData) => {
for (const fieldName of Object.keys(
fields as object,
) as (keyof TFieldGroupData)[]) {
this.setFieldValue(
fieldName as unknown as DeepKeys<TFieldGroupData>,
fields[fieldName] as never,
)
}
}

/**
* Removes a value from an array field at the specified index.
*/
Expand Down Expand Up @@ -514,12 +551,33 @@ export class FieldGroupApi<
}

/**
* Resets the field value and meta to default state
* Resets the field value and meta to default state.
*/
resetField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {
return this.form.resetField(this.getFormFieldName(field))
}

/**
* Resets all field values and meta within this field group.
*/
resetAllFields = () => {
if (typeof this.fieldsMap === 'string') {
const fieldsToReset = Object.keys(this.form.fieldInfo).filter((f) => {
const fieldStr = this.fieldsMap.toString()
return f !== fieldStr && f.startsWith(fieldStr)
})

fieldsToReset.forEach((f) => this.form.resetField(f))
return
}

const fieldsMap = this.fieldsMap as FieldsMap<TFormData, TFieldGroupData>

for (const key in fieldsMap) {
this.resetField(key)
}
}

validateAllFields = (cause: ValidationCause) =>
this.form.validateAllFields(cause)
}
121 changes: 121 additions & 0 deletions packages/form-core/tests/FieldGroupApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,127 @@ describe('field group api', () => {
expect(form.state.values.nested.field.name).toBeUndefined()
})

it('should deleteAllFields for string field groups', () => {
const defaultValues = {
nested: {
field: {
name: 'hello',
},
},
}

const form = new FormApi({
defaultValues,
})
form.mount()

const field = new FieldApi({
form,
name: 'nested.field.name',
})
field.mount()

const group = new FieldGroupApi({
defaultValues: { name: '' },
form,
fields: 'nested.field',
})
group.mount()

group.deleteAllFields()

expect(form.state.values.nested.field).toEqual({})
})

it('should deleteAllFields for mapped field groups', () => {
type FormVals = {
a: string
b: string
}

const defaultValues: FormVals = { a: 'A', b: 'B' }
const form = new FormApi({
defaultValues,
})
form.mount()

const group = new FieldGroupApi({
form,
fields: {
firstName: 'a',
lastName: 'b',
},
defaultValues: { firstName: '', lastName: '' },
})
group.mount()

group.deleteAllFields()

expect(form.state.values.a).toBeUndefined()
expect(form.state.values.b).toBeUndefined()
})

it('should replaceAllFields for string field groups', () => {
const defaultValues: FormValues = {
name: '',
age: 0,
people: [],
relatives: {
father: {
name: 'father',
age: 10,
},
},
}

const form = new FormApi({
defaultValues,
})
form.mount()

const group = new FieldGroupApi({
defaultValues: {} as Person,
form,
fields: 'relatives.father',
})
group.mount()

group.replaceAllFields({ name: 'New name', age: 99 })

expect(form.state.values.relatives.father).toEqual({
name: 'New name',
age: 99,
})
})

it('should replaceAllFields for mapped field groups', () => {
type FormVals = {
a: string
b: string
}

const defaultValues: FormVals = { a: 'A', b: 'B' }
const form = new FormApi({
defaultValues,
})
form.mount()

const group = new FieldGroupApi({
form,
fields: {
firstName: 'a',
lastName: 'b',
},
defaultValues: { firstName: '', lastName: '' },
})
group.mount()

group.replaceAllFields({ firstName: 'X', lastName: 'Y' })

expect(form.state.values.a).toBe('X')
expect(form.state.values.b).toBe('Y')
})

it('should forward array methods to the form', async () => {
vi.useFakeTimers()
const defaultValues = {
Expand Down
Loading