Skip to content

Commit

Permalink
Query block: client-side pagination (WordPress#53812)
Browse files Browse the repository at this point in the history
* Add router with region-based client-side navigation

* Add changelog

* Initial version working

* Only add supports.interactivity and enqueue the view.js file if the "Enhanced Pagination" option is enabled

* Interactivity API: merge new server-side rendered context on client-side navigation (WordPress#53853)

* Add failing test

* Fix the test

* Add changelog

* Fix lint error

* Fix changelog placement

* Interactivity API: Support for the `data-wp-key` directive (WordPress#53844)

* Add failing test

* Fix test using key

* Replace key with data-wp-key

* Refactor test a bit

* Add changelog

* Add docs

* Remove unnecessary paragraph

* Fix lint error

* Interactivity API: Fix non stable context reference on client side navigation (WordPress#53876)

* Add failing test

* Fix the test

* Add basic accessibility (announcement + focus)

* Add basic loading animation

Co-authored-by: David Arenas <[email protected]>

* Only enqueue styles if they are needed

* Add missing CSS props on aria-live element

Apparently, some screen readers don't read elements that don't have width and height.

* Fix two typos

* Update fixture

* Simplify loaded text

* Move navigation announce style to a class

---------

Co-authored-by: David Arenas <[email protected]>
Co-authored-by: David Arenas <[email protected]>
  • Loading branch information
3 people authored Aug 29, 2023
1 parent 2436169 commit abfe3c6
Show file tree
Hide file tree
Showing 16 changed files with 380 additions and 23 deletions.
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ An advanced block that allows displaying post types based on different query par
- **Name:** core/query
- **Category:** theme
- **Supports:** align (full, wide), layout, ~~html~~
- **Attributes:** namespace, query, queryId, tagName
- **Attributes:** enhancedPagination, namespace, query, queryId, tagName

## No results

Expand Down
3 changes: 2 additions & 1 deletion packages/block-library/src/post-template/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"queryContext",
"displayLayout",
"templateSlug",
"previewPostType"
"previewPostType",
"enhancedPagination"
],
"supports": {
"reusable": false,
Expand Down
10 changes: 7 additions & 3 deletions packages/block-library/src/post-template/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ function block_core_post_template_uses_featured_image( $inner_blocks ) {
* @return string Returns the output of the query, structured using the layout defined by the block's inner blocks.
*/
function render_block_core_post_template( $attributes, $content, $block ) {
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$enhanced_pagination = isset( $block->context['enhancedPagination'] ) && $block->context['enhancedPagination'];
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];

// Use global query if needed.
$use_global_query = ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] );
Expand Down Expand Up @@ -120,7 +121,10 @@ function render_block_core_post_template( $attributes, $content, $block ) {

// Wrap the render inner blocks in a `li` element with the appropriate post classes.
$post_classes = implode( ' ', get_post_class( 'wp-block-post' ) );
$content .= '<li class="' . esc_attr( $post_classes ) . '">' . $block_content . '</li>';

$inner_block_directives = $enhanced_pagination ? ' data-wp-key="post-template-item-' . $post_id . '"' : '';

$content .= '<li' . $inner_block_directives . ' class="' . esc_attr( $post_classes ) . '">' . $block_content . '</li>';
}

/*
Expand Down
8 changes: 7 additions & 1 deletion packages/block-library/src/query-pagination-next/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
"type": "string"
}
},
"usesContext": [ "queryId", "query", "paginationArrow", "showLabel" ],
"usesContext": [
"queryId",
"query",
"paginationArrow",
"showLabel",
"enhancedPagination"
],
"supports": {
"reusable": false,
"html": false,
Expand Down
23 changes: 20 additions & 3 deletions packages/block-library/src/query-pagination-next/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
* @return string Returns the next posts link for the query pagination.
*/
function render_block_core_query_pagination_next( $attributes, $content, $block ) {
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
$max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$enhanced_pagination = isset( $block->context['enhancedPagination'] ) && $block->context['enhancedPagination'];
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
$max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;

$wrapper_attributes = get_block_wrapper_attributes();
$show_label = isset( $block->context['showLabel'] ) ? (bool) $block->context['showLabel'] : true;
Expand Down Expand Up @@ -61,6 +62,22 @@ function render_block_core_query_pagination_next( $attributes, $content, $block
}
wp_reset_postdata(); // Restore original Post Data.
}

if ( $enhanced_pagination ) {
$p = new WP_HTML_Tag_Processor( $content );
if ( $p->next_tag(
array(
'tag_name' => 'a',
'class_name' => 'wp-block-query-pagination-next',
)
) ) {
$p->set_attribute( 'data-wp-key', 'query-pagination-next' );
$p->set_attribute( 'data-wp-on--click', 'actions.core.query.navigate' );
$p->set_attribute( 'data-wp-on--mouseenter', 'actions.core.query.prefetch' );
$content = $p->get_updated_html();
}
}

return $content;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"default": 2
}
},
"usesContext": [ "queryId", "query" ],
"usesContext": [ "queryId", "query", "enhancedPagination" ],
"supports": {
"reusable": false,
"html": false,
Expand Down
22 changes: 19 additions & 3 deletions packages/block-library/src/query-pagination-numbers/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
* @return string Returns the pagination numbers for the Query.
*/
function render_block_core_query_pagination_numbers( $attributes, $content, $block ) {
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
$max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$enhanced_pagination = isset( $block->context['enhancedPagination'] ) && $block->context['enhancedPagination'];
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
$max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;

$wrapper_attributes = get_block_wrapper_attributes();
$content = '';
Expand Down Expand Up @@ -84,9 +85,24 @@ function render_block_core_query_pagination_numbers( $attributes, $content, $blo
wp_reset_postdata(); // Restore original Post Data.
$wp_query = $prev_wp_query;
}

if ( empty( $content ) ) {
return '';
}

if ( $enhanced_pagination ) {
$p = new WP_HTML_Tag_Processor( $content );
while ( $p->next_tag(
array(
'tag_name' => 'a',
'class_name' => 'page-numbers',
)
) ) {
$p->set_attribute( 'data-wp-on--click', 'actions.core.query.navigate' );
}
$content = $p->get_updated_html();
}

return sprintf(
'<div %1$s>%2$s</div>',
$wrapper_attributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
"type": "string"
}
},
"usesContext": [ "queryId", "query", "paginationArrow", "showLabel" ],
"usesContext": [
"queryId",
"query",
"paginationArrow",
"showLabel",
"enhancedPagination"
],
"supports": {
"reusable": false,
"html": false,
Expand Down
21 changes: 19 additions & 2 deletions packages/block-library/src/query-pagination-previous/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
* @return string Returns the previous posts link for the query.
*/
function render_block_core_query_pagination_previous( $attributes, $content, $block ) {
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
$page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
$enhanced_pagination = isset( $block->context['enhancedPagination'] ) && $block->context['enhancedPagination'];
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];

$wrapper_attributes = get_block_wrapper_attributes();
$show_label = isset( $block->context['showLabel'] ) ? (bool) $block->context['showLabel'] : true;
Expand Down Expand Up @@ -49,6 +50,22 @@ function render_block_core_query_pagination_previous( $attributes, $content, $bl
$label
);
}

if ( $enhanced_pagination ) {
$p = new WP_HTML_Tag_Processor( $content );
if ( $p->next_tag(
array(
'tag_name' => 'a',
'class_name' => 'wp-block-query-pagination-previous',
)
) ) {
$p->set_attribute( 'data-wp-key', 'query-pagination-previous' );
$p->set_attribute( 'data-wp-on--click', 'actions.core.query.navigate' );
$p->set_attribute( 'data-wp-on--mouseenter', 'actions.core.query.prefetch' );
$content = $p->get_updated_html();
}
}

return $content;
}

Expand Down
11 changes: 9 additions & 2 deletions packages/block-library/src/query/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,24 @@
},
"namespace": {
"type": "string"
},
"enhancedPagination": {
"type": "boolean",
"default": false
}
},
"providesContext": {
"queryId": "queryId",
"query": "query",
"displayLayout": "displayLayout"
"displayLayout": "displayLayout",
"enhancedPagination": "enhancedPagination"
},
"supports": {
"align": [ "wide", "full" ],
"html": false,
"layout": true
},
"editorStyle": "wp-block-query-editor"
"editorStyle": "wp-block-query-editor",
"style": "wp-block-query",
"viewScript": "file:./view.min.js"
}
49 changes: 46 additions & 3 deletions packages/block-library/src/query/edit/inspector-controls/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
import { debounce } from '@wordpress/compose';
import { useEffect, useState, useCallback } from '@wordpress/element';
import { useEffect, useState, useCallback, useRef } from '@wordpress/element';
import { speak } from '@wordpress/a11y';

