diff --git a/inc/Editor/DataBinding/BlockBindings.php b/inc/Editor/DataBinding/BlockBindings.php
index 9e6efbd8..be706c4d 100644
--- a/inc/Editor/DataBinding/BlockBindings.php
+++ b/inc/Editor/DataBinding/BlockBindings.php
@@ -236,7 +236,7 @@ public static function get_remote_value( array $block_context, array $source_arg
return sprintf( '%s %s', $source_args['label'], $value );
}
- return $value;
+ return strval( $value );
}
public static function loop_block_render_callback( array $attributes, string $content, WP_Block $block ): string {
diff --git a/src/blocks/remote-data-container/components/InnerBlocks.tsx b/src/blocks/remote-data-container/components/InnerBlocks.tsx
index 0d8521ca..30c7e5f3 100644
--- a/src/blocks/remote-data-container/components/InnerBlocks.tsx
+++ b/src/blocks/remote-data-container/components/InnerBlocks.tsx
@@ -5,9 +5,7 @@ import { LoopTemplate } from '@/blocks/remote-data-container/components/loop-tem
interface InnerBlocksProps {
blockConfig: BlockConfig;
- getInnerBlocks: (
- result: Record< string, string >
- ) => BlockInstance< RemoteDataInnerBlockAttributes >[];
+ getInnerBlocks: ( result: RemoteDataResult ) => BlockInstance< RemoteDataInnerBlockAttributes >[];
remoteData: RemoteData;
}
diff --git a/src/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection.tsx b/src/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection.tsx
index 0c8eb3b5..83eab6a0 100644
--- a/src/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection.tsx
+++ b/src/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection.tsx
@@ -74,7 +74,8 @@ export function FieldSelectionFromAvailableBindings( props: FieldSelectionWithFi
...acc,
[ fieldName ]: {
name: binding.name,
- value: fieldValue,
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
+ value: fieldValue?.toString() ?? '',
},
};
},
diff --git a/src/blocks/remote-data-container/components/item-list/ItemList.tsx b/src/blocks/remote-data-container/components/item-list/ItemList.tsx
index 45a6abdd..a4a713fd 100644
--- a/src/blocks/remote-data-container/components/item-list/ItemList.tsx
+++ b/src/blocks/remote-data-container/components/item-list/ItemList.tsx
@@ -9,7 +9,7 @@ interface ItemListProps {
blockName: string;
loading: boolean;
onSelect: ( data: RemoteDataQueryInput ) => void;
- results?: RemoteData[ 'results' ];
+ results?: RemoteDataResult[];
searchTerms: string;
setSearchTerms: ( newValue: string ) => void;
}
@@ -31,7 +31,7 @@ export function ItemList( props: ItemListProps ) {
? item[ Object.keys( item ).find( key => /(^|_)(id)$/i.test( key ) ) as string ]
: instanceId,
}
- ) as RemoteData[ 'results' ];
+ ) as RemoteDataResult[];
}, [ results ] );
// get fields from results data to use as columns
@@ -62,7 +62,7 @@ export function ItemList( props: ItemListProps ) {
id: string;
label: string;
enableGlobalSearch: boolean;
- render?: ( { item }: { item: RemoteData[ 'results' ][ 0 ] } ) => JSX.Element;
+ render?: ( { item }: { item: RemoteDataResult } ) => JSX.Element;
enableSorting: boolean;
}[] = getFields.map( field => {
return {
@@ -71,7 +71,7 @@ export function ItemList( props: ItemListProps ) {
enableGlobalSearch: true,
render:
field === media
- ? ( { item }: { item: RemoteData[ 'results' ][ 0 ] } ) => {
+ ? ( { item }: { item: RemoteDataResult } ) => {
return (
{ __( 'Choose' ) }>,
isPrimary: true,
label: '',
- callback: ( items: RemoteData[ 'results' ] ) => {
+ callback: ( items: RemoteDataResult[] ) => {
items.map( item => onSelect( item ) );
},
},
diff --git a/src/blocks/remote-data-container/components/loop-template/LoopTemplate.tsx b/src/blocks/remote-data-container/components/loop-template/LoopTemplate.tsx
index 4e6c41d5..e6adcc14 100644
--- a/src/blocks/remote-data-container/components/loop-template/LoopTemplate.tsx
+++ b/src/blocks/remote-data-container/components/loop-template/LoopTemplate.tsx
@@ -13,9 +13,7 @@ import { LoopTemplateInnerBlocks } from '@/blocks/remote-data-container/componen
import { LoopIndexContext } from '@/blocks/remote-data-container/context/LoopIndexContext';
interface LoopTemplateProps {
- getInnerBlocks: (
- result: Record< string, string >
- ) => BlockInstance< RemoteDataInnerBlockAttributes >[];
+ getInnerBlocks: ( result: RemoteDataResult ) => BlockInstance< RemoteDataInnerBlockAttributes >[];
remoteData: RemoteData;
}
diff --git a/src/blocks/remote-data-container/hooks/usePatterns.ts b/src/blocks/remote-data-container/hooks/usePatterns.ts
index d8c6be8e..336c3414 100644
--- a/src/blocks/remote-data-container/hooks/usePatterns.ts
+++ b/src/blocks/remote-data-container/hooks/usePatterns.ts
@@ -18,7 +18,7 @@ import { getBlockConfig } from '@/utils/localized-block-data';
export function cloneBlockWithAttributes(
block: BlockInstance,
- attributes: Record< string, string >,
+ attributes: RemoteDataResult,
remoteDataBlockName: string
): BlockInstance {
const mismatchedAttributes = getMismatchedAttributes(
@@ -53,13 +53,13 @@ export function usePatterns( remoteDataBlockName: string, rootClientId: string =
const returnValue = {
defaultPattern,
getInnerBlocks: (
- result: Record< string, string >
+ result: RemoteDataResult
): BlockInstance< RemoteDataInnerBlockAttributes >[] => {
return getBlocks< RemoteDataInnerBlockAttributes >( rootClientId ).map( block =>
cloneBlockWithAttributes( block, result, remoteDataBlockName )
);
},
- getSupportedPatterns: ( result?: Record< string, string > ): BlockPattern[] => {
+ getSupportedPatterns: ( result?: RemoteDataResult ): BlockPattern[] => {
const supportedPatterns = __experimentalGetAllowedPatterns( rootClientId ).filter(
pattern =>
pattern?.blockTypes?.includes( remoteDataBlockName ) ||
diff --git a/src/utils/block-binding.ts b/src/utils/block-binding.ts
index 311977b6..9c848840 100644
--- a/src/utils/block-binding.ts
+++ b/src/utils/block-binding.ts
@@ -19,20 +19,21 @@ function getAttributeValue( attributes: unknown, key: string | undefined | null
}
function getExpectedAttributeValue(
- result?: Record< string, string >,
+ result?: Record< string, unknown >,
args?: RemoteDataBlockBindingArgs
): string | null {
if ( ! args?.field || ! result?.[ args.field ] ) {
return null;
}
- let expectedValue = result[ args.field ];
+ // See comment on toString() in getAttributeValue.
+ let expectedValue = result[ args.field ]?.toString() ?? '';
if ( args.label ) {
const labelClass = getClassName( 'block-label' );
expectedValue = `${ args.label } ${ expectedValue }`;
}
- return expectedValue ?? null;
+ return expectedValue;
}
export function getBoundAttributeEntries(
@@ -64,7 +65,7 @@ export function getBoundBlockClassName(
export function getMismatchedAttributes(
attributes: RemoteDataInnerBlockAttributes,
- results: RemoteData[ 'results' ],
+ results: RemoteDataResult[],
remoteDataBlockName: string,
index = 0
): Partial< RemoteDataInnerBlockAttributes > {
diff --git a/tests/inc/Editor/DataBinding/BlockBindingsTest.php b/tests/inc/Editor/DataBinding/BlockBindingsTest.php
index 1a67fe18..6fd4c772 100644
--- a/tests/inc/Editor/DataBinding/BlockBindingsTest.php
+++ b/tests/inc/Editor/DataBinding/BlockBindingsTest.php
@@ -334,4 +334,100 @@ public function execute( HttpQueryInterface $query, array $input_variables ): ar
'test_input_field' => 'override_value transformed',
] );
}
+
+ /**
+ * @runInSeparateProcess
+ */
+ public function test_get_remote_value(): void {
+ /**
+ * Mock the QueryRunner to return a result.
+ */
+ $mock_qr = new MockQueryRunner();
+ $mock_qr->addResult( 'output_field', 'Test Output Value' );
+
+ $block_context = [
+ 'blockName' => self::MOCK_BLOCK_NAME,
+ 'queryInput' => [
+ 'test_input_field' => 'test_value',
+ ],
+ ];
+
+ $input_schema = [
+ 'test_input_field' => [
+ 'name' => 'Test Input Field',
+ 'type' => 'string',
+ ],
+ ];
+
+ $mock_block_config = [
+ 'queries' => [
+ ConfigRegistry::DISPLAY_QUERY_KEY => MockQuery::from_array( [
+ 'input_schema' => $input_schema,
+ 'output_schema' => self::MOCK_OUTPUT_SCHEMA,
+ 'query_runner' => $mock_qr,
+ ] ),
+ ],
+ ];
+
+ $mock_config_store = Mockery::namedMock( ConfigStore::class );
+ $mock_config_store->shouldReceive( 'get_block_configuration' )
+ ->once()
+ ->with( self::MOCK_BLOCK_NAME )
+ ->andReturn( $mock_block_config );
+
+ $source_args = [
+ 'field' => self::MOCK_OUTPUT_FIELD_NAME,
+ ];
+
+ $remote_value = BlockBindings::get_remote_value( $block_context, $source_args );
+ $this->assertSame( $remote_value, self::MOCK_OUTPUT_FIELD_VALUE );
+ }
+
+ /**
+ * @runInSeparateProcess
+ */
+ public function test_get_remote_value_with_non_string(): void {
+ /**
+ * Mock the QueryRunner to return a result.
+ */
+ $mock_qr = new MockQueryRunner();
+ $mock_qr->addResult( 'output_field', 123 );
+
+ $block_context = [
+ 'blockName' => self::MOCK_BLOCK_NAME,
+ 'queryInput' => [
+ 'test_input_field' => 'test_value',
+ ],
+ ];
+
+ $input_schema = [
+ 'test_input_field' => [
+ 'name' => 'Test Input Field',
+ 'type' => 'string',
+ ],
+ ];
+
+ $mock_block_config = [
+ 'queries' => [
+ ConfigRegistry::DISPLAY_QUERY_KEY => MockQuery::from_array( [
+ 'input_schema' => $input_schema,
+ 'output_schema' => self::MOCK_OUTPUT_SCHEMA,
+ 'query_runner' => $mock_qr,
+ ] ),
+ ],
+ ];
+
+ $mock_config_store = Mockery::namedMock( ConfigStore::class );
+ $mock_config_store->shouldReceive( 'get_block_configuration' )
+ ->once()
+ ->with( self::MOCK_BLOCK_NAME )
+ ->andReturn( $mock_block_config );
+
+ $source_args = [
+ 'field' => self::MOCK_OUTPUT_FIELD_NAME,
+ ];
+
+ $remote_value = BlockBindings::get_remote_value( $block_context, $source_args );
+ $this->assertSame( $remote_value, '123' );
+ }
}
diff --git a/tests/src/utils/block-binding.test.ts b/tests/src/utils/block-binding.test.ts
index dd249182..83edd21b 100644
--- a/tests/src/utils/block-binding.test.ts
+++ b/tests/src/utils/block-binding.test.ts
@@ -127,5 +127,43 @@ describe( 'block-binding utils', () => {
content: 'Title My Title',
} );
} );
+
+ it( 'should handle mismatched types', () => {
+ const block = 'test/block';
+ const attributes: RemoteDataInnerBlockAttributes = {
+ content: '123',
+ metadata: {
+ bindings: {
+ content: { source: BLOCK_BINDING_SOURCE, args: { block, field: 'a_field' } },
+ },
+ },
+ };
+
+ const results: Record< string, unknown >[] = [ { a_field: 123 } ];
+
+ const result = getMismatchedAttributes( attributes, results, block );
+
+ expect( result ).toEqual( {} );
+ } );
+
+ it( 'should handle convert mismatched attributes to string', () => {
+ const block = 'test/block';
+ const attributes: RemoteDataInnerBlockAttributes = {
+ content: '123',
+ metadata: {
+ bindings: {
+ content: { source: BLOCK_BINDING_SOURCE, args: { block, field: 'a_field' } },
+ },
+ },
+ };
+
+ const results: Record< string, unknown >[] = [ { a_field: 1234 } ];
+
+ const result = getMismatchedAttributes( attributes, results, block );
+
+ expect( result ).toEqual( {
+ content: '1234',
+ } );
+ } );
} );
} );
diff --git a/types/remote-data.d.ts b/types/remote-data.d.ts
index f4e23d13..faa244ee 100644
--- a/types/remote-data.d.ts
+++ b/types/remote-data.d.ts
@@ -14,14 +14,17 @@ interface QueryInputOverride {
sourceType: 'query_var';
}
+type RemoteDataResult = Record< string, unknown >;
+type RemoteDataQueryInput = Record< string, unknown >;
+
interface RemoteData {
blockName: string;
isCollection: boolean;
metadata: Record< string, RemoteDataResultFields >;
- queryInput: Record< string, string >;
+ queryInput: RemoteDataQueryInput;
queryInputOverrides?: Record< string, QueryInputOverride >;
resultId: string;
- results: Record< string, string >[];
+ results: RemoteDataResult[];
}
interface RemoteDataBlockAttributes {
@@ -62,8 +65,6 @@ interface RemoteDataInnerBlockAttributes {
url?: string | RichTextData;
}
-type RemoteDataQueryInput = Record< string, string >;
-
interface RemoteDataApiRequest {
block_name: string;
query_key: string;