Back

100% Statements 20/20
100% Branches 18/18
100% Functions 5/5
100% Lines 18/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63                                    1x 18x 12x 37x     12x 12x   12x       12x                       1x 4x   4x 7x 10x 4x   10x 3x         4x 4x        
import { childBlocks } from '../child-blocks.js';
 
type BlockLike = {
	name?: string;
	clientId?: string;
	attributes?: Record< string, unknown >;
	innerBlocks?: BlockLike[];
};
 
/**
 * Determine whether a block is an input the visitor can be asked to fill in.
 *
 * Hidden fields and implicit consent checkboxes carry a `required` attribute but
 * are never something a visitor answers, so they don't count.
 *
 * @param {string} fullName - Fully qualified block name, e.g. `jetpack/field-email`.
 * @return {boolean} True when the block is a visitor-facing input.
 */
export const isInputWithRequiredField = ( fullName?: string ): boolean => {
	if ( ! fullName || ! fullName.startsWith( 'jetpack/' ) ) return false;
	const baseName = fullName.slice( 'jetpack/'.length );
	const field = childBlocks.find( block => block.name === baseName );
	// @ts-expect-error: childBlocks are defined in JS without explicit types.
	// TS is inferring the type wrong. Fix is to update childBlocks to TS with types.
	const hasRequired = !! field && field?.settings?.attributes?.required !== undefined;
	const isHidden = field?.name === 'field-hidden';
	const isImplicitConsent =
		field?.name === 'field-consent' &&
		// @ts-expect-error: childBlocks are defined in JS without explicit types.
		// TS is inferring the type wrong. Fix is to update childBlocks to TS with types.
		field?.settings?.attributes?.consentType !== 'explicit';
	return hasRequired && ! isHidden && ! isImplicitConsent;
};
 
/**
 * Collect the visitor-facing input fields anywhere inside a form.
 *
 * Recurses, because a multi-step form keeps its fields inside step blocks rather
 * than directly under the form.
 *
 * @param {Array} blocks - Blocks to search, typically a form's inner blocks.
 * @return {Array} Every input block found, in document order.
 */
export const getInputFields = ( blocks: BlockLike[] = [] ): BlockLike[] => {
	const inputFields: BlockLike[] = [];
 
	const findInputFields = ( blockList: BlockLike[] ) => {
		blockList.forEach( block => {
			if ( isInputWithRequiredField( block.name ) ) {
				inputFields.push( block );
			}
			if ( block.innerBlocks && block.innerBlocks.length > 0 ) {
				findInputFields( block.innerBlocks );
			}
		} );
	};
 
	findInputFields( blocks );
	return inputFields;
};
 
export default getInputFields;