/**
* Internal dependencies
Expand All @@ -40,8 +41,8 @@ import {
const { BlockInfo } = unlock( blockEditorPrivateApis );

export default function QueryInspectorControls( props ) {
const { attributes, setQuery, setDisplayLayout } = props;
const { query, displayLayout } = attributes;
const { attributes, setQuery, setDisplayLayout, setAttributes } = props;
const { query, displayLayout, enhancedPagination } = attributes;
const {
order,
orderBy,
Expand Down Expand Up @@ -123,6 +124,18 @@ export default function QueryInspectorControls( props ) {
isControlAllowed( allowedControls, 'parents' ) &&
isPostTypeHierarchical;

const enhancedPaginationNotice = __(
'Enhanced Pagination might cause interactive blocks within the Post Template to stop working. Disable it if you experience any issues.'
);

const isFirstRender = useRef( true ); // Don't speak on first render.
useEffect( () => {
if ( ! isFirstRender.current && enhancedPagination ) {
speak( enhancedPaginationNotice );
}
isFirstRender.current = false;
}, [ enhancedPagination, enhancedPaginationNotice ] );

const showFiltersPanel =
showTaxControl ||
showAuthorControl ||
Expand Down Expand Up @@ -280,6 +293,36 @@ export default function QueryInspectorControls( props ) {
</ToolsPanel>
</InspectorControls>
) }
<InspectorControls>
<PanelBody
title={ __( 'User Experience' ) }
initialOpen={ false }
>
<ToggleControl
label={ __( 'Enhanced pagination' ) }
help={ __(
"Don't refresh the page when paginating to another page."
) }
checked={ !! enhancedPagination }
onChange={ ( value ) =>
setAttributes( {
enhancedPagination: !! value,
} )
}
/>
{ enhancedPagination && (
<div>
<Notice
spokenMessage={ null }
status="warning"
isDismissible={ false }
>
{ enhancedPaginationNotice }
</Notice>
</div>
) }
</PanelBody>
</InspectorControls>
</>
);
}
1 change: 1 addition & 0 deletions packages/block-library/src/query/edit/query-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export default function QueryContent( {
attributes={ attributes }
setQuery={ updateQuery }
setDisplayLayout={ updateDisplayLayout }
setAttributes={ setAttributes }
/>
<BlockControls>
<QueryToolbar
Expand Down
Loading

0 comments on commit abfe3c6

Please sign in to comment.