diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4e310f9254cdea..6a2c29e06c81e0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -116,6 +116,9 @@ /lib @timothybjacobs @spacedmonkey /lib/global-styles.php @timothybjabocs @spacedmonkey @nosolosw /lib/experimental-default-theme.json @timothybjabocs @spacedmonkey @nosolosw +/lib/class-wp-theme-json.php @timothybjabocs @spacedmonkey @nosolosw +/lib/class-wp-theme-json-resolver.php @timothybjabocs @spacedmonkey @nosolosw +/phpunit/class-wp-theme-json-test.php @nosolosw # Native (Unowned) *.native.js @ghost diff --git a/.github/workflows/stale-issue-needs-info.yml b/.github/workflows/stale-issue-needs-info.yml new file mode 100644 index 00000000000000..c7413a32c2e4ec --- /dev/null +++ b/.github/workflows/stale-issue-needs-info.yml @@ -0,0 +1,18 @@ +name: "Close stale issues that requires info" +on: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'Help us move this issue forward. Since it has no activity after 15 days of requesting more information, a bot is marking the issue as stale. Please add additional information as a comment or this issued will be closed in 5 days.' + close-issue-message: 'This issue was closed because more information was requested and there was no activity. If this is a bug report and still a problem, please supply the additional information requested and reopen the issue.' + days-before-stale: 15 + days-before-close: 5 + only-labels: '[Status] Needs More Info' + stale-issue-label: '[Status] Stale' diff --git a/changelog.txt b/changelog.txt index 8cc5b05bd77a1b..7029ea3abeee96 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,8 +2,6 @@ = 9.8.0-rc.1 = - - ### Enhancements - Try: Transparent spacer. ([28103](https://github.com/WordPress/gutenberg/pull/28103)) @@ -131,6 +129,19 @@ - Add srcset for cover image. ([25171](https://github.com/WordPress/gutenberg/pull/25171)) += 9.7.4 = + +### Bug Fixes + +- Fix isRTL check by loading the ltr string explicitely. + + += 9.7.3 = + +### Bug Fixes + +- Prevent mangle of translation functions to fix RTL locales. + = 9.7.2 = diff --git a/docs/manifest.json b/docs/manifest.json index 8c0881895f1428..fc5de0f2c6377b 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1469,6 +1469,12 @@ "markdown_source": "../packages/core-data/README.md", "parent": "packages" }, + { + "title": "@wordpress/create-block-tutorial-template", + "slug": "packages-create-block-tutorial-template", + "markdown_source": "../packages/create-block-tutorial-template/README.md", + "parent": "packages" + }, { "title": "@wordpress/create-block", "slug": "packages-create-block", diff --git a/lib/class-wp-theme-json-resolver.php b/lib/class-wp-theme-json-resolver.php index 67f5f7e12c4ec9..721aa77b046297 100644 --- a/lib/class-wp-theme-json-resolver.php +++ b/lib/class-wp-theme-json-resolver.php @@ -325,7 +325,7 @@ private static function get_user_origin() { $config = $decoded_data; } } - self::$user = new WP_Theme_JSON( $config, true ); + self::$user = new WP_Theme_JSON( $config ); return self::$user; } diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index c8cd12f41023e4..0784bd2185e597 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -293,10 +293,9 @@ class WP_Theme_JSON { /** * Constructor. * - * @param array $contexts A structure that follows the theme.json schema. - * @param boolean $should_escape_styles Whether the incoming styles should be escaped. + * @param array $contexts A structure that follows the theme.json schema. */ - public function __construct( $contexts = array(), $should_escape_styles = false ) { + public function __construct( $contexts = array() ) { $this->contexts = array(); if ( ! is_array( $contexts ) ) { @@ -317,10 +316,10 @@ public function __construct( $contexts = array(), $should_escape_styles = false // Process styles subtree. $this->process_key( 'styles', $context, self::SCHEMA ); if ( isset( $context['styles'] ) ) { - $this->process_key( 'border', $context['styles'], self::SCHEMA['styles'], $should_escape_styles ); - $this->process_key( 'color', $context['styles'], self::SCHEMA['styles'], $should_escape_styles ); - $this->process_key( 'spacing', $context['styles'], self::SCHEMA['styles'], $should_escape_styles ); - $this->process_key( 'typography', $context['styles'], self::SCHEMA['styles'], $should_escape_styles ); + $this->process_key( 'border', $context['styles'], self::SCHEMA['styles'] ); + $this->process_key( 'color', $context['styles'], self::SCHEMA['styles'] ); + $this->process_key( 'spacing', $context['styles'], self::SCHEMA['styles'] ); + $this->process_key( 'typography', $context['styles'], self::SCHEMA['styles'] ); if ( empty( $context['styles'] ) ) { unset( $context['styles'] ); @@ -346,6 +345,28 @@ public function __construct( $contexts = array(), $should_escape_styles = false } } + /** + * Returns the kebab-cased name of a given property. + * + * @param string $property Property name to convert. + * @return string kebab-cased name of the property + */ + private static function to_kebab_case( $property ) { + $mappings = self::get_case_mappings(); + return $mappings['to_kebab_case'][ $property ]; + } + + /** + * Returns the property name of a kebab-cased property. + * + * @param string $property Property name to convert in kebab-case. + * @return string Name of the property + */ + private static function to_property( $property ) { + $mappings = self::get_case_mappings(); + return $mappings['to_property'][ $property ]; + } + /** * Returns a mapping on metadata properties to avoid having to constantly * transforms properties between camel case and kebab. @@ -353,36 +374,33 @@ public function __construct( $contexts = array(), $should_escape_styles = false * @return array Containing three mappings * "to_kebab_case" mapping properties in camel case to * properties in kebab case e.g: "paddingTop" to "padding-top". - * "to_camel_case" mapping properties in kebab case to - * properties in camel case e.g: "padding-top" to "paddingTop". * "to_property" mapping properties in kebab case to * the main properties in camel case e.g: "padding-top" to "padding". */ - private static function get_properties_metadata_case_mappings() { - static $properties_metadata_case_mappings; - if ( null === $properties_metadata_case_mappings ) { - $properties_metadata_case_mappings = array( + private static function get_case_mappings() { + static $case_mappings; + if ( null === $case_mappings ) { + $case_mappings = array( 'to_kebab_case' => array(), - 'to_camel_case' => array(), 'to_property' => array(), ); foreach ( self::PROPERTIES_METADATA as $key => $metadata ) { $kebab_case = strtolower( preg_replace( '/(? $value ) { - $name = 'background-color'; - if ( 'gradient' === $property ) { - $name = 'background'; - } - - if ( is_array( $value ) ) { - $result = array(); - foreach ( $value as $subproperty => $subvalue ) { - $result_subproperty = safecss_filter_attr( "$name: $subvalue" ); - if ( '' !== $result_subproperty ) { - $result[ $subproperty ] = $result_subproperty; - } - } - - if ( empty( $result ) ) { - unset( $input[ $key ][ $property ] ); - } - } else { - $result = safecss_filter_attr( "$name: $value" ); - - if ( '' === $result ) { - unset( $input[ $key ][ $property ] ); - } - } - } - } - if ( 0 === count( $input[ $key ] ) ) { unset( $input[ $key ] ); } @@ -708,8 +695,7 @@ private static function compute_style_properties( &$declarations, $context, $con if ( empty( $context['styles'] ) ) { return; } - $metadata_mappings = self::get_properties_metadata_case_mappings(); - $properties = array(); + $properties = array(); foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { if ( ! in_array( $name, $context_supports, true ) ) { continue; @@ -735,7 +721,7 @@ private static function compute_style_properties( &$declarations, $context, $con foreach ( $properties as $prop ) { $value = self::get_property_value( $context['styles'], $prop['value'] ); if ( ! empty( $value ) ) { - $kebab_cased_name = $metadata_mappings['to_kebab_case'][ $prop['name'] ]; + $kebab_cased_name = self::to_kebab_case( $prop['name'] ); $declarations[] = array( 'name' => $kebab_cased_name, 'value' => $value, @@ -1058,8 +1044,7 @@ public function merge( $theme_json ) { * Removes insecure data from theme.json. */ public function remove_insecure_properties() { - $blocks_metadata = self::get_blocks_metadata(); - $metadata_mappings = self::get_properties_metadata_case_mappings(); + $blocks_metadata = self::get_blocks_metadata(); foreach ( $this->contexts as $context_name => &$context ) { // Escape the context key. if ( empty( $blocks_metadata[ $context_name ] ) ) { @@ -1081,7 +1066,7 @@ public function remove_insecure_properties() { if ( null === $escaped_styles ) { $escaped_styles = array(); } - $property = $metadata_mappings['to_property'][ $declaration['name'] ]; + $property = self::to_property( $declaration['name'] ); $path = self::PROPERTIES_METADATA[ $property ]['value']; if ( self::has_properties( self::PROPERTIES_METADATA[ $property ] ) ) { $declaration_divided = explode( '-', $declaration['name'] ); diff --git a/lib/client-assets.php b/lib/client-assets.php index 77c0c5fd40ce2b..454b11c0ed1387 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -95,6 +95,11 @@ function gutenberg_override_script( $scripts, $handle, $src, $deps = array(), $v if ( 'wp-i18n' !== $handle && 'wp-polyfill' !== $handle ) { $scripts->set_translations( $handle, 'default' ); } + if ( 'wp-i18n' === $handle ) { + $ltr = 'rtl' === _x( 'ltr', 'text direction', 'default' ) ? 'rtl' : 'ltr'; + $output = sprintf( "wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ '%s' ] }, 'default' );", $ltr ); + $scripts->add_inline_script( 'wp-i18n', $output, 'after' ); + } } /** @@ -311,7 +316,7 @@ function gutenberg_register_packages_styles( $styles ) { $styles, 'wp-block-editor', gutenberg_url( 'build/block-editor/style.css' ), - array( 'wp-components', 'wp-editor-font' ), + array( 'wp-components' ), filemtime( gutenberg_dir_path() . 'build/editor/style.css' ) ); $styles->add_data( 'wp-block-editor', 'rtl', 'replace' ); diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index bb5d26157ff35e..a40a7e0187a074 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -105,6 +105,42 @@ function _gutenberg_get_template_files( $template_type ) { return $template_files; } +/** + * Parses wp_template content and injects the current theme's + * stylesheet as a theme attribute into each wp_template_part + * + * @param string $template_content serialized wp_template content. + * @param string $theme the active theme's stylesheet. + * + * @return string Updated wp_template content. + */ +function _inject_theme_attribute_in_content( $template_content, $theme ) { + $has_updated_content = false; + $new_content = ''; + $template_blocks = parse_blocks( $template_content ); + + foreach ( $template_blocks as $key => $block ) { + if ( + 'core/template-part' === $block['blockName'] && + ! isset( $block['attrs']['theme'] ) && + wp_get_theme()->get_stylesheet() === $theme + ) { + $template_blocks[ $key ]['attrs']['theme'] = $theme; + $has_updated_content = true; + } + } + + if ( $has_updated_content ) { + foreach ( $template_blocks as $block ) { + $new_content .= serialize_block( $block ); + } + + return $new_content; + } else { + return $template_content; + } +} + /** * Build a unified template object based on a theme file. * @@ -115,12 +151,17 @@ function _gutenberg_get_template_files( $template_type ) { */ function _gutenberg_build_template_result_from_file( $template_file, $template_type ) { $default_template_types = gutenberg_get_default_template_types(); + $template_content = file_get_contents( $template_file['path'] ); + $theme = wp_get_theme()->get_stylesheet(); + + if ( 'wp_template' === $template_type ) { + $template_content = _inject_theme_attribute_in_content( $template_content, $theme ); + } - $theme = wp_get_theme()->get_stylesheet(); $template = new WP_Block_Template(); $template->id = $theme . '//' . $template_file['slug']; $template->theme = $theme; - $template->content = file_get_contents( $template_file['path'] ); + $template->content = $template_content; $template->slug = $template_file['slug']; $template->is_custom = false; $template->type = $template_type; diff --git a/lib/full-site-editing/default-template-types.php b/lib/full-site-editing/default-template-types.php index 54cc722be96f37..132aef0224af90 100644 --- a/lib/full-site-editing/default-template-types.php +++ b/lib/full-site-editing/default-template-types.php @@ -27,7 +27,7 @@ function gutenberg_get_default_template_types() { ), 'singular' => array( 'title' => _x( 'Singular', 'Template name', 'gutenberg' ), - 'description' => __( 'Used when a single entry is queried. This template will be overridden the Single, Post, and Page templates where appropriate', 'gutenberg' ), + 'description' => __( 'Used when a single entry is queried. This template will be overridden by the Single, Post, and Page templates where appropriate', 'gutenberg' ), ), 'single' => array( 'title' => _x( 'Single', 'Template name', 'gutenberg' ), @@ -43,7 +43,7 @@ function gutenberg_get_default_template_types() { ), 'archive' => array( 'title' => _x( 'Archive', 'Template name', 'gutenberg' ), - 'description' => __( 'Used when multiple entries are queried. This template will be overridden the Category, Author, and Date templates where appropriate', 'gutenberg' ), + 'description' => __( 'Used when multiple entries are queried. This template will be overridden by the Category, Author, and Date templates where appropriate', 'gutenberg' ), ), 'author' => array( 'title' => _x( 'Author Archive', 'Template name', 'gutenberg' ), diff --git a/lib/full-site-editing/edit-site-export.php b/lib/full-site-editing/edit-site-export.php index b299b51e16ee84..a08450f548b6c5 100644 --- a/lib/full-site-editing/edit-site-export.php +++ b/lib/full-site-editing/edit-site-export.php @@ -5,6 +5,38 @@ * @package gutenberg */ +/** + * Parses wp_template content and injects the current theme's + * stylesheet as a theme attribute into each wp_template_part + * + * @param string $template_content serialized wp_template content. + * + * @return string Updated wp_template content. + */ +function _remove_theme_attribute_from_content( $template_content ) { + $has_updated_content = false; + $new_content = ''; + $template_blocks = parse_blocks( $template_content ); + + foreach ( $template_blocks as $key => $block ) { + if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['theme'] ) ) { + unset( $template_blocks[ $key ]['attrs']['theme'] ); + $has_updated_content = true; + } + } + + if ( $has_updated_content ) { + foreach ( $template_blocks as $block ) { + $new_content .= serialize_block( $block ); + } + + return $new_content; + } else { + return $template_content; + } +} + + /** * Output a ZIP file with an export of the current templates * and template parts from the site editor, and close the connection. @@ -24,6 +56,9 @@ function gutenberg_edit_site_export() { // Load templates into the zip file. $templates = gutenberg_get_block_templates(); foreach ( $templates as $template ) { + $updated_content = _remove_theme_attribute_from_content( $template['content'] ); + $template->content = $updated_content; + $zip->addFromString( 'theme/block-templates/' . $template->slug . '.html', $template->content diff --git a/lib/full-site-editing/edit-site-page.php b/lib/full-site-editing/edit-site-page.php index d73ad831a5233a..992e9b82a0982f 100644 --- a/lib/full-site-editing/edit-site-page.php +++ b/lib/full-site-editing/edit-site-page.php @@ -50,7 +50,7 @@ function gutenberg_get_editor_styles() { ); /* translators: Use this to specify the CSS font family for the default font. */ - $locale_font_family = esc_html_x( 'Noto Serif', 'CSS Font Family for Editor Font', 'gutenberg' ); + $locale_font_family = '-apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif'; $styles[] = array( 'css' => "body { font-family: '$locale_font_family' }", ); diff --git a/package-lock.json b/package-lock.json index bcac25c51b7bf0..c9150a55bd0814 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11883,6 +11883,10 @@ "write-pkg": "^4.0.0" } }, + "@wordpress/create-block-tutorial-template": { + "version": "file:packages/create-block-tutorial-template", + "dev": true + }, "@wordpress/custom-templated-path-webpack-plugin": { "version": "file:packages/custom-templated-path-webpack-plugin", "dev": true, diff --git a/package.json b/package.json index 21f5a770cf6399..3d15cf55539cd3 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "@wordpress/base-styles": "file:packages/base-styles", "@wordpress/browserslist-config": "file:packages/browserslist-config", "@wordpress/create-block": "file:packages/create-block", + "@wordpress/create-block-tutorial-template": "file:packages/create-block-tutorial-template", "@wordpress/custom-templated-path-webpack-plugin": "file:packages/custom-templated-path-webpack-plugin", "@wordpress/dependency-extraction-webpack-plugin": "file:packages/dependency-extraction-webpack-plugin", "@wordpress/docgen": "file:packages/docgen", diff --git a/packages/base-styles/_variables.scss b/packages/base-styles/_variables.scss index c51a12db7b68c8..e0c2a409028bdb 100644 --- a/packages/base-styles/_variables.scss +++ b/packages/base-styles/_variables.scss @@ -14,7 +14,6 @@ $default-font: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif; $default-font-size: 13px; $default-line-height: 1.4; -$editor-font: "Noto Serif", serif; $editor-html-font: Menlo, Consolas, monaco, monospace; $editor-font-size: 16px; $default-block-margin: 28px; // This value provides a consistent, contiguous spacing between blocks. diff --git a/packages/block-directory/src/components/downloadable-blocks-panel/index.js b/packages/block-directory/src/components/downloadable-blocks-panel/index.js index fa6af8b174dbda..2929e5dc439a56 100644 --- a/packages/block-directory/src/components/downloadable-blocks-panel/index.js +++ b/packages/block-directory/src/components/downloadable-blocks-panel/index.js @@ -7,6 +7,7 @@ import { withSelect } from '@wordpress/data'; import { __, _n, sprintf } from '@wordpress/i18n'; import { Spinner } from '@wordpress/components'; import { speak } from '@wordpress/a11y'; +import { store as blockEditorStore } from '@wordpress/block-editor'; /** * Internal dependencies @@ -77,18 +78,26 @@ function DownloadableBlocksPanel( { } export default compose( [ - withSelect( ( select, { filterValue } ) => { + withSelect( ( select, { filterValue, rootClientId = null } ) => { const { getDownloadableBlocks, isRequestingDownloadableBlocks, } = select( blockDirectoryStore ); + const { canInsertBlockType } = select( blockEditorStore ); const hasPermission = select( 'core' ).canUser( 'read', 'block-directory/search' ); + + function getInstallableBlocks( term ) { + return getDownloadableBlocks( term ).filter( ( block ) => + canInsertBlockType( block, rootClientId, true ) + ); + } + const downloadableItems = hasPermission - ? getDownloadableBlocks( filterValue ) + ? getInstallableBlocks( filterValue ) : []; const isLoading = isRequestingDownloadableBlocks( filterValue ); diff --git a/packages/block-directory/src/plugins/inserter-menu-downloadable-blocks-panel/index.js b/packages/block-directory/src/plugins/inserter-menu-downloadable-blocks-panel/index.js index b91a3692b6d295..fa96d684791c41 100644 --- a/packages/block-directory/src/plugins/inserter-menu-downloadable-blocks-panel/index.js +++ b/packages/block-directory/src/plugins/inserter-menu-downloadable-blocks-panel/index.js @@ -21,7 +21,13 @@ function InserterMenuDownloadableBlocksPanel() { return ( <__experimentalInserterMenuExtension> - { ( { onSelect, onHover, filterValue, hasItems } ) => { + { ( { + onSelect, + onHover, + filterValue, + hasItems, + rootClientId, + } ) => { if ( hasItems || ! filterValue ) { return null; } @@ -34,6 +40,7 @@ function InserterMenuDownloadableBlocksPanel() { diff --git a/packages/block-editor/src/components/alignment-toolbar/index.js b/packages/block-editor/src/components/alignment-toolbar/index.js index 8eed48b364e480..18e9f4002b681c 100644 --- a/packages/block-editor/src/components/alignment-toolbar/index.js +++ b/packages/block-editor/src/components/alignment-toolbar/index.js @@ -38,7 +38,8 @@ export function AlignmentToolbar( props ) { value, onChange, alignmentControls = DEFAULT_ALIGNMENT_CONTROLS, - label = __( 'Change text alignment' ), + label = __( 'Align' ), + describedBy = __( 'Change text alignment' ), isCollapsed = true, } = props; @@ -61,6 +62,7 @@ export function AlignmentToolbar( props ) { isCollapsed={ isCollapsed } icon={ setIcon() } label={ label } + toggleProps={ { describedBy } } popoverProps={ POPOVER_PROPS } controls={ alignmentControls.map( ( control ) => { const { align } = control; diff --git a/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap index f995c767f95428..c6699db92e4961 100644 --- a/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap @@ -47,13 +47,18 @@ exports[`AlignmentToolbar should allow custom alignment controls to be specified } isCollapsed={true} - label="Change text alignment" + label="Align" popoverProps={ Object { "isAlternate": true, "position": "bottom right", } } + toggleProps={ + Object { + "describedBy": "Change text alignment", + } + } /> `; @@ -119,12 +124,17 @@ exports[`AlignmentToolbar should match snapshot 1`] = ` } isCollapsed={true} - label="Change text alignment" + label="Align" popoverProps={ Object { "isAlternate": true, "position": "bottom right", } } + toggleProps={ + Object { + "describedBy": "Change text alignment", + } + } /> `; diff --git a/packages/block-editor/src/components/block-alignment-toolbar/index.js b/packages/block-editor/src/components/block-alignment-toolbar/index.js index 769e96d4542198..e14edfac6f47e0 100644 --- a/packages/block-editor/src/components/block-alignment-toolbar/index.js +++ b/packages/block-editor/src/components/block-alignment-toolbar/index.js @@ -96,7 +96,8 @@ export function BlockAlignmentToolbar( { ? activeAlignmentControl.icon : defaultAlignmentControl.icon } - label={ __( 'Change alignment' ) } + label={ __( 'Align' ) } + toggleProps={ { describedBy: __( 'Change alignment' ) } } controls={ enabledControls.map( ( control ) => { return { ...BLOCK_ALIGNMENTS_CONTROLS[ control ], diff --git a/packages/block-editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap index 564a49e4dd238d..5910fc1b29e976 100644 --- a/packages/block-editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap @@ -59,11 +59,16 @@ exports[`BlockAlignmentToolbar should match snapshot 1`] = ` } isCollapsed={true} - label="Change alignment" + label="Align" popoverProps={ Object { "isAlternate": true, } } + toggleProps={ + Object { + "describedBy": "Change alignment", + } + } /> `; diff --git a/packages/block-editor/src/components/block-card/README.md b/packages/block-editor/src/components/block-card/README.md index f34ef2b23c0d99..cc8740580952eb 100644 --- a/packages/block-editor/src/components/block-card/README.md +++ b/packages/block-editor/src/components/block-card/README.md @@ -6,13 +6,11 @@ In the editor, this component is displayed in two different places: in the block ![Heading block card in the block inspector](https://make.wordpress.org/core/files/2020/09/screenshot-wordpress.org-2020.09.08-14_19_21.png) - ## Table of contents 1. [Development guidelines](#development-guidelines) 2. [Related components](#related-components) - ## Development guidelines ### Usage @@ -21,20 +19,37 @@ Renders a block card with default style. ```jsx import { BlockCard } from '@wordpress/block-editor'; +import { paragraph } from '@wordpress/icons'; + +const MyBlockCard = () => ( + +); +``` -const MyBlockCard = () => ; +### Props -``` +#### icon + +- **Type:** `String` | `Object` + +The icon of the block. This can be any of [WordPress' Dashicons](https://developer.wordpress.org/resource/dashicons/), or a custom `svg` element. + +#### title -### props +- **Type:** `String` -#### blockType +The title of the block. -The type of the block that is being displayed in +#### description -Type: `Object` +- **Type:** `String` +The description of the block. ## Related components -Block Editor components are components that can be used to compose the UI of your block editor. Thus, they can only be used under a [`BlockEditorProvider`](https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/provider/README.md) in the components tree. +Block Editor components are components that can be used to compose the UI of your block editor. Thus, they can only be used under a [`BlockEditorProvider`](https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/provider/README.md) in the components tree. diff --git a/packages/block-editor/src/components/block-list/block-popover.js b/packages/block-editor/src/components/block-list/block-popover.js index 819765d7913f4f..1a9043a424b54f 100644 --- a/packages/block-editor/src/components/block-list/block-popover.js +++ b/packages/block-editor/src/components/block-list/block-popover.js @@ -16,7 +16,7 @@ import { } from '@wordpress/element'; import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; import { Popover } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { useViewportMatch } from '@wordpress/compose'; import { getScrollContainer } from '@wordpress/dom'; @@ -71,6 +71,7 @@ function BlockPopover( { const [ isToolbarForced, setIsToolbarForced ] = useState( false ); const [ isInserterShown, setIsInserterShown ] = useState( false ); const blockNodes = useContext( BlockNodes ); + const { stopTyping } = useDispatch( 'core/block-editor' ); // Controls when the side inserter on empty lines should // be shown, including writing and selection modes. @@ -94,6 +95,7 @@ function BlockPopover( { 'core/block-editor/focus-toolbar', useCallback( () => { setIsToolbarForced( true ); + stopTyping( true ); }, [] ), { bindGlobal: true, @@ -102,6 +104,12 @@ function BlockPopover( { } ); + useEffect( () => { + if ( ! shouldShowContextualToolbar ) { + setIsToolbarForced( false ); + } + }, [ shouldShowContextualToolbar ] ); + // Stores the active toolbar item index so the block toolbar can return focus // to it when re-mounting. const initialToolbarItemIndexRef = useRef(); @@ -188,9 +196,6 @@ function BlockPopover( { __unstableBoundaryParent // Observe movement for block animations (especially horizontal). __unstableObserveElement={ node } - onFocusOutside={ () => { - setIsToolbarForced( false ); - } } shouldAnchorIncludePadding > { ( shouldShowContextualToolbar || isToolbarForced ) && ( diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index ef582793f9b9f4..e0dfc1ac29f92e 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -236,6 +236,21 @@ } } + // Active entity spotlight. + &.has-active-entity:not(.is-focus-mode) { + opacity: 0.5; + transition: opacity 0.1s linear; + @include reduce-motion("transition"); + + &.is-active-entity, + &.has-child-selected, + &:not(.has-child-selected) .block-editor-block-list__block, + &.is-active-entity .block-editor-block-list__block, + .is-active-entity .block-editor-block-list__block { + opacity: 1; + } + } + /** * Block styles and alignments */ @@ -297,6 +312,11 @@ } } + // Reusable blocks parent borer. + &.is-reusable.has-child-selected::after { + box-shadow: 0 0 0 1px var(--wp-admin-theme-color); + } + // Select tool/navigation mode shows the default cursor until an additional click edits. .is-navigate-mode & { cursor: default; diff --git a/packages/block-editor/src/components/block-mover/index.js b/packages/block-editor/src/components/block-mover/index.js index 7ba38ce5f039a9..d727e56b65eb56 100644 --- a/packages/block-editor/src/components/block-mover/index.js +++ b/packages/block-editor/src/components/block-mover/index.js @@ -40,8 +40,7 @@ function BlockMover( { return null; } - const dragHandleLabel = - clientIds.length === 1 ? __( 'Drag block' ) : __( 'Drag blocks' ); + const dragHandleLabel = __( 'Drag' ); // We emulate a disabled state because forcefully applying the `disabled` // attribute on the buttons while it has focus causes the screen to change diff --git a/packages/block-editor/src/components/block-settings-menu-controls/index.js b/packages/block-editor/src/components/block-settings-menu-controls/index.js index 2509c794c978a1..e24d904d1e2804 100644 --- a/packages/block-editor/src/components/block-settings-menu-controls/index.js +++ b/packages/block-editor/src/components/block-settings-menu-controls/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { compact, isEmpty, map } from 'lodash'; +import { compact, map } from 'lodash'; /** * WordPress dependencies @@ -9,6 +9,11 @@ import { compact, isEmpty, map } from 'lodash'; import { createSlotFill, MenuGroup } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import ConvertToGroupButton from '../convert-to-group-buttons'; + const { Fill: BlockSettingsMenuControls, Slot } = createSlotFill( 'BlockSettingsMenuControls' ); @@ -31,9 +36,14 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => { return ( - { ( fills ) => - ! isEmpty( fills ) && { fills } - } + { ( fills ) => { + return ( + + { fills } + + + ); + } } ); }; diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js index acb7bfcf39d318..7a9c54f4cd234a 100644 --- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js +++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js @@ -95,7 +95,7 @@ export function BlockSettingsDropdown( { } ) => ( { const { replaceBlocks } = useDispatch( blockEditorStore ); const blockInformation = useBlockDisplayInformation( blocks[ 0 ].clientId ); - const { possibleBlockTransformations, hasBlockStyles, icon } = useSelect( + const { + possibleBlockTransformations, + hasBlockStyles, + icon, + blockTitle, + } = useSelect( ( select ) => { const { getBlockRootClientId, getBlockTransformItems } = select( blockEditorStore @@ -61,10 +66,12 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { ), hasBlockStyles: !! styles?.length, icon: _icon, + blockTitle: getBlockType( firstBlockName ).title, }; }, [ clientIds, blocks, blockInformation?.icon ] ); + const onTransform = ( name ) => replaceBlocks( clientIds, switchToBlockType( blocks, name ) ); const hasPossibleBlockTransformations = !! possibleBlockTransformations.length; @@ -74,13 +81,16 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { } /> ); } - const blockSwitcherLabel = + + const blockSwitcherLabel = blockTitle; + + const blockSwitcherDescription = 1 === blocks.length ? __( 'Change block type or style' ) : sprintf( @@ -112,7 +122,10 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { showColors /> } - toggleProps={ toggleProps } + toggleProps={ { + describedBy: blockSwitcherDescription, + ...toggleProps, + } } menuProps={ { orientation: 'both' } } > { ( { onClose } ) => diff --git a/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap index a8ec2981e622b6..b66f639d65af91 100644 --- a/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap @@ -20,7 +20,6 @@ exports[`BlockSwitcherDropdownMenu should render disabled block switcher with mu showColors={true} /> } - title="Block icon" /> `; diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index c9499b4731dfd0..432a7374f612bb 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -106,3 +106,100 @@ transform: translateY(-($block-toolbar-height + $grid-unit-15)); } } + +.show-icon-labels { + .block-editor-block-toolbar { + .components-button.has-icon { + width: auto; + + // Hide the button icons when labels are set to display... + svg { + display: none; + } + // ... and display labels. + &::after { + content: attr(aria-label); + font-size: $helptext-font-size; + } + } + } + + // Padding overrides. + + .components-accessible-toolbar .components-toolbar-group > div:first-child:last-child > .components-button.has-icon { + padding-left: 6px; + padding-right: 6px; + } + + // Switcher overrides. + .block-editor-block-switcher { + border-right: 1px solid $gray-900; + + .components-dropdown-menu__toggle { + margin-left: 0; + } + } + + .block-editor-block-switcher .components-dropdown-menu__toggle, + .block-editor-block-switcher__no-switcher-icon { + .block-editor-block-icon { + width: 0 !important; + height: 0 !important; + } + + &:focus::before { + right: $grid-unit-05 !important; + } + } + + // Parent selector overrides + + .block-editor-block-parent-selector__button { + .block-editor-block-icon { + width: 0; + } + } + + // Mover overrides. + .block-editor-block-toolbar__block-controls .block-editor-block-mover { + margin-left: 0; + white-space: nowrap; + } + + .block-editor-block-mover-button { + // The specificity can be reduced once https://github.com/WordPress/gutenberg/blob/try/block-toolbar-labels/packages/block-editor/src/components/block-mover/style.scss#L34 is also dealt with. + padding-left: $grid-unit !important; + padding-right: $grid-unit !important; + } + + .block-editor-block-mover__drag-handle.has-icon { + padding-left: 6px !important; + padding-right: 6px !important; + border-right: 1px solid $gray-900; + } + + @include break-small() { + // Specificity override for https://github.com/WordPress/gutenberg/blob/try/block-toolbar-labels/packages/block-editor/src/components/block-mover/style.scss#L69 + .is-up-button.is-up-button.is-up-button { + border-bottom: 1px solid $gray-900; + margin-right: 0; + border-radius: 0; + } + } + + .block-editor-block-contextual-toolbar .block-editor-block-mover.is-horizontal .block-editor-block-mover-button.block-editor-block-mover-button { + width: auto; + } + + // Mobile adjustments + .components-toolbar, + .components-toolbar-group { + flex-shrink: 1; + } + + .block-editor-format-toolbar { + .components-button + .components-button { + margin-left: 6px; + } + } +} diff --git a/packages/block-editor/src/components/button-block-appender/style.scss b/packages/block-editor/src/components/button-block-appender/style.scss index 96c34d9e49d6af..dbce803006d438 100644 --- a/packages/block-editor/src/components/button-block-appender/style.scss +++ b/packages/block-editor/src/components/button-block-appender/style.scss @@ -33,7 +33,8 @@ flex-direction: row; color: $gray-900; box-shadow: none; - height: 24px; + height: $icon-size; + width: $icon-size; padding: 0; margin-left: $grid-unit-10; diff --git a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap index 789949e13e9c09..1328665a03eb5d 100644 --- a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap +++ b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap @@ -51,6 +51,7 @@ exports[`ColorPaletteControl matches the snapshot 1`] = ` className="components-circular-option-picker__option-wrapper" > - ); + ).find( 'button' ); expect( iconButton.name() ).toBe( 'button' ); } ); @@ -152,14 +187,16 @@ describe( 'Button', () => { - ); + ).find( 'Tooltip' ); expect( iconButton.name() ).toBe( 'Tooltip' ); } ); } ); describe( 'with href property', () => { it( 'should render a link instead of a button with href prop', () => { - const button = shallow( + + + `; exports[`ColorPalette Dropdown should render it correctly 1`] = ` @@ -117,6 +120,7 @@ exports[`ColorPalette Dropdown should render it correctly 1`] = ` onClick={[Function]} >