From 819e9c318d29a37b3d688e9938fba34e230d8a5f Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 20 Jul 2022 12:42:52 +1000 Subject: [PATCH] Layout: Add a disable-layout-styles theme supports flag to opt-out of all layout styles --- lib/block-supports/layout.php | 21 +++++++----- .../wordpress-6.1/block-editor-settings.php | 1 + .../wordpress-6.1/class-wp-theme-json-6-1.php | 5 +++ ...-rest-block-editor-settings-controller.php | 6 ++++ packages/block-editor/src/hooks/layout.js | 22 ++++++++++-- .../src/components/visual-editor/index.js | 34 +++++++++++-------- .../global-styles/use-global-styles-output.js | 30 ++++++++++++++-- .../provider/use-block-editor-settings.js | 1 + phpunit/class-wp-theme-json-test.php | 31 +++++++++++++++++ 9 files changed, 123 insertions(+), 28 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index b26ed9df659c28..838327c626f209 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -241,15 +241,18 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { $fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' ); $block_spacing = _wp_array_get( $block, array( 'attrs', 'style', 'spacing' ), null ); - // If a block's block.json skips serialization for spacing or spacing.blockGap, - // don't apply the user-defined value to the styles. - $should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' ); - $style = gutenberg_get_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing ); - - // Only add container class and enqueue block support styles if unique styles were generated. - if ( ! empty( $style ) ) { - $class_names[] = $container_class; - wp_enqueue_block_support_styles( $style ); + // Only generate Layout styles if the theme has not opted-out. + if ( ! current_theme_supports( 'disable-layout-styles' ) ) { + // If a block's block.json skips serialization for spacing or spacing.blockGap, + // don't apply the user-defined value to the styles. + $should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' ); + $style = gutenberg_get_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing ); + + // Only add container class and enqueue block support styles if unique styles were generated. + if ( ! empty( $style ) ) { + $class_names[] = $container_class; + wp_enqueue_block_support_styles( $style ); + } } // This assumes the hook only applies to blocks with a single wrapper. diff --git a/lib/compat/wordpress-6.1/block-editor-settings.php b/lib/compat/wordpress-6.1/block-editor-settings.php index de61109ed3ac73..5512390db5910d 100644 --- a/lib/compat/wordpress-6.1/block-editor-settings.php +++ b/lib/compat/wordpress-6.1/block-editor-settings.php @@ -171,6 +171,7 @@ function gutenberg_get_block_editor_settings( $settings ) { } $settings['localAutosaveInterval'] = 15; + $settings['disableLayoutStyles'] = current_theme_supports( 'disable-layout-styles' ); return $settings; } diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php index 1679e87c5a8a92..0261adb267035e 100644 --- a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php +++ b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php @@ -1252,6 +1252,11 @@ protected function get_layout_styles( $block_metadata ) { $block_rules = ''; $block_type = null; + // Skip outputting layout styles if explicitly disabled. + if ( current_theme_supports( 'disable-layout-styles' ) ) { + return $block_rules; + } + if ( isset( $block_metadata['name'] ) ) { $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_metadata['name'] ); if ( ! block_has_support( $block_type, array( '__experimentalLayout' ), false ) ) { diff --git a/lib/experimental/class-wp-rest-block-editor-settings-controller.php b/lib/experimental/class-wp-rest-block-editor-settings-controller.php index d9b726f1158da2..777e193ffe70a3 100644 --- a/lib/experimental/class-wp-rest-block-editor-settings-controller.php +++ b/lib/experimental/class-wp-rest-block-editor-settings-controller.php @@ -204,6 +204,12 @@ public function get_item_schema() { 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), ), + 'disableLayoutStyles' => array( + 'description' => __( 'Disables output of layout styles.', 'gutenberg' ), + 'type' => 'boolean', + 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), + ), + 'enableCustomLineHeight' => array( 'description' => __( 'Enables custom line height.', 'gutenberg' ), 'type' => 'boolean', diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 4db7ac3b6ef05f..d7df54ea3c614c 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -84,6 +84,22 @@ function useLayoutClasses( layout, layoutDefinitions ) { return layoutClassnames; } +/** + * Determines whether or not the theme has disabled all layout styles output. + * + * This feature only disables the output of layout styles, + * the controls for adjusting layout will still be available in the editor. + * Themes that use this feature commit to providing their own styling for layout features. + * + * @return {boolean} Whether or not the theme opts-in to disable all layout styles. + */ +function useThemeHasDisabledLayoutStyles() { + return useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return !! getSettings().disableLayoutStyles; + } ); +} + function LayoutPanel( { setAttributes, attributes, name: blockName } ) { const { layout } = attributes; const defaultThemeLayout = useSetting( 'layout' ); @@ -264,10 +280,12 @@ export const withInspectorControls = createHigherOrderComponent( export const withLayoutStyles = createHigherOrderComponent( ( BlockListBlock ) => ( props ) => { const { name, attributes } = props; - const shouldRenderLayoutStyles = hasBlockSupport( + const hasLayoutBlockSupport = hasBlockSupport( name, layoutBlockSupportKey ); + const shouldRenderLayoutStyles = + hasLayoutBlockSupport && ! useThemeHasDisabledLayoutStyles(); const id = useInstanceId( BlockListBlock ); const defaultThemeLayout = useSetting( 'layout' ) || {}; const element = useContext( BlockList.__unstableElementContext ); @@ -277,7 +295,7 @@ export const withLayoutStyles = createHigherOrderComponent( const usedLayout = layout?.inherit ? defaultThemeLayout : layout || defaultBlockLayout || {}; - const layoutClasses = shouldRenderLayoutStyles + const layoutClasses = hasLayoutBlockSupport ? useLayoutClasses( usedLayout, defaultThemeLayout?.definitions ) : null; const selector = `.${ getBlockDefaultClassName( diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 77054884472a67..0f351f0148c0e8 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -118,13 +118,15 @@ export default function VisualEditor( { styles } ) { ( select ) => select( editPostStore ).hasMetaBoxes(), [] ); - const { themeSupportsLayout, assets } = useSelect( ( select ) => { - const _settings = select( blockEditorStore ).getSettings(); - return { - themeSupportsLayout: _settings.supportsLayout, - assets: _settings.__unstableResolvedAssets, - }; - }, [] ); + const { themeHasDisabledLayoutStyles, themeSupportsLayout, assets } = + useSelect( ( select ) => { + const _settings = select( blockEditorStore ).getSettings(); + return { + themeHasDisabledLayoutStyles: _settings.disableLayoutStyles, + themeSupportsLayout: _settings.supportsLayout, + assets: _settings.__unstableResolvedAssets, + }; + }, [] ); const { clearSelectedBlock } = useDispatch( blockEditorStore ); const { setIsEditingTemplate } = useDispatch( editPostStore ); const desktopCanvasStyles = { @@ -241,13 +243,17 @@ export default function VisualEditor( { styles } ) { assets={ assets } style={ { paddingBottom } } > - { themeSupportsLayout && ! isTemplateMode && ( - - ) } + { themeSupportsLayout && + ! themeHasDisabledLayoutStyles && + ! isTemplateMode && ( + + ) } { ! isTemplateMode && (
{ const nodesWithStyles = getNodesWithStyles( tree, blockSelectors ); const nodesWithSettings = getNodesWithSettings( tree, blockSelectors ); @@ -612,7 +615,10 @@ export const toStyles = ( } // Process blockGap and layout styles. - if ( ROOT_BLOCK_SELECTOR === selector || hasLayoutSupport ) { + if ( + ! skipLayoutStyles && + ( ROOT_BLOCK_SELECTOR === selector || hasLayoutSupport ) + ) { ruleset += getLayoutStyles( { tree, style: styles, @@ -761,6 +767,22 @@ export const getBlockSelectors = ( blockTypes ) => { return result; }; +/** + * Determines whether or not the theme has disabled all layout styles output. + * + * This feature only disables the output of layout styles, + * the controls for adjusting layout will still be available in the editor. + * Themes that use this feature commit to providing their own styling for layout features. + * + * @return {boolean} Whether or not the theme opts-in to disable all layout styles. + */ +function useThemeHasDisabledLayoutStyles() { + return useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return !! getSettings().disableLayoutStyles; + } ); +} + export function useGlobalStylesOutput() { const [ stylesheets, setStylesheets ] = useState( [] ); const [ settings, setSettings ] = useState( {} ); @@ -769,6 +791,7 @@ export function useGlobalStylesOutput() { const [ blockGap ] = useSetting( 'spacing.blockGap' ); const hasBlockGapSupport = blockGap !== null; const hasFallbackGapSupport = ! hasBlockGapSupport; // This setting isn't useful yet: it exists as a placeholder for a future explicit fallback styles support. + const skipLayoutStyles = useThemeHasDisabledLayoutStyles(); useEffect( () => { if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) { @@ -784,7 +807,8 @@ export function useGlobalStylesOutput() { mergedConfig, blockSelectors, hasBlockGapSupport, - hasFallbackGapSupport + hasFallbackGapSupport, + skipLayoutStyles ); const filters = toSvgFilters( mergedConfig, blockSelectors ); setStylesheets( [ diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index a1e802d2305dfc..2d8b1b4547021a 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -129,6 +129,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { 'disableCustomColors', 'disableCustomFontSizes', 'disableCustomGradients', + 'disableLayoutStyles', 'enableCustomLineHeight', 'enableCustomSpacing', 'enableCustomUnits', diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 655c5c5e5f8c92..77dcd413d4a014 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -103,6 +103,37 @@ public function test_get_stylesheet_generates_base_fallback_gap_layout_styles( $ ); } + /** + * @dataProvider data_get_layout_definitions + * + * @param array $layout_definitions Layout definitions as stored in core theme.json. + */ + public function test_get_stylesheet_skips_layout_styles( $layout_definitions ) { + add_theme_support( 'disable-layout-styles' ); + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'layout' => array( + 'definitions' => $layout_definitions, + ), + 'spacing' => array( + 'blockGap' => null, + ), + ), + ), + 'default' + ); + $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) ); + remove_theme_support( 'disable-layout-styles' ); + + // All Layout styles should be skipped. + $this->assertEquals( + '', + $stylesheet + ); + } + /** * Data provider. *