diff --git a/.eslintrc.js b/.eslintrc.js index d9f2b3ae8e132..5e20681906c42 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -54,6 +54,29 @@ module.exports = { }, ], '@wordpress/no-unsafe-wp-apis': 'off', + 'no-restricted-imports': [ + 'error', + { + paths: [ + { + name: 'lodash', + importNames: [ 'memoize' ], + message: 'Please use `memize` instead.', + }, + { + name: 'reakit', + message: + 'Please use Reakit API through `@wordpress/components` instead.', + }, + { + name: 'redux', + importNames: [ 'combineReducers' ], + message: + 'Please use `combineReducers` from `@wordpress/data` instead.', + }, + ], + }, + ], 'no-restricted-syntax': [ 'error', // NOTE: We can't include the forward slash in our regex or @@ -79,16 +102,6 @@ module.exports = { message: 'Deprecated functions must be removed before releasing this version.', }, - { - selector: - 'ImportDeclaration[source.value="redux"] Identifier.imported[name="combineReducers"]', - message: 'Use `combineReducers` from `@wordpress/data`', - }, - { - selector: - 'ImportDeclaration[source.value="lodash"] Identifier.imported[name="memoize"]', - message: 'Use memize instead of Lodash’s memoize', - }, { selector: 'CallExpression[callee.object.name="page"][callee.property.name="waitFor"]', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 033ecb9d331c0..6a2c29e06c81e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,7 +11,7 @@ /packages/data-controls @nerrad # Blocks -/packages/block-library @ajitbohra @talldan +/packages/block-library @ajitbohra /packages/block-library/src/gallery @mkevins /packages/block-library/src/social-links @mkaz /packages/block-library/src/social-link @mkaz @@ -54,7 +54,7 @@ /packages/custom-templated-path-webpack-plugin @ntwb @nerrad @ajitbohra /packages/docgen @nosolosw /packages/e2e-test-utils @gziolo @ntwb @nerrad @ajitbohra -/packages/e2e-tests @ntwb @nerrad @ajitbohra @talldan +/packages/e2e-tests @ntwb @nerrad @ajitbohra /packages/eslint-plugin @gziolo @ntwb @nerrad @ajitbohra /packages/jest-console @gziolo @ntwb @nerrad @ajitbohra /packages/jest-preset-default @gziolo @ntwb @nerrad @ajitbohra @@ -88,10 +88,10 @@ /packages/html-entities /packages/i18n @swissspidy /packages/is-shallow-equal -/packages/keycodes @talldan @ellatrix +/packages/keycodes @ellatrix /packages/priority-queue /packages/token-list -/packages/url @talldan +/packages/url /packages/wordcount /packages/warning /packages/keyboard-shortcuts @@ -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/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 050d91e2ef001..a27a295da5089 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,40 +1,68 @@ --- name: Bug report -about: Create a report to help us improve +about: Report a bug with the WordPress block editor or Gutenberg plugin --- -**Describe the bug** -A clear and concise description of what the bug is. + + +## Description + + +## Step-by-step reproduction instructions + + +## Expected behaviour + + +## Actual behaviour + + +## Screenshots or screen recording (optional) + + +## Code snippet (optional) + + +## WordPress information +- WordPress version: +- Gutenberg version: +- Are all plugins except Gutenberg deactivated? +- Are you using a default theme (e.g. Twenty Twenty-One)? + +## Device information +- Device: +- Operating system: +- Browser: diff --git a/.github/ISSUE_TEMPLATE/Bug_report_mobile.md b/.github/ISSUE_TEMPLATE/Bug_report_mobile.md new file mode 100644 index 0000000000000..295e8a0ed6b0a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_report_mobile.md @@ -0,0 +1,55 @@ +--- +name: Bug report (Mobile) +about: Report a bug with the mobile app version of Gutenberg +labels: Mobile App Android/iOS + +--- + + + +## Description + + +## Step-by-step reproduction instructions + + +## Expected behaviour + + +## Actual behaviour + + +## Screenshots or screen recording (optional) + + +## WordPress information +- WordPress version: +- Gutenberg version: +- Are all plugins except Gutenberg deactivated? +- Are you using a default theme (e.g. Twenty Twenty-One)? + +## Device information +- Device: +- Operating system: +- WordPress app version: diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md deleted file mode 100644 index c621fa8402fc4..0000000000000 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Help Request -about: Please post help requests or ‘how to’ questions in support channels first - ---- - -Search first! Your issue may have already been reported. - -For general help requests, please post in the support forum at https://wordpress.org/support/forum/how-to-and-troubleshooting/. - -Technical help requests have their own section of the support forum at https://wordpress.org/support/forum/wp-advanced/. - -You may also ask for technical support at https://wordpress.stackexchange.com/. - -Please make sure you have checked the Handbook at https://wordpress.org/gutenberg/handbook before asking your question. - -Thank you! diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index e36eff9baf5d2..cfae99f42ff9e 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -1,14 +1,18 @@ --- name: Feature request -about: Suggest an idea for this project +about: Propose an idea for a feature or an enhancement --- -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +## What problem does this address? + -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. +## What is your proposed solution? + diff --git a/.github/ISSUE_TEMPLATE/Mobile_apps_bug_report.md b/.github/ISSUE_TEMPLATE/Mobile_apps_bug_report.md deleted file mode 100644 index 8581315fe6c8a..0000000000000 --- a/.github/ISSUE_TEMPLATE/Mobile_apps_bug_report.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug report for Mobile Apps -about: Create a report to help us improve the Gutenberg mobile apps version -labels: Mobile App Android/iOS ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Tap on '....' -3. Scroll down to '....' -4. See '... exact error ...' - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Editor version (please complete the following information):** -- WordPress version: [e.g: 5.3.2] -- Does the website have the Gutenberg plugin installed, or is it using the block editor that comes by default? [e.g: "gutenberg plugin", "default"] -- If the Gutenberg plugin is installed, which version is it? [e.g., 7.6] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6, Pixel 3] - - OS: [e.g. iOS 8.1, Android 10.0] - - WordPress App Version [e.g. 15.3] or Demo App Version [branch name or git commit hash] - -**Additional context** -- To report a security issue, please visit the WordPress HackerOne program: https://hackerone.com/wordpress. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000000..65b1db3f9f9ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: General help request + url: https://wordpress.org/support/forum/how-to-and-troubleshooting/ + about: For general help requests, create a new topic in the Fixing WordPress support forum + - name: Technical help request + url: https://wordpress.org/support/forum/wp-advanced/ + about: For more technical help requests, create a new topic in the Developing with WordPress Forum + - name: Development help request + url: https://wordpress.stackexchange.com/ + about: For questions about WordPress development, ask a question in the WordPress Development Stack Exchange diff --git a/.github/workflows/stale-issue-needs-info.yml b/.github/workflows/stale-issue-needs-info.yml new file mode 100644 index 0000000000000..c7413a32c2e4e --- /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/bin/plugin/commands/packages.js b/bin/plugin/commands/packages.js index 6291008ae575a..7625bb8a71e27 100644 --- a/bin/plugin/commands/packages.js +++ b/bin/plugin/commands/packages.js @@ -108,8 +108,18 @@ async function updatePackages( const changelogFiles = await glob( path.resolve( gitWorkingDirectoryPath, 'packages/*/CHANGELOG.md' ) ); + const changelogFilesPublicPackages = changelogFiles.filter( + ( changelogPath ) => { + const pkg = require( path.join( + path.dirname( changelogPath ), + 'package.json' + ) ); + return pkg.private !== true; + } + ); + const processedPackages = await Promise.all( - changelogFiles.map( async ( changelogPath ) => { + changelogFilesPublicPackages.map( async ( changelogPath ) => { const fileStream = fs.createReadStream( changelogPath ); const rl = readline.createInterface( { diff --git a/changelog.txt b/changelog.txt index c335fd3bb0e1e..09218013c9007 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,159 @@ == Changelog == += 9.8.0-rc.1 = + +### Enhancements + +- Try: Transparent spacer. ([28103](https://github.com/WordPress/gutenberg/pull/28103)) +- Fix the border radius in the site editor. ([27986](https://github.com/WordPress/gutenberg/pull/27986)) +- Display matching variation icon in Block Switcher. ([27903](https://github.com/WordPress/gutenberg/pull/27903)) +- Group Block: Add border radius. ([27665](https://github.com/WordPress/gutenberg/pull/27665)) +- Try: Fix appender margins again. ([27392](https://github.com/WordPress/gutenberg/pull/27392)) + +### New APIs + +- Create Block: Allow using locally installed packages with templates. ([28105](https://github.com/WordPress/gutenberg/pull/28105)) +- Create block: Add support for static assets. ([28038](https://github.com/WordPress/gutenberg/pull/28038)) +- Compose: Export useIsomorphicLayoutEffect and use it. ([28023](https://github.com/WordPress/gutenberg/pull/28023)) + +### Bug Fixes + +- Fix block error when transforming blocks with Link Popover opened. ([28136](https://github.com/WordPress/gutenberg/pull/28136)) +- Fix PHP Notice in navigation-link. ([28134](https://github.com/WordPress/gutenberg/pull/28134)) +- Prevent link paste in RichText components in Button and Navigation blocks. ([28130](https://github.com/WordPress/gutenberg/pull/28130)) +- Fix floating date status inferred for posts where the status has been edited. ([28127](https://github.com/WordPress/gutenberg/pull/28127)) +- Show an error message when a reusable block has gone missing. ([28126](https://github.com/WordPress/gutenberg/pull/28126)) +- BlockSwitcher: Fix crash due to null reference. ([28122](https://github.com/WordPress/gutenberg/pull/28122)) +- Fix nested cover block bug. ([28114](https://github.com/WordPress/gutenberg/pull/28114)) +- Verse: Fix line-wrap rendering on front-end of site. ([28109](https://github.com/WordPress/gutenberg/pull/28109)) +- FocalPointPicker: Fix rendering and dragging experience. ([28096](https://github.com/WordPress/gutenberg/pull/28096)) +- Fix invalid cover block transforms. ([28087](https://github.com/WordPress/gutenberg/pull/28087)) +- Block Directory: Fix "missing" block when the block can be installed from the directory. ([28030](https://github.com/WordPress/gutenberg/pull/28030)) +- Reusable blocks: Fix dismiss notice after error. ([28015](https://github.com/WordPress/gutenberg/pull/28015)) +- Fix locked template not updating when inner blocks template prop changes. ([28007](https://github.com/WordPress/gutenberg/pull/28007)) +- Fix editor crash when registering a block pattern without `categories`. ([27970](https://github.com/WordPress/gutenberg/pull/27970)) +- Fix the RTL editor styles and the theme styles option. ([27947](https://github.com/WordPress/gutenberg/pull/27947)) +- Don't close the block inserter when clicking the scrollbar or an empty area. ([27946](https://github.com/WordPress/gutenberg/pull/27946)) +- Fix AlignmentMatrixControl focus issue. ([27945](https://github.com/WordPress/gutenberg/pull/27945)) +- Fix unexpected autosave for published posts. ([27942](https://github.com/WordPress/gutenberg/pull/27942)) +- Fix RadioGroup to support zero as a Radio value. ([27906](https://github.com/WordPress/gutenberg/pull/27906)) +- Update embed block transforms to permit multiple links to be pasted in a paragraph (#27551). ([27746](https://github.com/WordPress/gutenberg/pull/27746)) +- Change the week header and left button style to meet the date spacing. ([27730](https://github.com/WordPress/gutenberg/pull/27730)) +- Add aria labels to box control component inputs/button. ([27727](https://github.com/WordPress/gutenberg/pull/27727)) +- Use clientWidth when no width is available for cropper. ([27687](https://github.com/WordPress/gutenberg/pull/27687)) +- Core Data: Normalize `_fields` value for use in `stableKey`. ([27526](https://github.com/WordPress/gutenberg/pull/27526)) + +### Performance + +- Components: Expose composite API from Reakit. ([28085](https://github.com/WordPress/gutenberg/pull/28085)) +- Improve Inserter block hover performance. ([26348](https://github.com/WordPress/gutenberg/pull/26348)) + +### Experiments + +- Delete unused options while upgrading the plugin. ([28164](https://github.com/WordPress/gutenberg/pull/28164)) +- Hide the theme without comments.php deprecation message. ([28128](https://github.com/WordPress/gutenberg/pull/28128)) +- Fix navigation editor. ([28080](https://github.com/WordPress/gutenberg/pull/28080)) +- Widgets: Temporary fix for saving widgets. ([28078](https://github.com/WordPress/gutenberg/pull/28078)) +- Decouple query from edit site. ([27972](https://github.com/WordPress/gutenberg/pull/27972)) +- Only enable the template mode for viewable post types. ([27948](https://github.com/WordPress/gutenberg/pull/27948)) +- Box control units: Ensure custom units are preserved. ([27800](https://github.com/WordPress/gutenberg/pull/27800)) +- Theme JSON: Add border radius to the theme styles schema. ([27791](https://github.com/WordPress/gutenberg/pull/27791)) +- Add theme.json i18n mechanism and JSON file specifying which theme.json paths are translatable. ([27380](https://github.com/WordPress/gutenberg/pull/27380)) +- Navigation Block: Use draft status when user creates a post and don't render unpublished posts in menus. ([27207](https://github.com/WordPress/gutenberg/pull/27207)) +- Full site editor: Load content in iframe. ([25775](https://github.com/WordPress/gutenberg/pull/25775)) + +### Documentation + +- Docs: Improve README file for `@wordpress/create-block`. ([28052](https://github.com/WordPress/gutenberg/pull/28052)) +- Create Block: Update the demo included in the README file. ([28037](https://github.com/WordPress/gutenberg/pull/28037)) +- Docs: Switch heading to Quick Start for consistency. ([28019](https://github.com/WordPress/gutenberg/pull/28019)) +- Docs: A wether, as it turns out, is a castrated ram. ([28008](https://github.com/WordPress/gutenberg/pull/28008)) +- Update Quickstart guide for the Development Environment documentation. ([28005](https://github.com/WordPress/gutenberg/pull/28005)) +- Update copyright year to 2021 in `license.md`. ([27951](https://github.com/WordPress/gutenberg/pull/27951)) +- Block API: Add more inline comments. ([20257](https://github.com/WordPress/gutenberg/pull/20257)) + +### Code Quality + +- Remove effects test file and remove unused refx dependency. ([28162](https://github.com/WordPress/gutenberg/pull/28162)) +- Annotations: Replace store name string with exposed store definition. ([28156](https://github.com/WordPress/gutenberg/pull/28156)) +- Edit Widgets: Replace store name string with exposed store definition. ([28044](https://github.com/WordPress/gutenberg/pull/28044)) +- Interface: Replace store name string with exposed store definition. ([28041](https://github.com/WordPress/gutenberg/pull/28041)) +- Upgrade Reakit to version 1.3.4. ([28013](https://github.com/WordPress/gutenberg/pull/28013)) +- Fix PHPCS warning: Undefined variable $i. ([27955](https://github.com/WordPress/gutenberg/pull/27955)) +- Consolidate block editor initializations. ([27954](https://github.com/WordPress/gutenberg/pull/27954)) +- Fix create-block PHP template files according to WordPress standards. ([27949](https://github.com/WordPress/gutenberg/pull/27949)) +- block-directory: Simplify the LOAD_ASSETS flow by making it an async function. ([25956](https://github.com/WordPress/gutenberg/pull/25956)) + +### Tools + +- Scripts: Align default engines for `check-engines` with the package. ([28143](https://github.com/WordPress/gutenberg/pull/28143)) +- end-to-end FSE: Fix intermittent errors in multi entity editing test. ([28107](https://github.com/WordPress/gutenberg/pull/28107)) +- Testing: Prevent a direct usage of Reakit. ([28095](https://github.com/WordPress/gutenberg/pull/28095)) +- Update changelog for stylelint-config. ([28074](https://github.com/WordPress/gutenberg/pull/28074)) +- Testing: Fix randomly failing end-to-end test. ([28073](https://github.com/WordPress/gutenberg/pull/28073)) +- Upgrade puppeteer to 5.5.0. ([28055](https://github.com/WordPress/gutenberg/pull/28055)) +- Build Plugin Workflow: Bump node version to 14. ([28048](https://github.com/WordPress/gutenberg/pull/28048)) +- GH Actions: Compare Performance upon Release. ([28046](https://github.com/WordPress/gutenberg/pull/28046)) +- Scripts: Add support for static assets in build commands. ([28043](https://github.com/WordPress/gutenberg/pull/28043)) +- Performance tests: Fix. ([28026](https://github.com/WordPress/gutenberg/pull/28026)) +- Scripts: Make it possible to transpile `.jsx` files with build command. ([28002](https://github.com/WordPress/gutenberg/pull/28002)) +- Revert "Upgrade webpack to version 5". ([27974](https://github.com/WordPress/gutenberg/pull/27974)) +- Scripts: ESLint minor version upgrade to 7.17.0. ([27965](https://github.com/WordPress/gutenberg/pull/27965)) +- Scripts: Upgrade Jest to the new major version (26.x). ([27956](https://github.com/WordPress/gutenberg/pull/27956)) +- Update the minimum Node.js version to 12. ([27934](https://github.com/WordPress/gutenberg/pull/27934)) +- wp-env: Ensure the environment is used with the logs command. ([27907](https://github.com/WordPress/gutenberg/pull/27907)) +- Use @wordpress/stylelint-config in @wordpress/scripts. ([27810](https://github.com/WordPress/gutenberg/pull/27810)) +- GH Actions: Add action to upload release to SVN repo. ([27591](https://github.com/WordPress/gutenberg/pull/27591)) +- GitHub Actions: Create Release Draft when tagging version. ([27488](https://github.com/WordPress/gutenberg/pull/27488)) +- ESLint Plugin: Enable import rules used in Gutenberg. ([27387](https://github.com/WordPress/gutenberg/pull/27387)) +- Eslint: Add no-unsafe-wp-apis to recommended configuration. ([27327](https://github.com/WordPress/gutenberg/pull/27327)) +- Upgrade webpack to version 5. ([26382](https://github.com/WordPress/gutenberg/pull/26382)) +- Remove /wordpress from test/linting ignore paths. ([20270](https://github.com/WordPress/gutenberg/pull/20270)) + +### Various + +- URL: Remove redundant array coercion. ([28072](https://github.com/WordPress/gutenberg/pull/28072)) +- Add: Save time theme.json escaping. ([28061](https://github.com/WordPress/gutenberg/pull/28061)) +- Visual editor: Remove focusable wrapper. ([28058](https://github.com/WordPress/gutenberg/pull/28058)) +- Readme: Increase tested Version up to WP 5.6. ([28050](https://github.com/WordPress/gutenberg/pull/28050)) +- Interface: Remove deprecated prop from InterfaceSkeleton. ([28034](https://github.com/WordPress/gutenberg/pull/28034)) +- List View: Reduce whitespace and always show nested blocks. ([28029](https://github.com/WordPress/gutenberg/pull/28029)) +- Making the sidebar inspector's tabs stick when scrolling. ([28003](https://github.com/WordPress/gutenberg/pull/28003)) +- Chore: Update Lerna dependency. ([27990](https://github.com/WordPress/gutenberg/pull/27990)) +- Try: Make focus width a CSS variable. ([27968](https://github.com/WordPress/gutenberg/pull/27968)) +- Add translation context to all block's titles. ([27933](https://github.com/WordPress/gutenberg/pull/27933)) +- Avoid using auto-drafts for theme templates and template parts. ([27910](https://github.com/WordPress/gutenberg/pull/27910)) +- Add primary destructive button style. ([27774](https://github.com/WordPress/gutenberg/pull/27774)) +- Modifies the widgets dashboard link to point to the new widgets editor. ([26880](https://github.com/WordPress/gutenberg/pull/26880)) +- Use standard select element for small number of authors. ([26426](https://github.com/WordPress/gutenberg/pull/26426)) +- Add srcset for cover image. ([25171](https://github.com/WordPress/gutenberg/pull/25171)) + + += 9.7.3 = + +### Bug Fixes + +- Prevent mangle of translation functions to fix RTL locales. + + += 9.7.2 = + +### Bug Fixes + +- Keep the inserter opened when clicking the scrollbar. + +### Various + +- Updated the "tested up to" WordPress version. + + += 9.7.1 = + +### Bug Fixes + + - Fix styling of the verse block. + + = 9.7.0 = ### Features diff --git a/docs/manifest.json b/docs/manifest.json index 8c0881895f142..fc5de0f2c6377 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/gutenberg.php b/gutenberg.php index 1f4b7c40bc37a..cd5311ad3db31 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the new block editor in core. * Requires at least: 5.3 * Requires PHP: 5.6 - * Version: 9.7.0 + * Version: 9.8.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/class-wp-theme-json-resolver.php b/lib/class-wp-theme-json-resolver.php index a94dbb1fde86d..721aa77b04629 100644 --- a/lib/class-wp-theme-json-resolver.php +++ b/lib/class-wp-theme-json-resolver.php @@ -71,6 +71,85 @@ private static function get_from_file( $file_path ) { return $config; } + /** + * Processes a tree from i18n-theme.json into a linear array + * containing the a translatable path from theme.json and an array + * of properties that are translatable. + * + * @param array $file_structure_partial A part of a theme.json i18n tree. + * @param array $current_path An array with a path on the theme.json i18n tree. + * + * @return array An array of arrays each one containing a translatable path and an array of properties that are translatable. + */ + private static function theme_json_i18_file_structure_to_preset_paths( $file_structure_partial, $current_path = array() ) { + $result = array(); + foreach ( $file_structure_partial as $property => $partial_child ) { + if ( is_numeric( $property ) ) { + return array( + array( + 'path' => $current_path, + 'translatable_keys' => $file_structure_partial, + ), + ); + } + $result = array_merge( + $result, + self::theme_json_i18_file_structure_to_preset_paths( $partial_child, array_merge( $current_path, array( $property ) ) ) + ); + } + return $result; + } + + /** + * Returns a data structure used in theme.json translation. + * + * @return array An array of theme.json paths that are translatable and the keys that are translatable + */ + private static function get_presets_to_translate() { + static $theme_json_i18n = null; + if ( null === $theme_json_i18n ) { + $file_structure = self::get_from_file( __DIR__ . '/experimental-i18n-theme.json' ); + $theme_json_i18n = self::theme_json_i18_file_structure_to_preset_paths( $file_structure ); + + } + return $theme_json_i18n; + } + + /** + * Translates a theme.json structure. + * + * @param array $theme_json_structure A theme.json structure that is going to be translatable. + * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. + * Default 'default'. + */ + private static function translate_presets( &$theme_json_structure, $domain = 'default' ) { + $preset_to_translate = self::get_presets_to_translate(); + foreach ( $theme_json_structure as &$context_value ) { + if ( empty( $context_value ) ) { + continue; + } + foreach ( $preset_to_translate as $preset ) { + $path = $preset['path']; + $translatable_keys = $preset['translatable_keys']; + $array_to_translate = gutenberg_experimental_get( $context_value, $path, null ); + if ( null === $array_to_translate ) { + continue; + } + foreach ( $array_to_translate as &$item_to_translate ) { + foreach ( $translatable_keys as $translatable_key ) { + if ( empty( $item_to_translate[ $translatable_key ] ) ) { + continue; + } + // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain + $item_to_translate[ $translatable_key ] = translate( $item_to_translate[ $translatable_key ], $domain ); + // phpcs:enable + } + } + gutenberg_experimental_set( $context_value, $path, $array_to_translate ); + } + } + } + /** * Return core's origin config. * @@ -82,6 +161,7 @@ private static function get_core_origin() { } $config = self::get_from_file( __DIR__ . '/experimental-default-theme.json' ); + self::translate_presets( $config ); // Start i18n logic to remove when JSON i18 strings are extracted. $default_colors_i18n = array( @@ -155,6 +235,7 @@ private static function get_core_origin() { */ private function get_theme_origin( $theme_support_data = array() ) { $theme_json_data = self::get_from_file( locate_template( 'experimental-theme.json' ) ); + self::translate_presets( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) ); /* * We want the presets and settings declared in theme.json @@ -233,11 +314,18 @@ private static function get_user_origin() { return $config; } - if ( is_array( $decoded_data ) ) { + // Very important to verify if the flag isGlobalStylesUserThemeJSON is true. + // If is not true the content was not escaped and is not safe. + if ( + is_array( $decoded_data ) && + isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) && + $decoded_data['isGlobalStylesUserThemeJSON'] + ) { + unset( $decoded_data['isGlobalStylesUserThemeJSON'] ); $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 6b140156f65db..0784bd2185e59 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,64 @@ 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. + * + * @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_property" mapping properties in kebab case to + * the main properties in camel case e.g: "padding-top" to "padding". + */ + private static function get_case_mappings() { + static $case_mappings; + if ( null === $case_mappings ) { + $case_mappings = array( + 'to_kebab_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 ] ); } @@ -669,7 +695,6 @@ private static function compute_style_properties( &$declarations, $context, $con if ( empty( $context['styles'] ) ) { return; } - $properties = array(); foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { if ( ! in_array( $name, $context_supports, true ) ) { @@ -696,9 +721,9 @@ 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 ) ) { - $kebabcased_name = strtolower( preg_replace( '/(? $kebabcased_name, + $kebab_cased_name = self::to_kebab_case( $prop['name'] ); + $declarations[] = array( + 'name' => $kebab_cased_name, 'value' => $value, ); } @@ -1015,6 +1040,119 @@ public function merge( $theme_json ) { } } + /** + * Removes insecure data from theme.json. + */ + public function remove_insecure_properties() { + $blocks_metadata = self::get_blocks_metadata(); + foreach ( $this->contexts as $context_name => &$context ) { + // Escape the context key. + if ( empty( $blocks_metadata[ $context_name ] ) ) { + unset( $this->contexts[ $context_name ] ); + continue; + } + + $escaped_settings = null; + $escaped_styles = null; + + // Style escaping. + if ( ! empty( $context['styles'] ) ) { + $supports = $blocks_metadata[ $context_name ]['supports']; + $declarations = array(); + self::compute_style_properties( $declarations, $context, $supports ); + foreach ( $declarations as $declaration ) { + $style_to_validate = $declaration['name'] . ': ' . $declaration['value']; + if ( esc_html( safecss_filter_attr( $style_to_validate ) ) === $style_to_validate ) { + if ( null === $escaped_styles ) { + $escaped_styles = array(); + } + $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'] ); + $path[] = $declaration_divided[1]; + gutenberg_experimental_set( + $escaped_styles, + $path, + gutenberg_experimental_get( $context['styles'], $path ) + ); + } else { + gutenberg_experimental_set( + $escaped_styles, + $path, + gutenberg_experimental_get( $context['styles'], $path ) + ); + } + } + } + } + + // Settings escaping. + // For now the ony allowed settings are presets. + if ( ! empty( $context['settings'] ) ) { + foreach ( self::PRESETS_METADATA as $preset_metadata ) { + $current_preset = gutenberg_experimental_get( $context, $preset_metadata['path'], null ); + if ( null !== $current_preset ) { + $escaped_preset = array(); + foreach ( $current_preset as $single_preset ) { + if ( + esc_attr( esc_html( $single_preset['name'] ) ) === $single_preset['name'] && + sanitize_html_class( $single_preset['slug'] ) === $single_preset['slug'] + ) { + $value = $single_preset[ $preset_metadata['value_key'] ]; + $single_preset_is_valid = null; + if ( isset( $preset_metadata['classes'] ) && count( $preset_metadata['classes'] ) > 0 ) { + $single_preset_is_valid = true; + foreach ( $preset_metadata['classes'] as $class_meta_data ) { + $property = $class_meta_data['property_name']; + $style_to_validate = $property . ': ' . $value; + if ( esc_html( safecss_filter_attr( $style_to_validate ) ) !== $style_to_validate ) { + $single_preset_is_valid = false; + break; + } + } + } else { + $property = $preset_metadata['css_var_infix']; + $style_to_validate = $property . ': ' . $value; + $single_preset_is_valid = esc_html( safecss_filter_attr( $style_to_validate ) ) === $style_to_validate; + } + if ( $single_preset_is_valid ) { + $escaped_preset[] = $single_preset; + } + } + } + if ( count( $escaped_preset ) > 0 ) { + if ( null === $escaped_settings ) { + $escaped_settings = array(); + } + gutenberg_experimental_set( $escaped_settings, $preset_metadata['path'], $escaped_preset ); + } + } + } + if ( null !== $escaped_settings ) { + $escaped_settings = $escaped_settings['settings']; + } + } + + if ( null === $escaped_settings && null === $escaped_styles ) { + unset( $this->contexts[ $context_name ] ); + } elseif ( null !== $escaped_settings && null !== $escaped_styles ) { + $context = array( + 'styles' => $escaped_styles, + 'settings' => $escaped_settings, + ); + } elseif ( null === $escaped_settings ) { + $context = array( + 'styles' => $escaped_styles, + ); + } else { + $context = array( + 'settings' => $escaped_settings, + ); + } + } + } + /** * Retuns the raw data. * diff --git a/lib/client-assets.php b/lib/client-assets.php index 54c5f41d4d3a3..89be692a87b02 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -234,6 +234,14 @@ function gutenberg_register_vendor_scripts( $scripts ) { '4.17.19', true ); + + gutenberg_register_vendor_script( + $scripts, + 'object-fit-polyfill', + 'https://unpkg.com/objectFitPolyfill@2.3.0/dist/objectFitPolyfill.min.js', + array(), + '2.3.0' + ); } add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' ); @@ -303,7 +311,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' ); @@ -706,3 +714,29 @@ function gutenberg_extend_block_editor_styles_html() { echo ""; } add_action( 'admin_footer-toplevel_page_gutenberg-edit-site', 'gutenberg_extend_block_editor_styles_html' ); + +/** + * Adds a polyfill for object-fit in environments which do not support it. + * + * The script registration occurs in `gutenberg_register_vendor_scripts`, which + * should be removed in coordination with this function. + * + * @see gutenberg_register_vendor_scripts + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit + * + * @since 9.1.0 + * + * @param WP_Scripts $scripts WP_Scripts object. + */ +function gutenberg_add_object_fit_polyfill( $scripts ) { + did_action( 'init' ) && $scripts->add_inline_script( + 'wp-polyfill', + wp_get_script_polyfill( + $scripts, + array( + '"objectFit" in document.documentElement.style' => 'object-fit-polyfill', + ) + ) + ); +} +add_action( 'wp_default_scripts', 'gutenberg_add_object_fit_polyfill', 20 ); diff --git a/lib/experimental-i18n-theme.json b/lib/experimental-i18n-theme.json new file mode 100644 index 0000000000000..4731a59df0e97 --- /dev/null +++ b/lib/experimental-i18n-theme.json @@ -0,0 +1,32 @@ +{ + "settings": { + "typography": { + "fontSizes": [ + "name" + ], + "fontStyles": [ + "name" + ], + "fontWeights": [ + "name" + ], + "fontFamilies": [ + "name" + ], + "textTransforms": [ + "name" + ], + "textDecorations": [ + "name" + ] + }, + "color": { + "palette": [ + "name" + ], + "gradients": [ + "name" + ] + } + } +} diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index bb5d26157ff35..a40a7e0187a07 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/edit-site-export.php b/lib/full-site-editing/edit-site-export.php index b299b51e16ee8..a08450f548b6c 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 d73ad831a5233..992e9b82a0982 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/lib/global-styles.php b/lib/global-styles.php index 800048bdcb219..5aab9277715e4 100644 --- a/lib/global-styles.php +++ b/lib/global-styles.php @@ -271,3 +271,112 @@ function gutenberg_experimental_global_styles_register_user_cpt() { add_action( 'init', 'gutenberg_experimental_global_styles_register_user_cpt' ); add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX ); add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' ); + + +/** + * Sanitizes global styles user content removing unsafe rules. + * + * @param string $content Post content to filter. + * @return string Filtered post content with unsafe rules removed. + */ +function gutenberg_global_styles_filter_post( $content ) { + $decoded_data = json_decode( stripslashes( $content ), true ); + $json_decoding_error = json_last_error(); + if ( + JSON_ERROR_NONE === $json_decoding_error && + is_array( $decoded_data ) && + isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) && + $decoded_data['isGlobalStylesUserThemeJSON'] + ) { + unset( $decoded_data['isGlobalStylesUserThemeJSON'] ); + $theme_json = new WP_Theme_JSON( $decoded_data ); + $theme_json->remove_insecure_properties(); + $data_to_encode = $theme_json->get_raw_data(); + $data_to_encode['isGlobalStylesUserThemeJSON'] = true; + return wp_json_encode( $data_to_encode ); + } + return $content; +} + +/** + * Adds the filters to filter global styles user theme.json. + */ +function gutenberg_global_styles_kses_init_filters() { + add_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' ); +} + +/** + * Removes the filters to filter global styles user theme.json. + */ +function gutenberg_global_styles_kses_remove_filters() { + remove_filter( 'content_save_pre', 'gutenberg_global_styles_filter_post' ); +} + +/** + * Register global styles kses filters if the user does not have unfiltered_html capability. + * + * @uses render_block_core_navigation() + * @throws WP_Error An WP_Error exception parsing the block definition. + */ +function gutenberg_global_styles_kses_init() { + gutenberg_global_styles_kses_remove_filters(); + if ( ! current_user_can( 'unfiltered_html' ) ) { + gutenberg_global_styles_kses_init_filters(); + } +} + +/** + * This filter is the last being executed on force_filtered_html_on_import. + * If the input of the filter is true it means we are in an import situation and should + * enable kses, independently of the user capabilities. + * So in that case we call gutenberg_global_styles_kses_init_filters; + * + * @param string $arg Input argument of the filter. + * @return string Exactly what was passed as argument. + */ +function gutenberg_global_styles_force_filtered_html_on_import_filter( $arg ) { + // force_filtered_html_on_import is true we need to init the global styles kses filters. + if ( $arg ) { + gutenberg_global_styles_kses_init_filters(); + } + return $arg; +} + +/** + * This filter is the last being executed on force_filtered_html_on_import. + * If the input of the filter is true it means we are in an import situation and should + * enable kses, independently of the user capabilities. + * So in that case we call gutenberg_global_styles_kses_init_filters; + * + * @param bool $allow_css Whether the CSS in the test string is considered safe. + * @param bool $css_test_string The CSS string to test.. + * @return bool If $allow_css is true it returns true. + * If $allow_css is false and the CSS rule is referencing a WordPress css variable it returns true. + * Otherwise the function return false. + */ +function gutenberg_global_styles_include_support_for_wp_variables( $allow_css, $css_test_string ) { + if ( $allow_css ) { + return $allow_css; + } + $allowed_preset_attributes = array( + 'background', + 'background-color', + 'color', + 'font-family', + 'font-size', + ); + $parts = explode( ':', $css_test_string, 2 ); + + if ( ! in_array( trim( $parts[0] ), $allowed_preset_attributes, true ) ) { + return $allow_css; + } + return ! ! preg_match( '/^var\(--wp-[a-zA-Z0-9\-]+\)$/', trim( $parts[1] ) ); +} + + +add_action( 'init', 'gutenberg_global_styles_kses_init' ); +add_action( 'set_current_user', 'gutenberg_global_styles_kses_init' ); +add_filter( 'force_filtered_html_on_import', 'gutenberg_global_styles_force_filtered_html_on_import_filter', 999 ); +add_filter( 'safecss_filter_attr_allow_css', 'gutenberg_global_styles_include_support_for_wp_variables', 10, 2 ); +// This filter needs to be executed last. + diff --git a/lib/upgrade.php b/lib/upgrade.php index fe80cc0b7e89e..2573ed6d575da 100644 --- a/lib/upgrade.php +++ b/lib/upgrade.php @@ -53,6 +53,10 @@ function _gutenberg_migrate_remove_fse_drafts() { if ( $term ) { wp_delete_term( $term->term_id, 'wp-theme' ); } + + // Delete useless options. + delete_option( 'gutenberg_last_synchronize_theme_template_checks' ); + delete_option( 'gutenberg_last_synchronize_theme_template-part_checks' ); } add_action( 'plugins_loaded', '_gutenberg_migrate_database' ); diff --git a/package-lock.json b/package-lock.json index cf92071bf6142..c9150a55bd081 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "9.7.0", + "version": "9.8.0-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -11694,7 +11694,6 @@ "react-autosize-textarea": "^7.1.0", "react-merge-refs": "^1.0.0", "react-spring": "^8.0.19", - "reakit": "1.3.4", "redux-multi": "^0.1.12", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", @@ -11739,7 +11738,6 @@ "memize": "^1.1.0", "moment": "^2.22.1", "react-easy-crop": "^3.0.0", - "reakit": "1.3.4", "tinycolor2": "^1.4.1" } }, @@ -11885,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, @@ -12163,7 +12165,6 @@ "lodash": "^4.17.19", "memize": "^1.1.0", "react-autosize-textarea": "^7.1.0", - "refx": "^3.0.0", "rememo": "^3.0.0" } }, @@ -47867,11 +47868,6 @@ } } }, - "refx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/refx/-/refx-3.0.0.tgz", - "integrity": "sha512-qmd73YvYiVWfKPECtE90ujmPwwtAnmtEOkBKgfNEuqJ4trTeKbqFV2UY878yFvHBvU7BBu4/w/Q8pk/t0zDpYA==" - }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", diff --git a/package.json b/package.json index 35a9a07afef03..3d15cf55539cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "9.7.0", + "version": "9.8.0-rc.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", @@ -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/annotations/src/block/index.js b/packages/annotations/src/block/index.js index 3a5dee89607ab..8342eb8e32609 100644 --- a/packages/annotations/src/block/index.js +++ b/packages/annotations/src/block/index.js @@ -4,6 +4,10 @@ import { addFilter } from '@wordpress/hooks'; import { withSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { STORE_NAME } from '../store/constants'; /** * Adds annotation className to the block-list-block component. * @@ -13,7 +17,7 @@ import { withSelect } from '@wordpress/data'; const addAnnotationClassName = ( OriginalComponent ) => { return withSelect( ( select, { clientId, className } ) => { const annotations = select( - 'core/annotations' + STORE_NAME ).__experimentalGetAnnotationsForBlock( clientId ); return { diff --git a/packages/annotations/src/format/annotation.js b/packages/annotations/src/format/annotation.js index f3ea7096a3782..dd0d618f9c678 100644 --- a/packages/annotations/src/format/annotation.js +++ b/packages/annotations/src/format/annotation.js @@ -7,7 +7,10 @@ import { applyFormat, removeFormat } from '@wordpress/rich-text'; const FORMAT_NAME = 'core/annotation'; const ANNOTATION_ATTRIBUTE_PREFIX = 'annotation-text-'; -const STORE_NAME = 'core/annotations'; +/** + * Internal dependencies + */ +import { STORE_NAME } from '../store/constants'; /** * Applies given annotations to the given record. diff --git a/packages/annotations/src/store/constants.js b/packages/annotations/src/store/constants.js new file mode 100644 index 0000000000000..4b8cf0416eb92 --- /dev/null +++ b/packages/annotations/src/store/constants.js @@ -0,0 +1,6 @@ +/** + * The identifier for the data store. + * + * @type {string} + */ +export const STORE_NAME = 'core/annotations'; diff --git a/packages/annotations/src/store/index.js b/packages/annotations/src/store/index.js index 81dee6efd82b4..50c3cab06a72e 100644 --- a/packages/annotations/src/store/index.js +++ b/packages/annotations/src/store/index.js @@ -13,7 +13,7 @@ import * as actions from './actions'; /** * Module Constants */ -const STORE_NAME = 'core/annotations'; +import { STORE_NAME } from './constants'; /** * Store definition for the annotations namespace. diff --git a/packages/base-styles/_variables.scss b/packages/base-styles/_variables.scss index c51a12db7b68c..e0c2a409028bd 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/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 14c40bdeebd86..630323a871348 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -32,6 +32,7 @@ $z-layers: ( ".block-library-button__inline-link .block-editor-url-input__suggestions": 6, // URL suggestions for button block above sibling inserter ".wp-block-cover__inner-container": 1, // InnerBlocks area inside cover image block ".wp-block-cover.has-background-dim::before": 1, // Overlay area inside block cover need to be higher than the video background. + ".wp-block-cover__image-background": 0, // Image background inside cover block. ".wp-block-cover__video-background": 0, // Video background inside cover block. ".wp-block-template-part__placeholder-preview-filter-input": 1, 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 fa6af8b174dbd..2929e5dc439a5 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 b91a3692b6d29..fa96d684791c4 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/package.json b/packages/block-editor/package.json index 4df1e348d459d..05841cb049ec5 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -62,7 +62,6 @@ "react-autosize-textarea": "^7.1.0", "react-merge-refs": "^1.0.0", "react-spring": "^8.0.19", - "reakit": "1.3.4", "redux-multi": "^0.1.12", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", diff --git a/packages/block-editor/src/components/alignment-toolbar/index.js b/packages/block-editor/src/components/alignment-toolbar/index.js index 8eed48b364e48..18e9f4002b681 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 f995c767f9542..c6699db92e496 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 769e96d454219..e14edfac6f47e 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 564a49e4dd238..5910fc1b29e97 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 f34ef2b23c0d9..cc8740580952e 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-appender/style.scss b/packages/block-editor/src/components/block-list-appender/style.scss index 1b893c26875d7..db86e8e8da012 100644 --- a/packages/block-editor/src/components/block-list-appender/style.scss +++ b/packages/block-editor/src/components/block-list-appender/style.scss @@ -1,7 +1,11 @@ // These styles are only applied to the appender when it appears inside of a block. // Otherwise the default appender may be improperly positioned in some themes. .block-editor-block-list__block .block-list-appender { - margin: $grid-unit-10 0; + margin: 0; + + .block-editor-default-block-appender { + margin: $grid-unit-10 0; + } // Animate appearance. .block-list-appender__toggle { diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index ef582793f9b9f..23acd106a185a 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 */ diff --git a/packages/block-editor/src/components/block-mover/index.js b/packages/block-editor/src/components/block-mover/index.js index 7ba38ce5f039a..d727e56b65eb5 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-navigation/appender.js b/packages/block-editor/src/components/block-navigation/appender.js index 58bdb381e10e6..5ea05906062c0 100644 --- a/packages/block-editor/src/components/block-navigation/appender.js +++ b/packages/block-editor/src/components/block-navigation/appender.js @@ -15,7 +15,7 @@ import { useSelect } from '@wordpress/data'; * Internal dependencies */ import BlockNavigationLeaf from './leaf'; -import DescenderLines from './descender-lines'; +import Indentation from './indentation'; import Inserter from '../inserter'; export default function BlockNavigationAppender( { @@ -23,7 +23,6 @@ export default function BlockNavigationAppender( { position, level, rowCount, - terminatedLevels, path, } ) { const isDragging = useSelect( @@ -63,11 +62,7 @@ export default function BlockNavigationAppender( { > { ( { ref, tabIndex, onFocus } ) => (
- + + { blockDisplayName } { isSelected && ( diff --git a/packages/block-editor/src/components/block-navigation/block.js b/packages/block-editor/src/components/block-navigation/block.js index da24893d49d0d..6ae8a43a376ed 100644 --- a/packages/block-editor/src/components/block-navigation/block.js +++ b/packages/block-editor/src/components/block-navigation/block.js @@ -25,7 +25,6 @@ import { BlockMoverUpButton, BlockMoverDownButton, } from '../block-mover/button'; -import DescenderLines from './descender-lines'; import BlockNavigationBlockContents from './block-contents'; import BlockSettingsDropdown from '../block-settings-menu/block-settings-dropdown'; import { useBlockNavigationContext } from './context'; @@ -39,7 +38,6 @@ export default function BlockNavigationBlock( { rowCount, siblingBlockCount, showBlockMovers, - terminatedLevels, path, } ) { const cellRef = useRef( null ); @@ -108,11 +106,6 @@ export default function BlockNavigationBlock( { > { ( { ref, tabIndex, onFocus } ) => (
- onClick( block.clientId ) } diff --git a/packages/block-editor/src/components/block-navigation/descender-lines.js b/packages/block-editor/src/components/block-navigation/indentation.js similarity index 58% rename from packages/block-editor/src/components/block-navigation/descender-lines.js rename to packages/block-editor/src/components/block-navigation/indentation.js index 58b7144bd6dd8..6ec4e5206134c 100644 --- a/packages/block-editor/src/components/block-navigation/descender-lines.js +++ b/packages/block-editor/src/components/block-navigation/indentation.js @@ -4,15 +4,11 @@ import { times } from 'lodash'; import classnames from 'classnames'; -const lineClassName = 'block-editor-block-navigator-descender-line'; +const lineClassName = 'block-editor-block-navigator-indentation'; -export default function DescenderLines( { - level, - isLastRow, - terminatedLevels, -} ) { +export default function Indentation( { level } ) { return times( level - 1, ( index ) => { - // The first 'level' that has a descender line is level 2. + // The first 'level' that has an indentation is level 2. // Add 2 to the zero-based index below to reflect that. const currentLevel = index + 2; const hasItem = currentLevel === level; @@ -22,8 +18,6 @@ export default function DescenderLines( { aria-hidden="true" className={ classnames( lineClassName, { 'has-item': hasItem, - 'is-last-row': isLastRow, - 'is-terminated': terminatedLevels.includes( currentLevel ), } ) } /> ); diff --git a/packages/block-editor/src/components/block-navigation/index.js b/packages/block-editor/src/components/block-navigation/index.js index 4e18231de1d66..ccce62a2c3313 100644 --- a/packages/block-editor/src/components/block-navigation/index.js +++ b/packages/block-editor/src/components/block-navigation/index.js @@ -36,23 +36,14 @@ function BlockNavigation( {

{ __( 'List view' ) }

- { hasHierarchy && ( - - ) } - { ! hasHierarchy && ( - - ) } + +
); } diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index 9d5dfb714a4a3..546dab181208e 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -1,6 +1,3 @@ -$tree-border-width: 2px; -$tree-item-height: 36px; - .block-editor-block-navigation__label { margin: 0 0 $grid-unit-15; color: $gray-700; @@ -10,7 +7,7 @@ $tree-item-height: 36px; } .block-editor-block-navigation__container { - padding: $grid-unit - $border-width; + min-width: 280px; } .block-editor-block-navigation-tree { @@ -24,6 +21,13 @@ $tree-item-height: 36px; // Use position relative for row animation. position: relative; + &.is-selected .block-editor-block-navigation-block-contents, + &.is-selected:hover .block-editor-block-navigation-block-contents, + &.is-selected:focus .block-editor-block-navigation-block-contents { + background: $gray-900; + color: $white; + } + &.is-dragging { display: none; } @@ -33,15 +37,21 @@ $tree-item-height: 36px; align-items: center; width: 100%; height: auto; - padding: $grid-unit-15 ($grid-unit-15 / 2); - margin-top: auto; - margin-bottom: auto; + padding: ($grid-unit-15 / 2); text-align: left; color: $gray-900; border-radius: 2px; position: relative; white-space: nowrap; + &:hover { + background: $gray-100; + } + + &:focus { + z-index: 1; + } + &.is-dropping-before::before { content: ""; position: absolute; @@ -91,14 +101,6 @@ $tree-item-height: 36px; margin-right: 6px; } - &.is-selected .block-editor-block-icon svg, - &.is-selected:focus .block-editor-block-icon svg { - color: $white; - background: $gray-900; - box-shadow: 0 0 0 $border-width $gray-900; - border-radius: $border-width; - } - .block-editor-block-navigation-block__menu-cell, .block-editor-block-navigation-block__mover-cell, .block-editor-block-navigation-block__contents-cell { @@ -225,64 +227,9 @@ $tree-item-height: 36px; .block-editor-block-navigation-appender__container { display: flex; } - - .block-editor-block-navigation-block__contents-container { - min-height: $grid-unit-60; - } - - .block-editor-block-navigator-descender-line { - position: relative; - flex-shrink: 0; - width: ( $button-size / 2 ) + 6px; - - &:first-child { - width: ( $button-size / 2 ); - } - - &.has-item { - margin-right: 6px; - } - - // Draw a vertical line using border-right. - &::before { - content: ""; - display: block; - position: absolute; - top: 1px; - bottom: -2px; - right: -1px; - border-right: 2px solid $gray-600; - } - - // If a vertical line has terminated, don't draw it. - &.is-terminated::before { - border-color: transparent; - } - - // Make the last vertical line half-height. - &.has-item.is-last-row { - height: 26px; - } - - // Draw a horizontal line using border-bottom. - &.has-item::after { - content: ""; - display: block; - position: absolute; - top: 26px; - left: 100%; - width: 5px; - border-bottom: 2px solid $gray-600; - } - } } -// Position the horizontal line in the middle of appender's height -.block-editor-block-navigation-appender__cell .block-editor-block-navigator-descender-line { - &.has-item.is-last-row { - height: $grid-unit-20; - &::after { - top: 100%; - } - } +.block-editor-block-navigator-indentation { + flex-shrink: 0; + width: 18px; } 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 2509c794c978a..e24d904d1e280 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 acb7bfcf39d31..7a9c54f4cd234 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 } ) => @@ -155,9 +168,13 @@ export const BlockSwitcher = ( { clientIds } ) => { [ clientIds ] ); - return !! blocks?.length ? ( + if ( ! blocks.length || blocks.some( ( block ) => ! block ) ) { + return null; + } + + return ( - ) : null; + ); }; export default BlockSwitcher; 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 a8ec2981e622b..b66f639d65af9 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-switcher/test/index.js b/packages/block-editor/src/components/block-switcher/test/index.js index b3048a3750586..6fda7e21f2ff0 100644 --- a/packages/block-editor/src/components/block-switcher/test/index.js +++ b/packages/block-editor/src/components/block-switcher/test/index.js @@ -25,6 +25,16 @@ describe( 'BlockSwitcher', () => { const wrapper = shallow( ); expect( wrapper.html() ).toBeNull(); } ); + + test( 'should not render block switcher with null blocks', () => { + useSelect.mockImplementation( () => ( { blocks: [ null ] } ) ); + const wrapper = shallow( + + ); + expect( wrapper.html() ).toBeNull(); + } ); } ); describe( 'BlockSwitcherDropdownMenu', () => { const headingBlock1 = { diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index c9499b4731dfd..432a7374f612b 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/block-types-list/index.js b/packages/block-editor/src/components/block-types-list/index.js index a569a8c13d4bd..ca54df0d24b78 100644 --- a/packages/block-editor/src/components/block-types-list/index.js +++ b/packages/block-editor/src/components/block-types-list/index.js @@ -1,12 +1,11 @@ -/** - * External dependencies - */ -import { Composite, useCompositeState } from 'reakit'; - /** * WordPress dependencies */ import { getBlockMenuDefaultClassName } from '@wordpress/blocks'; +import { + __unstableComposite as Composite, + __unstableUseCompositeState as useCompositeState, +} from '@wordpress/components'; /** * Internal dependencies 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 789949e13e9c0..1328665a03eb5 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]} >