-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Experiment: Add full page client-side navigation experiment setting (#…
…59707) * Add Gutenberg experiment option * Add config option and directives in PHP * Load full CSN logic conditionally * Add `data-wp-interactive` root * Change variables names * Register different scripts if the experiment is enabled * Require experimental code once interactivity is loaded * Change experiment namespace * Move full-csn logic to interactivity-router * Add proper support for prefetch * Adapt query loop * Fix modules error after csn * Add initial page to cache * WIP: Fix scripts loading after csn * Simplify code * Adapt query loop block * Fix full CSN when queryID is not defined * Remove preload logic * Change full csn conditional in query * Use only one app in the body * Use getRegionRootFragment and initialVdom * Adapt all query loop blocks * Add key to query loop block * Add `yield` to query block actions * Revert conditional scripts depending on the experiment * Register `interactivity-router-full-client-side-navigation` in the experiment * Load router conditionally in query loop * Scroll to anchor * Remove unnecessary empty conditional * Fix back and forward buttons * Fix query loop * Remove unnecessary conditional * Use full page client-side navigation naming * Change comments * Use render_block_data to change query attribute * Refactor JavaScript logic * Remove unused variable * Revert changes in query block view.js file * Remove unnecessary export from interactivity * Move logic to the existing router * Use vdom.get document.body * Remove nextTick function * Only call getElement when it is an event * Allow instanceof URL in navigate * Fix full page client-side navigation * Use `wp_enqueue_scripts` hook * Clean PHP code and docs * Move internal dependencies after WordPress ones * Add initial JSDocs to head helper functions * Allow URL instance in prefetch function * Properly support prefetch * Fix JSDoc comments * Add Promise to JSDoc Co-authored-by: Michal <mmczaplinski@gmail.com> * Specify experimental in query help message * Wrap fullPage code in IS_GUTENBERG_PLUGIN check * Use static variable to add body directive once * Wrap fetch in try/catch * Rename document variable to doc * Prevent client navigation in admin links * Add event listeners for navigate and prefetch in JS * Add check for anchor links of the same page --------- Co-authored-by: SantosGuillamot <santosguillamot@git.wordpress.org> Co-authored-by: cbravobernal <cbravobernal@git.wordpress.org> Co-authored-by: gziolo <gziolo@git.wordpress.org> Co-authored-by: DAreRodz <darerodz@git.wordpress.org> Co-authored-by: luisherranz <luisherranz@git.wordpress.org> Co-authored-by: michalczaplinski <czapla@git.wordpress.org> Co-authored-by: felixarntz <flixos90@git.wordpress.org> Co-authored-by: westonruter <westonruter@git.wordpress.org>
- Loading branch information
1 parent
1791f14
commit e16ea9f
Showing
9 changed files
with
313 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
/** | ||
* Registers full page client-side navigation option using the Interactivity API and adds the necessary directives. | ||
*/ | ||
|
||
/** | ||
* Enqueue the interactivity router script. | ||
*/ | ||
function _gutenberg_enqueue_interactivity_router() { | ||
// Set the navigation mode to full page client-side navigation. | ||
wp_interactivity_config( 'core/router', array( 'navigationMode' => 'fullPage' ) ); | ||
wp_enqueue_script_module( '@wordpress/interactivity-router' ); | ||
} | ||
|
||
add_action( 'wp_enqueue_scripts', '_gutenberg_enqueue_interactivity_router' ); | ||
|
||
/** | ||
* Set enhancedPagination attribute for query loop when the experiment is enabled. | ||
* | ||
* @param array $parsed_block The parsed block. | ||
* | ||
* @return array The same parsed block with the modified attribute. | ||
*/ | ||
function _gutenberg_add_enhanced_pagination_to_query_block( $parsed_block ) { | ||
if ( 'core/query' !== $parsed_block['blockName'] ) { | ||
return $parsed_block; | ||
} | ||
|
||
$parsed_block['attrs']['enhancedPagination'] = true; | ||
return $parsed_block; | ||
} | ||
|
||
add_filter( 'render_block_data', '_gutenberg_add_enhanced_pagination_to_query_block' ); | ||
|
||
/** | ||
* Add directives to all links. | ||
* | ||
* Note: This should probably be done per site, not by default when this option is enabled. | ||
* | ||
* @param array $content The block content. | ||
* | ||
* @return array The same block content with the directives needed. | ||
*/ | ||
function _gutenberg_add_client_side_navigation_directives( $content ) { | ||
$p = new WP_HTML_Tag_Processor( $content ); | ||
// Hack to add the necessary directives to the body tag. | ||
// TODO: Find a proper way to add directives to the body tag. | ||
static $body_interactive_added; | ||
if ( ! $body_interactive_added ) { | ||
$body_interactive_added = true; | ||
return (string) $p . '<body data-wp-interactive="core/experimental" data-wp-context="{}">'; | ||
} | ||
return (string) $p; | ||
} | ||
|
||
// TODO: Explore moving this to the server directive processing. | ||
add_filter( 'render_block', '_gutenberg_add_client_side_navigation_directives' ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/** | ||
* Helper to update only the necessary tags in the head. | ||
* | ||
* @async | ||
* @param {Array} newHead The head elements of the new page. | ||
* | ||
*/ | ||
export const updateHead = async ( newHead ) => { | ||
// Helper to get the tag id store in the cache. | ||
const getTagId = ( tag ) => tag.id || tag.outerHTML; | ||
|
||
// Map incoming head tags by their content. | ||
const newHeadMap = new Map(); | ||
for ( const child of newHead ) { | ||
newHeadMap.set( getTagId( child ), child ); | ||
} | ||
|
||
const toRemove = []; | ||
|
||
// Detect nodes that should be added or removed. | ||
for ( const child of document.head.children ) { | ||
const id = getTagId( child ); | ||
// Always remove styles and links as they might change. | ||
if ( child.nodeName === 'LINK' || child.nodeName === 'STYLE' ) | ||
toRemove.push( child ); | ||
else if ( newHeadMap.has( id ) ) newHeadMap.delete( id ); | ||
else if ( child.nodeName !== 'SCRIPT' && child.nodeName !== 'META' ) | ||
toRemove.push( child ); | ||
} | ||
|
||
// Prepare new assets. | ||
const toAppend = [ ...newHeadMap.values() ]; | ||
|
||
// Apply the changes. | ||
toRemove.forEach( ( n ) => n.remove() ); | ||
document.head.append( ...toAppend ); | ||
}; | ||
|
||
/** | ||
* Fetches and processes head assets (stylesheets and scripts) from a specified document. | ||
* | ||
* @async | ||
* @param {Document} doc The document from which to fetch head assets. It should support standard DOM querying methods. | ||
* @param {Map} headElements A map of head elements to modify tracking the URLs of already processed assets to avoid duplicates. | ||
* | ||
* @return {Promise<HTMLElement[]>} Returns an array of HTML elements representing the head assets. | ||
*/ | ||
export const fetchHeadAssets = async ( doc, headElements ) => { | ||
const headTags = []; | ||
const assets = [ | ||
{ | ||
tagName: 'style', | ||
selector: 'link[rel=stylesheet]', | ||
attribute: 'href', | ||
}, | ||
{ tagName: 'script', selector: 'script[src]', attribute: 'src' }, | ||
]; | ||
for ( const asset of assets ) { | ||
const { tagName, selector, attribute } = asset; | ||
const tags = doc.querySelectorAll( selector ); | ||
|
||
// Use Promise.all to wait for fetch to complete | ||
await Promise.all( | ||
Array.from( tags ).map( async ( tag ) => { | ||
const attributeValue = tag.getAttribute( attribute ); | ||
if ( ! headElements.has( attributeValue ) ) { | ||
try { | ||
const response = await fetch( attributeValue ); | ||
const text = await response.text(); | ||
headElements.set( attributeValue, { | ||
tag, | ||
text, | ||
} ); | ||
} catch ( e ) { | ||
// eslint-disable-next-line no-console | ||
console.error( e ); | ||
} | ||
} | ||
|
||
const headElement = headElements.get( attributeValue ); | ||
const element = doc.createElement( tagName ); | ||
element.innerText = headElement.text; | ||
for ( const attr of headElement.tag.attributes ) { | ||
element.setAttribute( attr.name, attr.value ); | ||
} | ||
headTags.push( element ); | ||
} ) | ||
); | ||
} | ||
|
||
return [ | ||
doc.querySelector( 'title' ), | ||
...doc.querySelectorAll( 'style' ), | ||
...headTags, | ||
]; | ||
}; |
Oops, something went wrong.
e16ea9f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flaky tests detected in e16ea9f.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.
🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/8783373087
📝 Reported issues:
/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js