-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Block editor: add a way to extend style dependency handles for editor content #37466
Changes from all commits
4ac24e6
6e155aa
1db9c0f
b1cafda
c31db2a
f59535a
b4b1eac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
/** | ||
* Resolves WP Dependency handles to HTML. | ||
* | ||
* @param array $instance WP Dependency instance. | ||
* @param array|null $handles Handles to resolve. | ||
* | ||
* @return string HTML. | ||
*/ | ||
function gutenberg_resolve_dependencies( $instance, $handles ) { | ||
if ( ! $handles || count( $handles ) === 0 ) { | ||
return ''; | ||
} | ||
|
||
ob_start(); | ||
|
||
$done = $instance->done; | ||
$instance->done = array(); | ||
$instance->do_items( array_unique( $handles ) ); | ||
$instance->done = $done; | ||
|
||
return ob_get_clean(); | ||
} | ||
|
||
add_filter( | ||
'block_editor_settings_all', | ||
function( $settings ) { | ||
// In the future the above assets should be passed through here as well, | ||
// but some stylesheets cannot be prefixed without specificity issues, | ||
// so we need to make an exception. | ||
$settings['__unstableResolvedContentStyles'] = gutenberg_resolve_dependencies( | ||
wp_styles(), | ||
isset( $settings['__experimentalContentStyles'] ) ? $settings['__experimentalContentStyles'] : array() | ||
); | ||
return $settings; | ||
}, | ||
100 | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,7 +66,71 @@ function useDarkThemeBodyClassName( styles ) { | |
); | ||
} | ||
|
||
export default function EditorStyles( { styles } ) { | ||
function useParsedAssets( html ) { | ||
return useMemo( () => { | ||
const doc = document.implementation.createHTMLDocument( '' ); | ||
doc.body.innerHTML = html; | ||
return Array.from( doc.body.children ); | ||
}, [ html ] ); | ||
} | ||
|
||
function PrefixedStyle( { | ||
tagName, | ||
__unstablePrefix, | ||
href, | ||
id, | ||
rel, | ||
media, | ||
textContent, | ||
} ) { | ||
const TagName = tagName.toLowerCase(); | ||
|
||
function onLoad( event ) { | ||
const { sheet, ownerDocument } = event.target; | ||
const { defaultView } = ownerDocument; | ||
const { CSSStyleRule } = defaultView; | ||
const { cssRules } = sheet; | ||
|
||
let index = 0; | ||
|
||
for ( const rule of cssRules ) { | ||
const { selectorText, cssText } = rule; | ||
|
||
if ( ! ( rule instanceof CSSStyleRule ) ) { | ||
continue; | ||
} | ||
|
||
if ( selectorText.includes( EDITOR_STYLES_SELECTOR ) ) { | ||
continue; | ||
} | ||
|
||
sheet.deleteRule( index ); | ||
sheet.insertRule( | ||
'html :where(.editor-styles-wrapper) ' + cssText, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey, I don't understand why we're prefixing the styles with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I'm not mistaken I think it's just to lower the specificity, but I'm also curious to know if that is indeed the case or not 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I understand it lowers the specificity but I don't know why. I guess my question is: why the styles loaded through a wp dependency should be any different than the styles loaded via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just used the same thing as #32659. But I guess if we consistently increase the specificity of all styles, this is not necessary. Cc @jasmussen. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where did we land in this conversation? I'd think we want to use the same we use for styles passed via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding the |
||
index | ||
); | ||
|
||
index++; | ||
} | ||
} | ||
|
||
if ( ! __unstablePrefix ) { | ||
onLoad = null; | ||
} | ||
|
||
if ( TagName === 'style' ) { | ||
return <TagName { ...{ id, onLoad } }>{ textContent }</TagName>; | ||
} | ||
|
||
return <TagName { ...{ href, id, rel, media, onLoad } } />; | ||
} | ||
|
||
export default function EditorStyles( { | ||
styles, | ||
__unstableResolvedContentStyles, | ||
__unstablePrefix, | ||
} ) { | ||
const _styles = useParsedAssets( __unstableResolvedContentStyles ); | ||
const transformedStyles = useMemo( | ||
() => transformStyles( styles, EDITOR_STYLES_SELECTOR ), | ||
[ styles ] | ||
|
@@ -77,6 +141,22 @@ export default function EditorStyles( { styles } ) { | |
{ /* Use an empty style element to have a document reference, | ||
but this could be any element. */ } | ||
<style ref={ useDarkThemeBodyClassName( styles ) } /> | ||
{ _styles.map( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do you think these styles should load? Before or after the other inline styles? I'd think they should load after. I've also found that styles are now inconsistent, they load in different order depending on the context. In the front, post editor, and template editor we do:
In the site editor we do:
cc @scruffian @andrewserong I thought we had them all loading in the same order? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, we can load them after. |
||
( { tagName, href, id, rel, media, textContent } ) => ( | ||
<PrefixedStyle | ||
key={ id } | ||
__unstablePrefix={ __unstablePrefix } | ||
{ ...{ | ||
tagName, | ||
href, | ||
id, | ||
rel, | ||
media, | ||
textContent, | ||
} } | ||
/> | ||
) | ||
) } | ||
{ transformedStyles.map( ( css, index ) => ( | ||
<style key={ index }>{ css }</style> | ||
) ) } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
/** | ||
* Plugin Name: Gutenberg Test Iframed Editor Style | ||
* Plugin URI: https://github.com/WordPress/gutenberg | ||
* Author: Gutenberg Team | ||
* | ||
* @package gutenberg-test-iframed-add-editor-style | ||
*/ | ||
|
||
// Enable the template editor to test the iframed content. | ||
add_action( | ||
'setup_theme', | ||
function() { | ||
add_theme_support( 'block-templates' ); | ||
} | ||
); | ||
|
||
add_filter( | ||
'block_editor_settings_all', | ||
function( $settings ) { | ||
wp_register_style( | ||
'my-theme-stylesheet', | ||
plugin_dir_url( __FILE__ ) . 'iframed-editor-style/style.css', | ||
array(), | ||
filemtime( plugin_dir_path( __FILE__ ) . 'iframed-editor-style/style.css' ) | ||
); | ||
$settings['__experimentalContentStyles'][] = 'my-theme-stylesheet'; | ||
return $settings; | ||
} | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
p { | ||
border: 1px solid red | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
activatePlugin, | ||
createNewPost, | ||
deactivatePlugin, | ||
insertBlock, | ||
canvas, | ||
createNewTemplate, | ||
} from '@wordpress/e2e-test-utils'; | ||
|
||
async function getComputedStyle( context ) { | ||
return await context.evaluate( () => { | ||
const container = document.querySelector( '.wp-block-paragraph' ); | ||
return window.getComputedStyle( container )[ 'border-width' ]; | ||
} ); | ||
} | ||
|
||
describe( 'iframed editor styles', () => { | ||
beforeEach( async () => { | ||
await activatePlugin( 'gutenberg-test-iframed-editor-style' ); | ||
await createNewPost( { postType: 'page' } ); | ||
} ); | ||
|
||
afterEach( async () => { | ||
await deactivatePlugin( 'gutenberg-test-iframed-editor-style' ); | ||
} ); | ||
|
||
it( 'should load editor styles through the block editor settings', async () => { | ||
await insertBlock( 'Paragraph' ); | ||
|
||
expect( await getComputedStyle( page ) ).toBe( '1px' ); | ||
|
||
await createNewTemplate( 'Iframed Test' ); | ||
await canvas().waitForSelector( '.wp-block-paragraph' ); | ||
|
||
expect( await getComputedStyle( canvas() ) ).toBe( '1px' ); | ||
} ); | ||
} ); |
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.
This technique is cool, though there's a noticeable delay in the styles being applied. Presumably, because we add the stylesheet in the client after all things have been already loaded and the
onLoad
event happens even after.Thinking about how to improve this:
link
andstyle
) to embedded styles (style
) and prefixed them just here, so we don't wait toonLoad
to do it?__unstableResolvedContentStyles
) and attach to itsonLoad
event a public function that takes care of prefixing them? While we still rely on theonLoad
event here it'll happen earlier because we're not adding it in the client.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.
The good thing here is that we don't have to do any CSS parsing. I talked with @mcsf about the delay. I wonder if we could block the original CSS from applying, maybe by emptying and later refilling it. 🤔
It would be good if we don't have to do any parsing. I was hoping there would be some API to scope link and style tags to a given selector, but that doesn't exist apparently.
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.
This would be ideal: https://css-tricks.com/saving-the-day-with-scoped-css/
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.
@oandregal Could you remind me where you see the delay?
I now removed the prefixing from the iframes since it's not needed there. Only the "old" non iframe editors will need prefixing.
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.
I was able to repro with a big post (Gutenberg demo). It's more noticeable the 1st time the post is loaded and if you disable the cache. Also by throtling the network and/or the CPU it becomes more visible.
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.
What is more visible? What do you see? Could you record it?