-
Notifications
You must be signed in to change notification settings - Fork 798
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Site Editor Compatibility: Podcast Player block issues in editor and …
…front end views (#19578) * [not verified] For TT1 block-based theme at least, we have to wait until the dom is loaded before we execute DOM-based queries. * [not verified] # This is a combination of 3 commits. I don't like doing this, but there's no other way to enqueue/inject the styles we need for the podcast player into the site editor iframe. So, using JS and the DOM, we find the CSS we need from the head of the parent window and move it into the site editor. YAY. Changelog Adding null checks to the elements we should ensure that the parent-level dom elements are there before we use them * Using the block element to check whether it's sitting inside an iframe now before we load. Abstracting the element copier to be more generic. * Remove unused method * Switch from useEffect to ref callback for getting podcast player element ref * Making element removal optional. In the case of style sheets it won't matter much for our current use case (podcast player) but for other elements we might want to leave the element in both contexts. I can't for the life of me think of a reason why right now. * This updates the iframe check to also look for the block editor iframe name. It's not 100% bullet-proof, but aligns with the general theme of ephemerality in relation to these site editor bug fixes * Returning an array from maybeCopyElementsToSiteEditorContext and not mixed return types Testing whether we indeed have access to the parent DOM before we try to avoid fatals Co-authored-by: Glen Davies <[email protected]>
- Loading branch information
Showing
4 changed files
with
127 additions
and
8 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
projects/plugins/jetpack/changelog/fix-site-editor-podcast-player-block
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,4 @@ | ||
Significance: patch | ||
Type: compat | ||
|
||
Ensure compatibility with the Site Editor by injecting required media assets into the Site Editor canvas, and loading frontend scripts onDomReady |
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
94 changes: 94 additions & 0 deletions
94
projects/plugins/jetpack/extensions/shared/block-editor-asset-loader.js
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,94 @@ | ||
/** | ||
* Returns the current document and window contexts for `elementRef`. | ||
* Use to retrieve the correct context for elements that may be within an iframe. | ||
* | ||
* @param {HTMLElement} elementRef - The element whose context we want to return. | ||
* @returns {Object} - The current document (`currentDoc`) and window (`currentWindow`) contexts. | ||
*/ | ||
export function getLoadContext( elementRef ) { | ||
const currentDoc = elementRef.ownerDocument; | ||
const currentWindow = currentDoc.defaultView || currentDoc.parentWindow; | ||
|
||
return { currentDoc, currentWindow }; | ||
} | ||
|
||
/** | ||
* Returns whether a given element is contained within an Editor iframe. | ||
* See: https://github.com/WordPress/gutenberg/blob/bee52e68292357011a799f067ad47aa1c1d710e1/packages/block-editor/src/components/iframe/index.js | ||
* | ||
* @param {HTMLElement} elementRef - The element whose context we want to return. | ||
* @returns {boolean} - Whether `elementRef` is contained within an Editor iframe. | ||
*/ | ||
export function isElementInEditorIframe( elementRef ) { | ||
const { currentWindow } = getLoadContext( elementRef ); | ||
return currentWindow.name === 'editor-canvas' && currentWindow.self !== currentWindow.top; | ||
} | ||
|
||
/** | ||
* Returns whether a iframe has domain access to its parent. | ||
* | ||
* @param {HTMLElement} currentWindow - The window context for which we want to test access. | ||
* @returns {boolean} - Whether we have access to the parent window. | ||
*/ | ||
function canIframeAccessParentWindow( currentWindow ) { | ||
try { | ||
return !! currentWindow?.parent?.location.href; | ||
} catch ( e ) { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* This function will check if the current element (e.g., a block) sits inside an Iframe (e.g., the Site Editor) | ||
* and tries to move elements from the parent window to the iframe. | ||
* | ||
* It's a temporary work-around to inject the styles we need for the media player into the site editor. | ||
* For use until Gutenberg offers a standardized way of including enqueued/3rd-party assets. | ||
* Target usage is the Podcast Playerblock: projects/plugins/jetpack/extensions/blocks/podcast-player/. | ||
* | ||
* @param {Array} elementSelectors - An array of selectors, e.g., [ '#conan', '#robocop' ] | ||
* @param {HTMLElement} elementRef - The current element. | ||
* @param {boolean} shouldRemoveSource - Optional. Whether to remove the source element in the parent frame. | ||
* @returns {Array} - An array of successfully migrated selectors; | ||
*/ | ||
export function maybeCopyElementsToSiteEditorContext( | ||
elementSelectors, | ||
elementRef, | ||
shouldRemoveSource = false | ||
) { | ||
let results = []; | ||
// Check to see if we're in an iframe, e.g., the Site Editor. | ||
// If not, do nothing. | ||
if ( | ||
! elementRef || | ||
( ! elementSelectors && ! elementSelectors.length ) || | ||
! isElementInEditorIframe( elementRef ) | ||
) { | ||
return results; | ||
} | ||
|
||
const { currentDoc, currentWindow } = getLoadContext( elementRef ); | ||
|
||
if ( ! canIframeAccessParentWindow( currentWindow ) ) { | ||
return results; | ||
} | ||
|
||
const parentDoc = currentWindow?.parent?.document; | ||
|
||
if ( currentDoc && parentDoc ) { | ||
results = elementSelectors.filter( selector => { | ||
const parentElementToCopy = parentDoc.querySelector( selector ); | ||
const isElementAlreadyPresentInCurrentWindow = !! currentDoc.querySelector( selector ); | ||
if ( parentElementToCopy && ! isElementAlreadyPresentInCurrentWindow ) { | ||
currentDoc.head.appendChild( parentElementToCopy.cloneNode() ); | ||
if ( shouldRemoveSource ) { | ||
parentElementToCopy.remove(); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} ); | ||
|
||
return results; | ||
} | ||
} |