Skip to content
Merged
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
1 change: 0 additions & 1 deletion docs/ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -3289,7 +3289,6 @@ function register_google_sheets_westeros_houses_blocks(): void {
register_remote_data_block( [
'title' => 'Westeros Houses List',
'render_query' => [
'loop' => true,
'query' => $list_westeros_houses_query,
],
] );
Expand Down
1 change: 0 additions & 1 deletion docs/extending/block-registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ The human-friendly name of the block. It is also used to construct the block's n
The render query is executed when the block is rendered and fetches the data that will be provided to block bindings. It is an array with the following properties:

- `query` (required): An instance of [`QueryInterface`](./query.md) that fetches the data.
- `loop`: A boolean that indicates if the query returns a collection of data. If `true`, the block will be rendered for each item in the collection. If not provided `false` is the default.

### `selection_queries`: array (optional)

Expand Down
1 change: 0 additions & 1 deletion example/google-sheets/westeros-houses/register.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ function register_google_sheets_westeros_houses_blocks(): void {
register_remote_data_block( [
'title' => 'Westeros Houses List',
'render_query' => [
'loop' => true,
'query' => $list_westeros_houses_query,
],
] );
Expand Down
9 changes: 4 additions & 5 deletions inc/Editor/BlockManagement/BlockRegistration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use RemoteDataBlocks\Editor\Assets\Assets;
use RemoteDataBlocks\Telemetry\TracksTelemetry;
use RemoteDataBlocks\Editor\BlockPatterns\BlockPatterns;
use RemoteDataBlocks\Editor\DataBinding\BlockBindings;
use RemoteDataBlocks\REST\RemoteDataController;
use function register_block_type;

Expand Down Expand Up @@ -45,8 +44,10 @@ public static function enqueue_block_assets(): void {

public static function register_helper_blocks(): void {
// Remote data HTML block - used to render HTML content in the absence of a proper binding.
$remote_data_html_block_path = REMOTE_DATA_BLOCKS__PLUGIN_DIRECTORY . '/build/blocks/remote-html';
register_block_type( $remote_data_html_block_path );
register_block_type( REMOTE_DATA_BLOCKS__PLUGIN_DIRECTORY . '/build/blocks/remote-html' );

// Remote data template - used to render remote data collections.
register_block_type( REMOTE_DATA_BLOCKS__PLUGIN_DIRECTORY . '/build/blocks/remote-data-template' );
}

public static function register_container_blocks(): void {
Expand Down Expand Up @@ -89,7 +90,6 @@ public static function register_block_configuration( array $config ): array {
'availableBindings' => $available_bindings,
'availableOverrides' => $config['overrides'] ?? [],
'instructions' => $config['instructions'],
'loop' => $config['loop'],
'name' => $block_name,
'dataSourceType' => ConfigStore::get_data_source_type( $block_name ),
'patterns' => $config['patterns'],
Expand All @@ -103,7 +103,6 @@ public static function register_block_configuration( array $config ): array {

$block_options = [
'name' => $block_name,
'render_callback' => [ BlockBindings::class, 'remote_data_block_render_callback' ],
'title' => $config['title'],
];

Expand Down
7 changes: 4 additions & 3 deletions inc/Editor/BlockManagement/ConfigRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public static function register_block( array $user_config = [] ): bool|WP_Error

$display_query = self::inflate_query( $user_config[ self::RENDER_QUERY_KEY ]['query'] );
$input_schema = $display_query->get_input_schema();
$output_schema = $display_query->get_output_schema();
$is_collection = true === ( $output_schema['is_collection'] ?? false );

// Check if any variables are required
$has_required_variables = array_reduce(
Expand All @@ -66,7 +68,6 @@ public static function register_block( array $user_config = [] ): bool|WP_Error
'icon' => $user_config['icon'] ?? 'cloud',
'instructions' => $user_config['instructions'] ?? null,
'name' => $block_name,
'loop' => $user_config[ self::RENDER_QUERY_KEY ]['loop'] ?? false,
'overrides' => $user_config['overrides'] ?? [],
'patterns' => [],
'queries' => [
Expand All @@ -83,9 +84,9 @@ public static function register_block( array $user_config = [] ): bool|WP_Error
'type' => $input_var['type'] ?? 'string',
];
}, array_keys( $input_schema ), array_values( $input_schema ) ),
'name' => $has_required_variables ? 'Manual input' : 'Load collection',
'name' => $has_required_variables ? 'Manual input' : ( $is_collection ? 'Load collection' : 'Load item' ),
'query_key' => self::DISPLAY_QUERY_KEY,
'type' => $has_required_variables ? 'input' : 'collection',
'type' => $has_required_variables ? 'manual-input' : 'load-without-input',
],
],
'title' => $block_title,
Expand Down
59 changes: 51 additions & 8 deletions inc/Editor/DataBinding/BlockBindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,51 @@ private static function get_block_fallback_content( string $field_name, array $b
return Sanitizer::sanitize_primitive_type( 'string', $fallback_content );
}

public static function remote_data_block_render_callback( array $attributes, string $content, WP_Block $block ): string {
// This is the parent block that provides the context, so we don't have
/**
* Find a "template block" in a parsed block's inner blocks.
*
* @param array $parsed_block The parsed block.
* @return bool True if a template block was found.
*/
private static function has_template_block( array $parsed_block ): bool {
foreach ( ( $parsed_block['innerBlocks'] ?? [] ) as $inner_block ) {
if ( 'remote-data-blocks/template' === $inner_block['blockName'] ) {
return true;
}

// Recurse inner blocks.
if ( true === self::has_template_block( $inner_block ) ) {
return true;
}
}

return false;
}

public static function render_remote_data_block( array $attributes, string $content, WP_Block $block ): string {
// Look for a template block in the parsed block's inner blocks. If
// there is one, we can delegate to it for template rendering.
if ( self::has_template_block( $block->parsed_block ) ) {
return $block->render( [ 'dynamic' => false ] );
}

// Otherwise, use this block's inner blocks as the template.
return self::render_remote_data_template_block( $attributes, $content, $block );
}

public static function render_remote_data_template_block( array $attributes, string $content, WP_Block $block ): string {
// If already rendered, don't render dynamically again.
if ( isset( $block->parsed_block['dynamicallyRenderedContent'] ) ) {
return $block->parsed_block['dynamicallyRenderedContent'];
}

// If this is the parent block that *provides* the context, we won't have
// context available on the block's context property. However, context for
// children blocks comes from this block's `remoteData` attribtue (see
// block.json#providesContext), so we can access it directly.
$block_context = $attributes['remoteData'] ?? [];
$block_context = $block->context[ self::$context_name ] ?? $attributes['remoteData'] ?? [];
$block_name = $block_context['blockName'] ?? null;
$operation_name = 'remote_data_block_render_callback';
$operation_name = 'remote_data_block_render';

$query_response = self::execute_queries( $block_context, [], $operation_name );

Expand All @@ -289,7 +326,8 @@ public static function remote_data_block_render_callback( array $attributes, str

$loop_template = $block->parsed_block['innerBlocks'];
$loop_template_content = $block->parsed_block['innerContent'];


// Remove the existing blocks and content so that we can repopulate it.
$block->parsed_block['innerBlocks'] = [];
$block->parsed_block['innerContent'] = [];

Expand All @@ -311,9 +349,14 @@ public static function remote_data_block_render_callback( array $attributes, str
// Create an updated block with the new inner blocks and content.
$updated_block = new WP_Block( $block->parsed_block );

// Render the updated block but set dynamic to false so that we don't have
// recursion.
return $updated_block->render( [ 'dynamic' => false ] );
// Render the updated block but set dynamic to false so that we don't
// have recursion. Save the rendered output in a property on the
// parsed block, which will not be persisted. This is needed because
// our container block can trigger a non-dynamic re-render. This helps
// avoid descendant dynamic blocks from being rendered twice.
$block->parsed_block['dynamicallyRenderedContent'] = $updated_block->render( [ 'dynamic' => false ] );

return $block->parsed_block['dynamicallyRenderedContent'];
}

/**
Expand Down
1 change: 0 additions & 1 deletion inc/Integrations/Airtable/AirtableIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public static function register_loop_blocks_for_airtable_data_source(
[
'title' => sprintf( '%s/%s Loop', $data_source->get_display_name(), $table['name'] ),
'render_query' => [
'loop' => true,
'query' => $list_query,
],
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ function register_airtable__{{BLOCK_REG_FN_SLUG}}__block(): void {
register_remote_data_block( [
'title' => sprintf( '%s/%s Loop', $data_source->get_display_name(), '{{TABLE_NAME}}' ),
'render_query' => [
'loop' => true,
'query' => $list_query,
],
] );
Expand Down
1 change: 0 additions & 1 deletion inc/Integrations/Google/Sheets/GoogleSheetsIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ public static function register_loop_blocks_for_google_sheets_data_source(
[
'title' => sprintf( '%s/%s Loop', $data_source->get_display_name(), $sheet['name'] ),
'render_query' => [
'loop' => true,
'query' => $list_query,
],
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ function register_google_sheets__{{BLOCK_REG_FN_SLUG}}__blocks(): void {
register_remote_data_block( [
'title' => sprintf( '%s List', $block_title ),
'render_query' => [
'loop' => true,
'query' => $list_query,
],
] );
Expand Down
1 change: 0 additions & 1 deletion inc/Validation/ConfigSchemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ private static function generate_remote_data_block_config_schema(): array {
Types::instance_of( QueryInterface::class ),
Types::serialized_config_for( HttpQueryInterface::class ),
),
'loop' => Types::nullable( Types::boolean() ),
] ),
'selection_queries' => Types::nullable(
Types::list_of(
Expand Down
5 changes: 5 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
<issueHandlers>
<PossiblyUnusedReturnValue errorLevel="suppress"/>
<PossiblyUnusedMethod errorLevel="suppress"/>
<UndefinedGlobalVariable>
<errorLevel type="suppress">
<file name="**/render.php" />
</errorLevel>
</UndefinedGlobalVariable>
<UnusedClass>
<errorLevel type="suppress">
<directory name="tests/" />
Expand Down
14 changes: 11 additions & 3 deletions src/block-editor/filters/withBlockBinding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ interface BoundBlockEditProps {
setAttributes: ( attributes: RemoteDataInnerBlockAttributes ) => void;
}

// This prop is provided by the `withPreviewIndex` filter, which is bundled with
// the Remote Data Template block.
interface BlockEditWithPreviewIndex {
previewIndex?: number;
}

function BoundBlockEdit( props: BoundBlockEditProps ) {
const { attributes, availableBindings, blockName, remoteDataName, setAttributes } = props;
const existingBindings = attributes.metadata?.bindings ?? {};
Expand Down Expand Up @@ -78,9 +84,11 @@ function BoundBlockEdit( props: BoundBlockEditProps ) {
}

export const withBlockBinding = createHigherOrderComponent( BlockEdit => {
return ( props: BlockEditProps< RemoteDataInnerBlockAttributes > ) => {
const { attributes, context, name, setAttributes } = props;
const { remoteData, index } = useRemoteDataContext( context );
return (
props: BlockEditProps< RemoteDataInnerBlockAttributes > & BlockEditWithPreviewIndex
) => {
const { attributes, context, name, previewIndex: index = 0, setAttributes } = props;
const { remoteData } = useRemoteDataContext( context );
const availableBindings = getBlockAvailableBindings( remoteData?.blockName ?? '' );
const hasAvailableBindings = Boolean( Object.keys( availableBindings ).length );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ type FieldShortcodeSelectNewProps = Omit< DropdownMenuProps, 'label' > & {
export function FieldShortcodeSelectNew( props: FieldShortcodeSelectNewProps ) {
const { onSelectField, ...restProps } = props;
const blockConfigs = getBlocksConfig();
const nonLoopBlocks = Object.values( blockConfigs ).filter( ( { loop } ) => ! loop );
const blocksByType = nonLoopBlocks.reduce<
const blocksByType = Object.values( blockConfigs ).reduce<
Record< string, Array< BlocksConfig[ keyof BlocksConfig ] > >
>( ( source, blockConfig ) => {
const type = blockConfig.dataSourceType;
Expand Down
1 change: 1 addition & 0 deletions src/blocks/remote-data-container/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
"textdomain": "remote-data-blocks",
"editorScript": [ "file:./index.js", "remote-data-blocks-block-editor" ],
"editorStyle": "file:./index.css",
"render": "file:./render.php",
"style": "file:./style-index.css"
}
24 changes: 2 additions & 22 deletions src/blocks/remote-data-container/components/InnerBlocks.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
import { InnerBlocks as CoreInnerBlocks } from '@wordpress/block-editor';
import { BlockInstance } from '@wordpress/blocks';

import { LoopTemplate } from '@/blocks/remote-data-container/components/loop-template/LoopTemplate';

interface InnerBlocksProps {
blockConfig: BlockConfig;
getInnerBlocks: (
result: RemoteDataApiResult
) => BlockInstance< RemoteDataInnerBlockAttributes >[];
remoteData: RemoteData;
}

export function InnerBlocks( props: InnerBlocksProps ) {
const {
blockConfig: { loop },
getInnerBlocks,
remoteData,
} = props;

if ( loop || remoteData.results.length > 1 ) {
return <LoopTemplate getInnerBlocks={ getInnerBlocks } remoteData={ remoteData } />;
}

// This component wraps the Core InnerBlocks component to enable the renderAppender.
export function InnerBlocks() {
return <CoreInnerBlocks renderAppender={ CoreInnerBlocks.DefaultBlockAppender } />;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ButtonGroup, Button } from '@wordpress/components';
import { Button, ButtonGroup } from '@wordpress/components';

import { InputModal } from '../modals/InputModal';
import { InputPopover } from '../popovers/InputPopover';
import { DataViewsModal } from '@/blocks/remote-data-container/components/modals/DataViewsModal';
import { InputModal } from '@/blocks/remote-data-container/components/modals/InputModal';
import { InputPopover } from '@/blocks/remote-data-container/components/popovers/InputPopover';

interface ItemSelectQueryTypeProps {
blockConfig: BlockConfig;
Expand Down Expand Up @@ -38,13 +38,13 @@ export function ItemSelectQueryType( props: ItemSelectQueryTypeProps ) {
{ ...selectorProps }
/>
);
case 'collection':
case 'load-without-input':
return (
<Button key={ title } onClick={ () => onSelect( [ {} ] ) } variant="primary">
{ selector.name }
</Button>
);
case 'input':
case 'manual-input':
if ( selector.inputs.length === 1 && selector.inputs[ 0 ] ) {
return (
<InputPopover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,24 @@ import { cloud } from '@wordpress/icons';

import { ItemSelectQueryType } from '@/blocks/remote-data-container/components/placeholders/ItemSelectQueryType';

interface PlaceholderProps {
export interface PlaceholderProps {
blockConfig: BlockConfig;
onSelect: ( data: RemoteDataQueryInput[] ) => void;
onSelect: ( input: RemoteDataQueryInput[] ) => void;
}

export function Placeholder( props: PlaceholderProps ) {
const { blockConfig, onSelect } = props;
const { instructions, loop, settings } = blockConfig;
const { instructions, settings } = blockConfig;

const iconElement: IconType = ( settings.icon as IconType ) ?? cloud;

const defaultInstructions = loop
? __( 'This block displays a list of items.' )
: __( 'This block requires selection of one or more items for display.' );

return (
<PlaceholderComponent
icon={ iconElement }
label={ settings.title }
instructions={ instructions ?? defaultInstructions }
instructions={
instructions ?? __( 'This block requires selection of one or more items for display.' )
}
>
<ItemSelectQueryType blockConfig={ blockConfig } onSelect={ onSelect } />
</PlaceholderComponent>
Expand Down
3 changes: 0 additions & 3 deletions src/blocks/remote-data-container/context/LoopIndexContext.ts

This file was deleted.

15 changes: 3 additions & 12 deletions src/blocks/remote-data-container/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,8 @@ export function Edit( props: BlockEditProps< RemoteDataBlockAttributes > ) {
const blockProps = useBlockProps( { className: CONTAINER_CLASS_NAME } );
const remoteDataAttribute = migrateRemoteData( props.attributes.remoteData );

const {
getInnerBlocks,
getSupportedPatterns,
innerBlocksPattern,
insertPatternBlocks,
resetInnerBlocks,
} = usePatterns( blockName, rootClientId );
const { getSupportedPatterns, innerBlocksPattern, insertPatternBlocks, resetInnerBlocks } =
usePatterns( blockName, rootClientId );

const { data, fetch, loading, reset } = useRemoteData( {
blockName,
Expand Down Expand Up @@ -148,11 +143,7 @@ export function Edit( props: BlockEditProps< RemoteDataBlockAttributes > ) {
/>
</div>
) }
<InnerBlocks
blockConfig={ blockConfig }
getInnerBlocks={ getInnerBlocks }
remoteData={ data }
/>
<InnerBlocks />
</div>
</>
);
Expand Down
Loading