fix: ensure blocks filterOptions are awaited (#13960)

Fixes #13956
This commit is contained in:
Alessio Gravili
2025-09-29 10:48:30 -07:00
committed by GitHub
parent 6d995ffb91
commit 41aa201f7b
4 changed files with 49 additions and 42 deletions

View File

@@ -205,7 +205,7 @@ export const promise = async ({
if (field.type === 'blocks' && field.filterOptions) {
// Re-run filteroptions. If the validation error is due to filteroptions, we need to add error paths to all the blocks
// that are no longer valid
const validationResult = validateBlocksFilterOptions({
const validationResult = await validateBlocksFilterOptions({
id,
data,
filterOptions: field.filterOptions,

View File

@@ -452,7 +452,7 @@ describe('Field Validations', () => {
const blocksOptions: Parameters<BlocksFieldValidation>[1] = {
...options,
}
it('basic blocks should pass validation', () => {
it('basic blocks should pass validation', async () => {
const val: any[] = [
{
blockType: 'block1',
@@ -463,12 +463,12 @@ describe('Field Validations', () => {
someField: 'some data',
},
]
const result = blocks(val, blocksOptions)
const result = await blocks(val, blocksOptions)
expect(result).toStrictEqual(true)
})
it('should respect required validation', () => {
const result1 = blocks(
it('should respect required validation', async () => {
const result1 = await blocks(
[
{
blockType: 'block1',
@@ -479,17 +479,17 @@ describe('Field Validations', () => {
)
expect(result1).toStrictEqual(true)
const result2 = blocks([], { ...blocksOptions, required: true })
const result2 = await blocks([], { ...blocksOptions, required: true })
expect(result2).not.toStrictEqual(true)
const result3 = blocks(undefined, { ...blocksOptions, required: true })
const result3 = await blocks(undefined, { ...blocksOptions, required: true })
expect(result3).not.toStrictEqual(true)
const result4 = blocks(null, { ...blocksOptions, required: true })
const result4 = await blocks(null, { ...blocksOptions, required: true })
expect(result4).not.toStrictEqual(true)
})
it('should respect minRows validation', () => {
it('should respect minRows validation', async () => {
const val: any[] = [
{
blockType: 'block1',
@@ -500,16 +500,16 @@ describe('Field Validations', () => {
someField: 'some data',
},
]
const result1 = blocks(val, { ...blocksOptions, minRows: 0 })
const result1 = await blocks(val, { ...blocksOptions, minRows: 0 })
expect(result1).toStrictEqual(true)
const result2 = blocks(val, { ...blocksOptions, minRows: 2 })
const result2 = await blocks(val, { ...blocksOptions, minRows: 2 })
expect(result2).toStrictEqual(true)
const result3 = blocks(val, { ...blocksOptions, minRows: 3 })
const result3 = await blocks(val, { ...blocksOptions, minRows: 3 })
expect(result3).not.toStrictEqual(true)
})
it('should respect maxRows validation', () => {
it('should respect maxRows validation', async () => {
const val: any[] = [
{
blockType: 'block1',
@@ -521,16 +521,16 @@ describe('Field Validations', () => {
},
]
const result1 = blocks(val, { ...blocksOptions, maxRows: 2 })
const result1 = await blocks(val, { ...blocksOptions, maxRows: 2 })
expect(result1).toStrictEqual(true)
const result2 = blocks(val, { ...blocksOptions, maxRows: 3 })
const result2 = await blocks(val, { ...blocksOptions, maxRows: 3 })
expect(result2).toStrictEqual(true)
const result3 = blocks(val, { ...blocksOptions, maxRows: 1 })
const result3 = await blocks(val, { ...blocksOptions, maxRows: 1 })
expect(result3).not.toStrictEqual(true)
})
it('should respect both minRows and maxRows validation', () => {
it('should respect both minRows and maxRows validation', async () => {
const val: any[] = [
{
blockType: 'block1',
@@ -541,20 +541,20 @@ describe('Field Validations', () => {
someField: 'some data',
},
]
const result1 = blocks(val, { ...blocksOptions, maxRows: 2, minRows: 2 })
const result1 = await blocks(val, { ...blocksOptions, maxRows: 2, minRows: 2 })
expect(result1).toStrictEqual(true)
const result2 = blocks(val, { ...blocksOptions, maxRows: 1, minRows: 4 })
const result2 = await blocks(val, { ...blocksOptions, maxRows: 1, minRows: 4 })
expect(result2).not.toStrictEqual(true)
const result3 = blocks(val, { ...blocksOptions, maxRows: 1, minRows: 0 })
const result3 = await blocks(val, { ...blocksOptions, maxRows: 1, minRows: 0 })
expect(result3).not.toStrictEqual(true)
const result4 = blocks(val, { ...blocksOptions, maxRows: 5, minRows: 3 })
const result4 = await blocks(val, { ...blocksOptions, maxRows: 5, minRows: 3 })
expect(result4).not.toStrictEqual(true)
})
it('should validate static filterOptions', () => {
it('should validate static filterOptions', async () => {
const val: any[] = [
{
blockType: 'block1',
@@ -565,23 +565,23 @@ describe('Field Validations', () => {
someField: 'some data',
},
]
const result1 = blocks(val, { ...blocksOptions, filterOptions: ['block1', 'block2'] })
const result1 = await blocks(val, { ...blocksOptions, filterOptions: ['block1', 'block2'] })
expect(result1).toStrictEqual(true)
const result2 = blocks(val, {
const result2 = await blocks(val, {
...blocksOptions,
filterOptions: ['block1', 'block2', 'block3'],
})
expect(result2).toStrictEqual(true)
const result3 = blocks(val, { ...blocksOptions, filterOptions: ['block1', 'block3'] })
const result3 = await blocks(val, { ...blocksOptions, filterOptions: ['block1', 'block3'] })
expect(result3).not.toStrictEqual(true)
const result4 = blocks(val, { ...blocksOptions, filterOptions: [] })
const result4 = await blocks(val, { ...blocksOptions, filterOptions: [] })
expect(result4).not.toStrictEqual(true)
})
it('should validate dynamic filterOptions 1', () => {
it('should validate dynamic filterOptions 1', async () => {
const val: any[] = [
{
blockType: 'block1',
@@ -592,25 +592,31 @@ describe('Field Validations', () => {
someField: 'some data',
},
]
const result1 = blocks(val, { ...blocksOptions, filterOptions: () => true })
const result1 = await blocks(val, { ...blocksOptions, filterOptions: () => true })
expect(result1).toStrictEqual(true)
const result2 = blocks(val, { ...blocksOptions, filterOptions: () => ['block1', 'block2'] })
const result2 = await blocks(val, {
...blocksOptions,
filterOptions: () => ['block1', 'block2'],
})
expect(result2).toStrictEqual(true)
const result3 = blocks(val, {
const result3 = await blocks(val, {
...blocksOptions,
filterOptions: () => ['block1', 'block2', 'block3'],
})
expect(result3).toStrictEqual(true)
const result4 = blocks(val, { ...blocksOptions, filterOptions: () => [] })
const result4 = await blocks(val, { ...blocksOptions, filterOptions: () => [] })
expect(result4).not.toStrictEqual(true)
const result5 = blocks(val, { ...blocksOptions, filterOptions: () => ['block1'] })
const result5 = await blocks(val, { ...blocksOptions, filterOptions: () => ['block1'] })
expect(result5).not.toStrictEqual(true)
const result6 = blocks(val, { ...blocksOptions, filterOptions: () => ['block1', 'block3'] })
const result6 = await blocks(val, {
...blocksOptions,
filterOptions: () => ['block1', 'block3'],
})
expect(result6).not.toStrictEqual(true)
})
})

View File

@@ -536,7 +536,7 @@ export type BlocksFieldValidation = Validate<unknown, unknown, unknown, BlocksFi
*
* @internal - this may break or be removed at any time
*/
export function validateBlocksFilterOptions({
export async function validateBlocksFilterOptions({
id,
data,
filterOptions,
@@ -546,7 +546,7 @@ export function validateBlocksFilterOptions({
}: { value: Parameters<BlocksFieldValidation>[0] } & Pick<
Parameters<BlocksFieldValidation>[1],
'data' | 'filterOptions' | 'id' | 'req' | 'siblingData'
>): {
>): Promise<{
/**
* All block slugs found in the value of the blocks field
*/
@@ -559,7 +559,7 @@ export function validateBlocksFilterOptions({
* A list of block slugs that are used despite being disallowed. If undefined, field passed validation.
*/
invalidBlockSlugs: string[] | undefined
} {
}> {
const allBlockSlugs = Array.isArray(value)
? (value as Array<{ blockType?: string }>)
.map((b) => b.blockType)
@@ -570,7 +570,7 @@ export function validateBlocksFilterOptions({
let allowedBlockSlugs: string[] | undefined = undefined
if (typeof filterOptions === 'function') {
const result = filterOptions({
const result = await filterOptions({
id: id!, // original code asserted presence
data,
req,
@@ -599,7 +599,7 @@ export function validateBlocksFilterOptions({
invalidBlockSlugs,
}
}
export const blocks: BlocksFieldValidation = (
export const blocks: BlocksFieldValidation = async (
value,
{ id, data, filterOptions, maxRows, minRows, req: { t }, req, required, siblingData },
) => {
@@ -609,7 +609,7 @@ export const blocks: BlocksFieldValidation = (
}
if (filterOptions) {
const { invalidBlockSlugs } = validateBlocksFilterOptions({
const { invalidBlockSlugs } = await validateBlocksFilterOptions({
id,
data,
filterOptions,

View File

@@ -413,10 +413,11 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
const blocksValue = Array.isArray(data[field.name]) ? data[field.name] : []
// Handle blocks filterOptions
let filterOptionsValidationResult: null | ReturnType<typeof validateBlocksFilterOptions> =
null
let filterOptionsValidationResult: Awaited<
ReturnType<typeof validateBlocksFilterOptions>
> | null = null
if (field.filterOptions) {
filterOptionsValidationResult = validateBlocksFilterOptions({
filterOptionsValidationResult = await validateBlocksFilterOptions({
id,
data: fullData,
filterOptions: field.filterOptions,