From 25e1c94e6aaba59984ced526a1768630fbf3b36b Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Thu, 20 Jul 2023 05:43:11 +0000 Subject: [PATCH] Merge changes published in the Gutenberg plugin "release/16.3" branch --- .eslintrc.js | 118 +- .github/CODEOWNERS | 1 + .github/workflows/php-changes-detection.yml | 100 ++ .github/workflows/stale-issue-gardening.yml | 2 +- .github/workflows/unit-test.yml | 2 +- .gitignore | 2 +- bin/cherry-pick.mjs | 19 +- bin/test-create-block.sh | 4 +- changelog.txt | 1008 +++++++++++++++ docs/assets/text-transform-component.png | Bin 0 -> 5936 bytes .../getting-started-react-native.md | 4 +- .../code/react-native/osx-setup-guide.md | 2 +- docs/contributors/code/release.md | 8 +- docs/contributors/code/scripts.md | 1 + docs/contributors/code/testing-overview.md | 2 +- docs/explanations/architecture/styles.md | 20 +- docs/manifest.json | 18 + docs/reference-guides/README.md | 1 + .../block-api/block-supports.md | 73 ++ docs/reference-guides/core-blocks.md | 8 +- docs/reference-guides/data/README.md | 1 + .../data/data-core-block-editor.md | 23 + docs/reference-guides/data/data-core-nux.md | 93 ++ .../reference-guides/filters/block-filters.md | 20 +- docs/toc.json | 1 + gutenberg.php | 2 +- ...class-wp-theme-json-resolver-gutenberg.php | 4 +- lib/client-assets.php | 9 + lib/compat/wordpress-6.3/blocks.php | 2 +- .../class-gutenberg-navigation-fallback.php | 11 +- ...class-gutenberg-rest-blocks-controller.php | 4 + lib/compat/wordpress-6.3/footnotes.php | 32 + lib/compat/wordpress-6.3/script-loader.php | 1 - lib/compat/wordpress-6.3/theme-previews.php | 4 +- lib/experimental/blocks.php | 21 + lib/experimental/disable-tinymce.php | 8 + lib/experimental/fonts-api/fonts-api.php | 23 +- .../class-gutenberg-fonts-api-bc-layer.php | 49 + .../class-wp-fonts-provider-local.php | 46 + .../bc-layer/class-wp-fonts-provider.php | 98 ++ .../bc-layer/class-wp-fonts-resolver.php | 64 + .../fonts/bc-layer/class-wp-fonts-utils.php | 74 ++ .../fonts/bc-layer/class-wp-fonts.php | 199 +++ .../fonts/bc-layer/class-wp-web-fonts.php | 190 +++ .../class-wp-webfonts-provider-local.php | 65 + .../bc-layer/class-wp-webfonts-provider.php | 53 + .../bc-layer/class-wp-webfonts-utils.php | 82 ++ .../fonts/bc-layer/class-wp-webfonts.php | 146 +++ .../fonts/bc-layer/webfonts-deprecations.php | 291 +++++ .../fonts/class-wp-font-face-resolver.php | 158 +++ lib/experimental/fonts/class-wp-font-face.php | 418 +++++++ lib/experimental/fonts/fonts.php | 58 + .../class-wp-directive-processor.php | 31 + .../directive-processing.php | 6 +- lib/load.php | 34 +- package-lock.json | 953 +++++++------- package.json | 16 +- packages/README.md | 4 +- packages/babel-preset-default/package.json | 4 +- packages/base-styles/_colors.native.scss | 4 + packages/base-styles/_z-index.scss | 7 +- packages/block-editor/README.md | 4 - packages/block-editor/package.json | 2 +- .../src/components/block-controls/README.md | 89 ++ .../src/components/block-draggable/index.js | 17 +- .../block-draggable/index.native.js | 10 +- .../block-draggable/test/index.native.js | 54 - .../src/components/block-edit/edit.js | 35 +- .../src/components/block-edit/test/edit.js | 2 +- .../block-list/block-outline.native.js | 51 +- .../src/components/block-list/block.native.js | 62 +- .../components/block-list/block.native.scss | 58 +- .../components/block-list/style.native.scss | 6 +- .../block-mobile-toolbar/index.native.js | 127 -- .../block-mobile-toolbar/style.native.scss | 16 - .../components/block-mover/index.native.js | 6 +- .../test/__snapshots__/index.native.js.snap | 22 +- .../block-removal-warning-modal/index.js | 3 - .../block-settings/button.native.js | 18 +- .../block-toolbar-menu.native.js} | 25 +- .../src/components/block-toolbar/index.js | 5 +- .../components/block-toolbar/index.native.js | 92 +- .../block-toolbar-menu.native.js.snap} | 0 .../test/block-toolbar-menu.native.js} | 74 +- .../block-toolbar/test/index.native.js | 42 + .../button-block-appender/styles.native.scss | 4 +- .../src/components/caption/README.md | 49 + .../components/colors-gradients/control.js | 5 +- .../components/global-styles/color-panel.js | 39 +- .../global-styles/dimensions-panel.js | 10 +- .../global-styles/get-block-css-selector.js | 14 +- .../src/components/global-styles/hooks.js | 46 +- .../global-styles/typography-panel.js | 22 +- .../global-styles/use-global-styles-output.js | 54 +- .../src/components/global-styles/utils.js | 21 +- .../src/components/iframe/index.js | 86 +- .../iframe/use-compatibility-styles.js | 5 + packages/block-editor/src/components/index.js | 5 - .../src/components/inner-blocks/README.md | 10 + .../src/components/inner-blocks/index.js | 4 + .../components/inner-blocks/index.native.js | 4 + .../use-nested-settings-update.js | 37 +- .../src/components/inserter/index.js | 5 +- .../src/components/inserter/index.native.js | 53 +- .../inserter/reusable-block-rename-hint.js | 19 +- .../src/components/inserter/style.native.scss | 25 +- .../src/components/link-control/index.js | 5 +- .../components/link-control/search-item.js | 2 +- .../src/components/link-control/style.scss | 9 +- .../src/components/link-control/test/index.js | 26 + .../list-view/use-list-view-client-ids.js | 4 +- .../media-placeholder/index.native.js | 167 ++- .../media-placeholder/styles.native.scss | 83 +- .../src/components/preview-options/README.md | 35 +- .../src/components/preview-options/index.js | 4 +- .../src/components/rich-text/content.js | 47 +- .../rich-text/get-rich-text-values.js | 105 ++ .../hooks/use-spacing-sizes.js | 2 +- .../input-controls/axial.js | 19 +- .../input-controls/separated.js | 19 +- .../input-controls/single.js | 14 +- .../components/spacing-sizes-control/utils.js | 2 +- .../text-transform-control/README.md | 44 + .../use-block-display-information/index.js | 17 +- .../use-block-drop-zone/index.native.js | 93 +- .../src/components/use-setting/index.js | 16 +- packages/block-editor/src/hooks/margin.js | 5 +- packages/block-editor/src/hooks/padding.js | 5 +- packages/block-editor/src/hooks/utils.js | 12 +- packages/block-editor/src/private-apis.js | 8 +- packages/block-editor/src/store/actions.js | 3 +- .../src/store/private-selectors.js | 9 +- packages/block-editor/src/store/reducer.js | 1 - packages/block-editor/src/store/selectors.js | 38 +- .../block-editor/src/store/test/actions.js | 3 + .../src/store/test/private-selectors.js | 10 +- .../block-editor/src/store/test/reducer.js | 21 +- .../block-editor/src/store/test/selectors.js | 2 +- packages/block-editor/src/utils/object.js | 34 +- .../block-editor/src/utils/test/object.js | 36 + packages/block-library/package.json | 1 + .../test/__snapshots__/edit.native.js.snap | 91 +- packages/block-library/src/avatar/edit.js | 1 + packages/block-library/src/block/block.json | 3 +- .../src/block/editor.native.scss | 4 +- packages/block-library/src/buttons/edit.js | 4 +- .../block-library/src/column/edit.native.js | 14 +- .../src/column/editor.native.scss | 4 - packages/block-library/src/columns/edit.js | 1 + .../src/comment-author-avatar/edit.js | 1 + .../src/comment-template/index.php | 7 +- .../block-library/src/cover/deprecated.js | 2 + .../src/embed/embed-placeholder.native.js | 127 +- .../src/embed/styles.native.scss | 72 +- .../src/embed/test/index.native.js | 10 +- packages/block-library/src/file/block.json | 3 +- packages/block-library/src/file/index.php | 20 +- packages/block-library/src/file/inspector.js | 1 + .../test/__snapshots__/edit.native.js.snap | 91 +- .../src/file/view-interactivity.js | 18 + packages/block-library/src/file/view.js | 19 +- .../block-library/src/footnotes/block.json | 1 - packages/block-library/src/footnotes/edit.js | 23 +- .../block-library/src/footnotes/format.js | 42 +- .../block-library/src/footnotes/style.scss | 9 +- packages/block-library/src/gallery/edit.js | 2 +- .../src/gallery/shared-icon.native.js | 23 - .../src/gallery/test/index.native.js | 33 +- .../block-library/src/home-link/index.php | 17 +- packages/block-library/src/image/index.php | 55 +- .../src/image/test/edit.native.js | 2 +- .../block-library/src/latest-comments/edit.js | 1 + .../block-library/src/latest-posts/edit.js | 2 + packages/block-library/src/list/edit.js | 10 +- .../src/list/test/edit.native.js | 162 ++- .../src/media-text/media-container.native.js | 1 + .../block-library/src/missing/edit.native.js | 22 +- .../src/missing/style.native.scss | 31 +- .../test/__snapshots__/edit.native.js.snap | 34 +- .../block-library/src/navigation-link/edit.js | 108 +- .../src/navigation-submenu/edit.js | 4 +- .../block-library/src/navigation/block.json | 5 +- .../block-library/src/navigation/constants.js | 18 +- .../src/navigation/edit/index.js | 30 +- .../src/navigation/edit/inner-blocks.js | 4 +- .../navigation/edit/unsaved-inner-blocks.js | 4 +- .../use-convert-classic-menu-to-block-menu.js | 38 +- .../block-library/src/navigation/index.php | 30 +- .../block-library/src/navigation/style.scss | 35 +- .../src/navigation/use-navigation-menu.js | 102 +- .../src/navigation/view-interactivity.js | 196 +++ .../src/navigation/view-modal.js | 78 ++ packages/block-library/src/navigation/view.js | 256 +--- .../test/__snapshots__/edit.native.js.snap | 1 + .../src/paragraph/test/edit.native.js | 26 + .../block-library/src/post-template/index.php | 6 +- .../block-library/src/post-title/index.php | 9 +- .../test/__snapshots__/edit.native.js.snap | 2 + .../src/query-pagination/edit.js | 31 +- .../block-library/src/query-title/edit.js | 54 +- .../__snapshots__/transforms.native.js.snap | 10 + .../src/quote/test/transforms.native.js | 6 +- .../block-library/src/quote/transforms.js | 13 + packages/block-library/src/rss/edit.js | 3 + packages/block-library/src/search/block.json | 1 + packages/block-library/src/search/edit.js | 7 +- packages/block-library/src/search/index.php | 26 +- .../test/__snapshots__/edit.native.js.snap | 7 + packages/block-library/src/site-logo/edit.js | 1 + packages/block-library/src/tag-cloud/edit.js | 1 + .../src/template-part/edit/index.js | 2 +- .../block-library/src/template-part/index.php | 24 +- .../block-library/src/text-columns/edit.js | 1 + packages/components/CHANGELOG.md | 13 + packages/components/package.json | 4 +- .../components/src/button/index.native.js | 12 +- .../components/src/button/style.native.scss | 9 + .../components/src/form-token-field/styles.ts | 2 + .../components/src/item-group/item/hook.ts | 3 +- .../src/item-group/stories/index.tsx | 11 +- packages/components/src/item-group/styles.ts | 67 +- .../src/mobile/image/index.native.js | 14 +- .../src/mobile/image/style.native.scss | 6 +- .../navigator-provider/component.tsx | 53 +- packages/components/src/navigator/types.ts | 5 +- .../components/src/placeholder/style.scss | 5 + .../src/query-controls/index.native.js | 1 + .../components/src/query-controls/index.tsx | 1 + .../components/src/text-control/index.tsx | 4 +- .../src/text-control/test/text-control.tsx | 61 + .../toolbar/toolbar-group/style.native.scss | 5 +- .../toolbar-group-container.native.js | 29 +- packages/components/src/tooltip/README.md | 2 +- .../src/site-editor-navigation-commands.js | 5 +- packages/core-data/src/actions.js | 23 +- packages/core-data/src/entity-provider.js | 111 +- packages/core-data/src/index.js | 6 +- .../core-data/src/utils/set-nested-value.js | 2 + .../create-block-interactive-template/.npmrc | 1 + .../CHANGELOG.md | 1 + .../README.md | 19 + .../block-templates/README.md.mustache | 16 + .../block-templates/edit.js.mustache | 36 + .../block-templates/editor.scss.mustache | 12 + .../block-templates/index.js.mustache | 43 + .../block-templates/render.php.mustache | 33 + .../block-templates/style.scss.mustache | 12 + .../block-templates/view.js.mustache | 26 + .../index.js | 24 + .../package.json | 25 + .../plugin-templates/$slug.php.mustache | 43 + .../plugin-templates/readme.txt.mustache | 61 + packages/create-block/lib/init-block.js | 2 + packages/create-block/lib/scaffold.js | 2 + packages/create-block/lib/templates.js | 2 + .../lib/templates/block/view.js.mustache | 3 + .../test/__snapshots__/build.js.snap | 22 +- .../function-output-filename/index.js | 1 + .../test/fixtures/no-default/index.js | 1 + .../option-function-output-filename/index.js | 1 + .../fixtures/option-output-filename/index.js | 1 + .../test/fixtures/output-format-json/index.js | 1 + .../test/fixtures/runtime-chunk-single/b.js | 1 + .../test/fixtures/style-imports/index.js | 1 + .../test/fixtures/wordpress/index.js | 1 + .../src/admin/visit-admin-page.ts | 2 +- .../e2e-test-utils-playwright/src/test.ts | 12 - .../src/create-reusable-block.js | 6 +- packages/e2e-test-utils/src/install-plugin.js | 4 +- packages/e2e-test-utils/src/install-theme.js | 4 +- packages/e2e-test-utils/src/login-user.js | 8 +- packages/e2e-tests/plugins/block-context.php | 22 +- .../plugins/iframed-enqueue-block-assets.php | 13 + .../iframed-enqueue-block-assets/script.js | 3 + .../directive-style/block.json | 14 + .../directive-style/render.php | 93 ++ .../directive-style/view.js | 22 + .../container-blocks.test.js.snap | 9 +- .../iframed-equeue-block-assets.test.js | 7 + .../various/adding-inline-tokens.test.js | 80 -- .../editor/various/publish-button.test.js | 15 - .../editor/various/reusable-blocks.test.js | 10 +- .../site-editor/multi-entity-saving.test.js | 102 -- .../src/components/device-preview/index.js | 74 +- .../components/header/header-toolbar/index.js | 5 +- .../header/header-toolbar/index.native.js | 192 ++- .../header/header-toolbar/style.native.scss | 27 +- .../edit-post/src/components/header/index.js | 34 +- .../header/post-publish-button-or-toggle.js | 2 - .../src/components/layout/actions-panel.js | 12 +- .../edit-post/src/components/layout/index.js | 19 +- .../src/components/layout/style.native.scss | 2 +- .../test/__snapshots__/index.js.snap | 14 +- .../src/components/text-editor/index.js | 2 - .../components/visual-editor/header.native.js | 18 +- .../src/components/visual-editor/index.js | 7 +- .../visual-editor/style.native.scss | 18 - .../test/__snapshots__/index.native.js.snap | 15 + .../visual-editor/test/index.native.js | 119 +- packages/edit-post/src/plugins/index.js | 40 +- packages/edit-post/src/store/actions.js | 45 +- packages/edit-post/src/style.scss | 8 +- packages/edit-site/package.json | 1 - .../src/components/add-new-page/index.js | 3 - .../src/components/add-new-pattern/index.js | 35 +- .../src/components/add-new-template/utils.js | 15 +- .../src/components/block-editor/constants.js | 6 +- .../components/block-editor/editor-canvas.js | 2 +- .../src/components/block-editor/index.js | 4 - .../block-editor/use-site-editor-settings.js | 119 +- .../src/components/canvas-spinner/style.scss | 14 + .../code-editor/code-editor-text-area.js | 84 -- .../src/components/code-editor/index.js | 84 +- .../components/create-pattern-modal/index.js | 7 +- .../edit-site/src/components/editor/index.js | 4 +- .../document-actions/style.scss | 4 + .../src/components/header-edit-mode/index.js | 51 +- .../components/keyboard-shortcuts/index.js | 137 -- .../edit-site/src/components/layout/index.js | 18 +- .../src/components/layout/style.scss | 3 +- .../src/components/page-actions/index.js | 4 +- ...e-menu-item.js => trash-page-menu-item.js} | 31 +- .../page-content-focus-manager/constants.js | 5 - .../disable-non-page-content-blocks.js | 15 +- .../page-patterns/duplicate-menu-item.js | 196 +++ .../src/components/page-patterns/grid-item.js | 322 +++-- .../src/components/page-patterns/grid.js | 23 +- .../src/components/page-patterns/header.js | 69 + .../src/components/page-patterns/index.js | 7 +- .../components/page-patterns/pagination.js | 80 ++ .../components/page-patterns/patterns-list.js | 232 ++-- .../page-patterns/rename-menu-item.js | 115 ++ .../src/components/page-patterns/style.scss | 170 ++- .../components/page-patterns/use-patterns.js | 274 ++-- .../add-new-template-part.js | 57 + .../components/page-template-parts/index.js | 25 +- .../src/components/resizable-frame/index.js | 136 +- .../src/components/resizable-frame/style.scss | 18 +- .../src/components/save-panel/index.js | 14 +- .../page-panels/page-content.js | 12 +- .../page-panels/page-status.js | 7 +- .../sidebar-navigation-item/style.scss | 17 +- .../sidebar-navigation-screen-main/index.js | 84 +- .../template-part-hint.js | 34 + .../index.js | 7 +- .../more-menu.js | 1 + .../single-navigation-menu.js | 2 +- .../use-navigation-menu-handlers.js | 20 +- .../constants.js | 2 + .../sidebar-navigation-screen-page/index.js | 6 +- .../status-label.js | 36 +- .../sidebar-navigation-screen-pages/index.js | 68 +- .../index.js | 7 +- .../template-part-navigation-menu.js | 3 +- .../template-part-navigation-menus.js | 3 +- .../use-navigation-menu-content.js | 14 +- .../use-pattern-details.js | 7 +- .../index.js | 201 +-- .../style.scss | 20 + .../use-my-patterns.js | 13 +- .../use-pattern-categories.js | 9 + .../use-template-part-areas.js | 40 +- .../use-theme-patterns.js | 3 +- .../sidebar-navigation-screen/index.js | 21 +- .../sidebar-navigation-screen/style.scss | 14 + .../src/components/site-hub/index.js | 73 +- .../src/components/site-hub/style.scss | 13 + .../use-init-edited-entity-from-url.js | 10 +- .../use-sync-path-with-url.js | 139 ++- .../src/components/template-actions/index.js | 3 +- .../template-actions/rename-menu-item.js | 27 +- .../use-edited-entity-record/index.js | 7 +- .../src/hooks/commands/use-common-commands.js | 79 +- .../push-changes-to-global-styles/index.js | 72 +- .../edit-site/src/store/private-actions.js | 6 +- packages/edit-site/src/style.scss | 22 +- .../src/utils/normalize-record-key.js | 11 + .../edit-site/src/utils/use-activate-theme.js | 2 +- packages/edit-widgets/src/style.scss | 10 +- .../entity-record-item.js | 64 +- .../entities-saved-states/entity-type-list.js | 2 - .../components/entities-saved-states/index.js | 1 - .../entities-saved-states/style.scss | 15 - .../global-keyboard-shortcuts/index.js | 49 + .../save-shortcut.js | 55 - .../text-editor-shortcuts.js | 8 - .../visual-editor-shortcuts.js | 29 - packages/editor/src/components/index.js | 15 +- .../components/post-preview-button/index.js | 229 ++-- .../post-preview-button/test/index.js | 252 ++-- .../components/post-publish-button/index.js | 9 +- .../components/post-publish-button/label.js | 4 +- .../post-publish-button/test/index.js | 10 - .../components/post-publish-panel/index.js | 4 +- .../src/components/post-saved-state/index.js | 7 +- .../src/components/post-schedule/label.js | 8 +- .../src/components/post-sync-status/index.js | 107 +- .../src/components/post-text-editor/index.js | 91 +- .../components/post-text-editor/test/index.js | 156 --- .../components/post-title/style.native.scss | 10 +- packages/editor/src/store/actions.js | 36 +- packages/editor/src/store/selectors.js | 86 +- packages/editor/src/store/test/selectors.js | 61 +- .../src/text-color/index.native.js | 25 +- .../src/text-color/style.native.scss | 5 +- packages/interactivity/CHANGELOG.md | 7 + packages/interactivity/src/directives.js | 69 + .../src/components/import-form/index.js | 4 +- packages/list-reusable-blocks/src/index.js | 4 +- .../list-reusable-blocks/src/utils/export.js | 2 + .../list-reusable-blocks/src/utils/import.js | 10 +- packages/nux/.npmrc | 1 + packages/nux/CHANGELOG.md | 124 ++ packages/nux/README.md | 114 ++ packages/nux/package.json | 50 + packages/nux/src/components/dot-tip/README.md | 38 + packages/nux/src/components/dot-tip/index.js | 93 ++ .../nux/src/components/dot-tip/style.scss | 123 ++ .../dot-tip/test/__snapshots__/index.js.snap | 46 + .../nux/src/components/dot-tip/test/index.js | 86 ++ packages/nux/src/index.js | 13 + packages/nux/src/store/actions.js | 52 + packages/nux/src/store/index.js | 36 + packages/nux/src/store/reducer.js | 70 ++ packages/nux/src/store/selectors.js | 81 ++ packages/nux/src/store/test/actions.js | 40 + packages/nux/src/store/test/reducer.js | 69 + packages/nux/src/store/test/selectors.js | 146 +++ packages/nux/src/style.scss | 1 + packages/primitives/src/svg/index.native.js | 5 +- packages/primitives/src/svg/style.native.scss | 17 +- packages/private-apis/src/implementation.js | 1 + packages/react-native-aztec/package.json | 4 +- .../GutenbergBridgeJS2Parent.java | 4 + .../RNReactNativeGutenbergBridgeModule.java | 22 + .../WPAndroidGlue/WPAndroidGlueCode.java | 34 + .../local-storage-overrides.json | 6 + packages/react-native-bridge/index.js | 16 + .../react-native-bridge/ios/Gutenberg.swift | 8 + .../ios/GutenbergBridgeDelegate.swift | 4 + .../ios/RNReactNativeGutenbergBridge.m | 2 + .../ios/RNReactNativeGutenbergBridge.swift | 12 + packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/CHANGELOG.md | 12 + ...erg-editor-block-insertion-@canary.test.js | 8 +- .../gutenberg-editor-device-actions.test.js | 4 +- ...enberg-editor-media-blocks-@canary.test.js | 10 +- ...utenberg-editor-unsupported-blocks.test.js | 27 - .../__device-tests__/helpers/utils.js | 19 +- .../__device-tests__/pages/editor-page.js | 70 +- .../android/app/build.gradle | 1 + .../main/java/com/gutenberg/MainActivity.java | 192 ++- .../java/com/gutenberg/MainApplication.java | 26 +- .../src/main/res/drawable/more_vertical.xml | 9 + .../app/src/main/res/drawable/redo.xml | 9 + .../app/src/main/res/drawable/undo.xml | 9 + .../app/src/main/res/menu/toolbar_menu.xml | 21 + .../src/main/res/values-night/colors_dark.xml | 6 + .../app/src/main/res/values-night/styles.xml | 20 + .../app/src/main/res/values/colors.xml | 6 + .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 14 +- .../src/main/res/values/styles_toolbar.xml | 10 + .../ios/Colors.xcassets/Contents.json | 6 + .../HeaderLine.colorset/Contents.json | 38 + .../Primary.colorset/Contents.json | 33 + .../GutenbergDemo.xcodeproj/project.pbxproj | 4 + .../GutenbergViewController.swift | 113 +- .../Images.xcassets/Contents.json | 4 +- .../more.imageset/Contents.json | 23 + .../more.imageset/editor-more.png | Bin 0 -> 94 bytes .../more.imageset/editor-more@2x.png | Bin 0 -> 128 bytes .../more.imageset/editor-more@3x.png | Bin 0 -> 181 bytes .../redo.imageset/Contents.json | 23 + .../Images.xcassets/redo.imageset/redo.png | Bin 0 -> 161 bytes .../Images.xcassets/redo.imageset/redo@2x.png | Bin 0 -> 241 bytes .../Images.xcassets/redo.imageset/redo@3x.png | Bin 0 -> 312 bytes .../undo.imageset/Contents.json | 23 + .../Images.xcassets/undo.imageset/undo.png | Bin 0 -> 167 bytes .../Images.xcassets/undo.imageset/undo@2x.png | Bin 0 -> 245 bytes .../Images.xcassets/undo.imageset/undo@3x.png | Bin 0 -> 310 bytes packages/react-native-editor/ios/Podfile.lock | 20 +- packages/react-native-editor/package.json | 14 +- packages/reusable-blocks/package.json | 1 + .../reusable-blocks-menu-items/index.js | 16 +- .../reusable-block-convert-button.js | 52 +- .../reusable-blocks-manage-button.js | 61 +- packages/reusable-blocks/src/lock-unlock.js | 9 + packages/reusable-blocks/src/store/actions.js | 6 +- .../rich-text/src/component/index.native.js | 42 +- .../rich-text/src/component/style.native.scss | 10 +- .../src/component/use-select-object.js | 7 +- packages/rich-text/src/to-dom.js | 4 + packages/rich-text/src/to-tree.js | 10 +- packages/scripts/package.json | 2 +- phpcs.xml.dist | 2 +- ...erg-navigation-fallback-gutenberg-test.php | 19 +- phpunit/fonts/wp-font-face-testcase.php | 187 +++ phpunit/fonts/wp-font-face-tests-dataset.php | 274 ++++ .../wpFontFace/generateAndPrint-test.php | 57 + .../getFontsFromThemeJson-test.php | 116 ++ phpunit/fonts/wpPrintFontFaces-test.php | 75 ++ schemas/json/block.json | 110 ++ schemas/json/theme.json | 136 +- test/e2e/config/global-setup.ts | 13 + test/e2e/specs/editor/blocks/buttons.spec.js | 8 +- test/e2e/specs/editor/blocks/comments.spec.js | 10 +- .../editor/blocks/navigation-colors.spec.js | 503 ++++++++ .../navigation-frontend-interactivity.spec.js | 324 +++++ .../blocks/navigation-list-view.spec.js | 581 +++++++++ .../specs/editor/blocks/navigation.spec.js | 1106 ++--------------- .../various/adding-inline-tokens.spec.js | 81 ++ .../specs/editor/various/behaviors.spec.js | 136 ++ .../specs/editor/various/footnotes.spec.js | 108 +- .../various/manage-reusable-blocks.spec.js | 2 +- .../editor/various/navigable-toolbar.spec.js | 15 + test/e2e/specs/editor/various/undo.spec.js | 14 +- .../interactivity/directives-style.spec.ts | 118 ++ .../specs/site-editor/block-removal.spec.js | 34 +- .../specs/site-editor/hybrid-theme.spec.js | 41 + .../site-editor/multi-entity-saving.spec.js | 87 ++ .../behaviors-enabled/templates/single.html | 1 + test/native/__mocks__/styleMock.js | 15 + .../integration/editor-history.native.js | 95 +- test/native/jest.config.js | 47 +- test/native/setup.js | 4 + test/performance/config/global-setup.ts | 13 + tools/webpack/interactivity.js | 4 + 528 files changed, 16243 insertions(+), 6319 deletions(-) create mode 100644 .github/workflows/php-changes-detection.yml create mode 100644 docs/assets/text-transform-component.png create mode 100644 docs/reference-guides/data/data-core-nux.md create mode 100644 lib/compat/wordpress-6.3/footnotes.php create mode 100644 lib/experimental/fonts/bc-layer/class-gutenberg-fonts-api-bc-layer.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-fonts-provider-local.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-fonts-provider.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-fonts-resolver.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-fonts-utils.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-fonts.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-web-fonts.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-webfonts-provider-local.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-webfonts-provider.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-webfonts-utils.php create mode 100644 lib/experimental/fonts/bc-layer/class-wp-webfonts.php create mode 100644 lib/experimental/fonts/bc-layer/webfonts-deprecations.php create mode 100644 lib/experimental/fonts/class-wp-font-face-resolver.php create mode 100644 lib/experimental/fonts/class-wp-font-face.php create mode 100644 lib/experimental/fonts/fonts.php create mode 100644 packages/block-editor/src/components/block-controls/README.md delete mode 100644 packages/block-editor/src/components/block-mobile-toolbar/index.native.js delete mode 100644 packages/block-editor/src/components/block-mobile-toolbar/style.native.scss rename packages/block-editor/src/components/{block-mobile-toolbar/block-actions-menu.native.js => block-toolbar/block-toolbar-menu.native.js} (96%) rename packages/block-editor/src/components/{block-mobile-toolbar/test/__snapshots__/block-actions-menu.native.js.snap => block-toolbar/test/__snapshots__/block-toolbar-menu.native.js.snap} (100%) rename packages/block-editor/src/components/{block-mobile-toolbar/test/block-actions-menu.native.js => block-toolbar/test/block-toolbar-menu.native.js} (81%) create mode 100644 packages/block-editor/src/components/block-toolbar/test/index.native.js create mode 100644 packages/block-editor/src/components/caption/README.md create mode 100644 packages/block-editor/src/components/rich-text/get-rich-text-values.js create mode 100644 packages/block-editor/src/components/text-transform-control/README.md create mode 100644 packages/block-library/src/file/view-interactivity.js delete mode 100644 packages/block-library/src/gallery/shared-icon.native.js create mode 100644 packages/block-library/src/navigation/view-interactivity.js create mode 100644 packages/block-library/src/navigation/view-modal.js create mode 100644 packages/components/src/button/style.native.scss create mode 100644 packages/components/src/text-control/test/text-control.tsx create mode 100644 packages/create-block-interactive-template/.npmrc create mode 100644 packages/create-block-interactive-template/CHANGELOG.md create mode 100644 packages/create-block-interactive-template/README.md create mode 100644 packages/create-block-interactive-template/block-templates/README.md.mustache create mode 100644 packages/create-block-interactive-template/block-templates/edit.js.mustache create mode 100644 packages/create-block-interactive-template/block-templates/editor.scss.mustache create mode 100644 packages/create-block-interactive-template/block-templates/index.js.mustache create mode 100644 packages/create-block-interactive-template/block-templates/render.php.mustache create mode 100644 packages/create-block-interactive-template/block-templates/style.scss.mustache create mode 100644 packages/create-block-interactive-template/block-templates/view.js.mustache create mode 100644 packages/create-block-interactive-template/index.js create mode 100644 packages/create-block-interactive-template/package.json create mode 100644 packages/create-block-interactive-template/plugin-templates/$slug.php.mustache create mode 100644 packages/create-block-interactive-template/plugin-templates/readme.txt.mustache create mode 100644 packages/create-block/lib/templates/block/view.js.mustache create mode 100644 packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js delete mode 100644 packages/e2e-tests/specs/editor/various/adding-inline-tokens.test.js delete mode 100644 packages/edit-post/src/components/visual-editor/style.native.scss create mode 100644 packages/edit-post/src/components/visual-editor/test/__snapshots__/index.native.js.snap delete mode 100644 packages/edit-site/src/components/code-editor/code-editor-text-area.js delete mode 100644 packages/edit-site/src/components/keyboard-shortcuts/index.js rename packages/edit-site/src/components/page-actions/{delete-page-menu-item.js => trash-page-menu-item.js} (59%) delete mode 100644 packages/edit-site/src/components/page-content-focus-manager/constants.js create mode 100644 packages/edit-site/src/components/page-patterns/duplicate-menu-item.js create mode 100644 packages/edit-site/src/components/page-patterns/header.js create mode 100644 packages/edit-site/src/components/page-patterns/pagination.js create mode 100644 packages/edit-site/src/components/page-patterns/rename-menu-item.js create mode 100644 packages/edit-site/src/components/page-template-parts/add-new-template-part.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js create mode 100644 packages/edit-site/src/utils/normalize-record-key.js create mode 100644 packages/editor/src/components/global-keyboard-shortcuts/index.js delete mode 100644 packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js delete mode 100644 packages/editor/src/components/global-keyboard-shortcuts/text-editor-shortcuts.js delete mode 100644 packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js delete mode 100644 packages/editor/src/components/post-text-editor/test/index.js create mode 100644 packages/interactivity/CHANGELOG.md create mode 100644 packages/nux/.npmrc create mode 100644 packages/nux/CHANGELOG.md create mode 100644 packages/nux/README.md create mode 100644 packages/nux/package.json create mode 100644 packages/nux/src/components/dot-tip/README.md create mode 100644 packages/nux/src/components/dot-tip/index.js create mode 100644 packages/nux/src/components/dot-tip/style.scss create mode 100644 packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap create mode 100644 packages/nux/src/components/dot-tip/test/index.js create mode 100644 packages/nux/src/index.js create mode 100644 packages/nux/src/store/actions.js create mode 100644 packages/nux/src/store/index.js create mode 100644 packages/nux/src/store/reducer.js create mode 100644 packages/nux/src/store/selectors.js create mode 100644 packages/nux/src/store/test/actions.js create mode 100644 packages/nux/src/store/test/reducer.js create mode 100644 packages/nux/src/store/test/selectors.js create mode 100644 packages/nux/src/style.scss delete mode 100644 packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js create mode 100644 packages/react-native-editor/android/app/src/main/res/drawable/more_vertical.xml create mode 100644 packages/react-native-editor/android/app/src/main/res/drawable/redo.xml create mode 100644 packages/react-native-editor/android/app/src/main/res/drawable/undo.xml create mode 100644 packages/react-native-editor/android/app/src/main/res/menu/toolbar_menu.xml create mode 100644 packages/react-native-editor/android/app/src/main/res/values-night/colors_dark.xml create mode 100644 packages/react-native-editor/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/react-native-editor/android/app/src/main/res/values/colors.xml create mode 100644 packages/react-native-editor/android/app/src/main/res/values/styles_toolbar.xml create mode 100644 packages/react-native-editor/ios/Colors.xcassets/Contents.json create mode 100644 packages/react-native-editor/ios/Colors.xcassets/HeaderLine.colorset/Contents.json create mode 100644 packages/react-native-editor/ios/Colors.xcassets/Primary.colorset/Contents.json create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/more.imageset/Contents.json create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/more.imageset/editor-more.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/more.imageset/editor-more@2x.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/more.imageset/editor-more@3x.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/redo.imageset/Contents.json create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/redo.imageset/redo.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/redo.imageset/redo@2x.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/redo.imageset/redo@3x.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/undo.imageset/Contents.json create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/undo.imageset/undo.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/undo.imageset/undo@2x.png create mode 100644 packages/react-native-editor/ios/GutenbergDemo/Images.xcassets/undo.imageset/undo@3x.png create mode 100644 packages/reusable-blocks/src/lock-unlock.js create mode 100644 phpunit/fonts/wp-font-face-testcase.php create mode 100644 phpunit/fonts/wp-font-face-tests-dataset.php create mode 100644 phpunit/fonts/wpFontFace/generateAndPrint-test.php create mode 100644 phpunit/fonts/wpFontFaceResolver/getFontsFromThemeJson-test.php create mode 100644 phpunit/fonts/wpPrintFontFaces-test.php create mode 100644 test/e2e/specs/editor/blocks/navigation-colors.spec.js create mode 100644 test/e2e/specs/editor/blocks/navigation-frontend-interactivity.spec.js create mode 100644 test/e2e/specs/editor/blocks/navigation-list-view.spec.js create mode 100644 test/e2e/specs/editor/various/adding-inline-tokens.spec.js create mode 100644 test/e2e/specs/interactivity/directives-style.spec.ts create mode 100644 test/e2e/specs/site-editor/hybrid-theme.spec.js create mode 100644 test/e2e/specs/site-editor/multi-entity-saving.spec.js create mode 100644 test/gutenberg-test-themes/behaviors-enabled/templates/single.html diff --git a/.eslintrc.js b/.eslintrc.js index 9933b831e4725a..eb5b2c6dccf201 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -44,123 +44,7 @@ const restrictedImports = [ }, { name: 'lodash', - importNames: [ - 'camelCase', - 'capitalize', - 'castArray', - 'chunk', - 'clamp', - 'clone', - 'cloneDeep', - 'compact', - 'concat', - 'countBy', - 'debounce', - 'deburr', - 'defaults', - 'defaultTo', - 'delay', - 'difference', - 'differenceWith', - 'dropRight', - 'each', - 'escape', - 'escapeRegExp', - 'every', - 'extend', - 'filter', - 'find', - 'findIndex', - 'findKey', - 'findLast', - 'first', - 'flatMap', - 'flatten', - 'flattenDeep', - 'flow', - 'flowRight', - 'forEach', - 'fromPairs', - 'groupBy', - 'has', - 'identity', - 'includes', - 'invoke', - 'isArray', - 'isBoolean', - 'isEmpty', - 'isEqual', - 'isFinite', - 'isFunction', - 'isMatch', - 'isNil', - 'isNumber', - 'isObject', - 'isObjectLike', - 'isPlainObject', - 'isString', - 'isUndefined', - 'kebabCase', - 'keyBy', - 'keys', - 'last', - 'lowerCase', - 'map', - 'mapKeys', - 'mapValues', - 'maxBy', - 'memoize', - 'merge', - 'mergeWith', - 'negate', - 'noop', - 'nth', - 'omit', - 'omitBy', - 'once', - 'orderby', - 'overEvery', - 'partial', - 'partialRight', - 'pick', - 'pickBy', - 'random', - 'reduce', - 'reject', - 'repeat', - 'reverse', - 'setWith', - 'size', - 'snakeCase', - 'some', - 'sortBy', - 'startCase', - 'startsWith', - 'stubFalse', - 'stubTrue', - 'sum', - 'sumBy', - 'take', - 'throttle', - 'times', - 'toString', - 'trim', - 'truncate', - 'unescape', - 'unionBy', - 'uniq', - 'uniqBy', - 'uniqueId', - 'uniqWith', - 'upperFirst', - 'values', - 'without', - 'words', - 'xor', - 'zip', - ], - message: - 'This Lodash method is not recommended. Please use native functionality instead. If using `memoize`, please use `memize` instead.', + message: 'Please use native functionality instead.', }, { name: 'reakit', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7ba9e884259ea4..4008efa1b4d4e1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -88,6 +88,7 @@ /packages/compose @ajitbohra /packages/element @ajitbohra /packages/notices @ajitbohra +/packages/nux @ajitbohra @peterwilsoncc /packages/viewport @ajitbohra /packages/base-styles /packages/icons diff --git a/.github/workflows/php-changes-detection.yml b/.github/workflows/php-changes-detection.yml new file mode 100644 index 00000000000000..be1e686483f5b3 --- /dev/null +++ b/.github/workflows/php-changes-detection.yml @@ -0,0 +1,100 @@ +name: OPTIONAL - Confirm if PHP changes require backporting to WordPress Core + +on: + pull_request: + types: [opened, synchronize] +jobs: + detect_php_changes: + name: Detect PHP changes + runs-on: ubuntu-latest + if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} + steps: + - name: Check out code + uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + with: + fetch-depth: 0 + + - name: Get changed PHP files + id: changed-files-php + uses: tj-actions/changed-files@v37 + with: + files: | + *.{php} + lib/** + phpunit/** + + - name: List all changed files + if: steps.changed-files-php.outputs.any_changed == 'true' + id: list-changed-php-files + run: | + echo "Changed files:" + formatted_change_list="" + for file in ${{ steps.changed-files-php.outputs.all_changed_files }}; do + echo "$file was changed" + formatted_change_list+="
:grey_question: $file" + done + formatted_change_list+="
" + echo "formatted_change_list=$formatted_change_list" >> $GITHUB_OUTPUT + + - name: Find Comment + uses: peter-evans/find-comment@v2 + id: find-comment + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: '' + + - name: Create comment + if: steps.find-comment.outputs.comment-id == '' && steps.changed-files-php.outputs.any_changed == 'true' + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + + This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress. + + If so, it is recommended to create a [new Trac ticket](https://core.trac.wordpress.org/newticket) and submit a pull request to the [WordPress Core Github repository](https://github.com/WordPress/wordpress-develop) soon after this pull request is merged. + + If you're unsure, you can always ask for help in the #core-editor channel in [WordPress Slack](https://make.wordpress.org/chat/). + + Thank you! :heart: + +
+ View changed files + ${{ steps.list-changed-php-files.outputs.formatted_change_list }} +
+ + - name: Update comment + if: steps.find-comment.outputs.comment-id != '' && steps.changed-files-php.outputs.any_changed == 'true' + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + + This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress. + + If so, it is recommended to create a [new Trac ticket](https://core.trac.wordpress.org/newticket) and submit a pull request to the [WordPress Core Github repository](https://github.com/WordPress/wordpress-develop) soon after this pull request is merged. + + If you're unsure, you can always ask for help in the #core-editor channel in [WordPress Slack](https://make.wordpress.org/chat/). + + Thank you! :heart: + +
+ View changed files + ${{ steps.list-changed-php-files.outputs.formatted_change_list }} +
+ + - name: Update comment + if: steps.find-comment.outputs.comment-id != '' && steps.changed-files-php.outputs.any_changed != 'true' + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + + This pull request changed or added PHP files in previous commits, but none have been detected in the latest commit. + + Thank you! :heart: diff --git a/.github/workflows/stale-issue-gardening.yml b/.github/workflows/stale-issue-gardening.yml index 078108a182850e..da823198ed806f 100644 --- a/.github/workflows/stale-issue-gardening.yml +++ b/.github/workflows/stale-issue-gardening.yml @@ -38,7 +38,7 @@ jobs: days-before-stale: 180 days-before-close: -1 remove-stale-when-updated: false - stale-issue-label: 'Needs Testing' + stale-issue-label: 'Needs check-in' steps: - name: Update issues diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index e222083a160e9a..09494f0d514ad4 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -296,4 +296,4 @@ jobs: run: npx lerna run build - name: Running the tests - run: npm run native test -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" + run: npm run test:native -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" diff --git a/.gitignore b/.gitignore index 19e43aecea7b82..b44eabe00cccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ yarn.lock Thumbs.db # Report generated from jest-junit -test/native/junit.xml +junit.xml # Local overrides .wp-env.override.json diff --git a/bin/cherry-pick.mjs b/bin/cherry-pick.mjs index 36829db6ee5cf8..baf8d42962f8e8 100644 --- a/bin/cherry-pick.mjs +++ b/bin/cherry-pick.mjs @@ -114,12 +114,25 @@ async function fetchPRs() { const { items } = await GitHubFetch( `/search/issues?q=is:pr state:closed sort:updated label:"${ LABEL }" repo:WordPress/gutenberg` ); - const PRs = items.map( ( { id, number, title } ) => ( { + const PRs = items.map( ( { id, number, title, pull_request, closed_at } ) => ( { id, number, title, - } ) ); - console.log( 'Found the following PRs to cherry-pick: ' ); + closed_at, + pull_request, + } ) ).sort( ( a, b ) => { + /* + * `closed_at` and `pull_request.merged_at` are _usually_ the same, + * but let's prefer the latter if it's available. + */ + if ( a?.pull_request?.merged_at && b?.pull_request?.merged_at ) { + return new Date( a?.pull_request?.merged_at ) - new Date( b?.pull_request?.merged_at ); + } + return new Date( a.closed_at ) - new Date( b.closed_at ); + } ); + + + console.log( 'Found the following PRs to cherry-pick (sorted by closed date in ascending order): ' ); PRs.forEach( ( { number, title } ) => console.log( indent( `#${ number } – ${ title }` ) ) ); diff --git a/bin/test-create-block.sh b/bin/test-create-block.sh index e17bdbb2d66946..7959334a8e30ec 100755 --- a/bin/test-create-block.sh +++ b/bin/test-create-block.sh @@ -55,7 +55,7 @@ if [ "$expected" -ne "$actual" ]; then error "Expected $expected files in the project root, but found $actual." exit 1 fi -expected=6 +expected=7 actual=$( find src -maxdepth 1 -type f | wc -l ) if [ "$expected" -ne "$actual" ]; then error "Expected $expected files in the \`src\` directory, but found $actual." @@ -69,7 +69,7 @@ status "Building block..." ../node_modules/.bin/wp-scripts build status "Verifying build..." -expected=5 +expected=7 actual=$( find build -maxdepth 1 -type f | wc -l ) if [ "$expected" -ne "$actual" ]; then error "Expected $expected files in the \`build\` directory, but found $actual." diff --git a/changelog.txt b/changelog.txt index e011668bc7e00c..ede5cff91e4eda 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,1013 @@ == Changelog == += 16.3.0-rc.1 = + + + +## Changelog + +### Enhancements + +#### Site Editor +- Edit Site: Add delay and fade-in animation to loading spinner. ([51902](https://github.com/WordPress/gutenberg/pull/51902)) +- Make "My patterns" category permanently visible. ([52531](https://github.com/WordPress/gutenberg/pull/52531)) +- Remove "Theme patterns" heading in Pattern library. ([52570](https://github.com/WordPress/gutenberg/pull/52570)) +- Remove sidebar group descriptions. ([52453](https://github.com/WordPress/gutenberg/pull/52453)) +- Show warning on removal of Post Template block in the site editor. ([52666](https://github.com/WordPress/gutenberg/pull/52666)) +- Swap pattern creation options. ([52726](https://github.com/WordPress/gutenberg/pull/52726)) +- Update Dashboard button tooltips in the site editor. ([52465](https://github.com/WordPress/gutenberg/pull/52465)) +- Update Site Editor frame z-index. ([52180](https://github.com/WordPress/gutenberg/pull/52180)) +- Update descriptions in Pattern library. ([52468](https://github.com/WordPress/gutenberg/pull/52468)) +- Update locked pattern tooltips. ([52497](https://github.com/WordPress/gutenberg/pull/52497)) +- Update navigation menu title size & weight in detail panels. ([52477](https://github.com/WordPress/gutenberg/pull/52477)) +- Update pattern library copy. ([52340](https://github.com/WordPress/gutenberg/pull/52340)) +- Update: Show more intuitive archive titles on Query Title block. ([52521](https://github.com/WordPress/gutenberg/pull/52521)) + +#### Patterns +- Add hint to show template part move. ([52395](https://github.com/WordPress/gutenberg/pull/52395)) +- Add renaming, duplication, and deletion options. ([52270](https://github.com/WordPress/gutenberg/pull/52270)) +- Add sync tooltip. ([52458](https://github.com/WordPress/gutenberg/pull/52458)) +- Display all custom template part areas in sidebar nav. ([52355](https://github.com/WordPress/gutenberg/pull/52355)) +- Don't override the rootClientID in create menu - only set if undefined. ([52713](https://github.com/WordPress/gutenberg/pull/52713)) +- Enable focus mode editing. ([52427](https://github.com/WordPress/gutenberg/pull/52427)) +- Remove `reusable` text from menu once rename hint has been dismissed. ([52664](https://github.com/WordPress/gutenberg/pull/52664)) +- Stop endless snackbars appearing. ([52012](https://github.com/WordPress/gutenberg/pull/52012)) +- Try: Sticky header and pagination on Patterns page. ([52663](https://github.com/WordPress/gutenberg/pull/52663)) +- Update manage pattern links to go to site editor if available. ([52403](https://github.com/WordPress/gutenberg/pull/52403)) +- [Patterns] Separate sync status into a filter control. ([52303](https://github.com/WordPress/gutenberg/pull/52303)) + +#### Components +- Adapt template part hint copy. ([52527](https://github.com/WordPress/gutenberg/pull/52527)) +- Adding support for defined IDs in `TextControl` component. ([52028](https://github.com/WordPress/gutenberg/pull/52028)) +- Updated "position" default value. ([52148](https://github.com/WordPress/gutenberg/pull/52148)) + +#### Interactivity API +- Add filter to turn off Interactivity API for a block. ([52579](https://github.com/WordPress/gutenberg/pull/52579)) +- Add runtime support for the `wp-style` directive. ([52645](https://github.com/WordPress/gutenberg/pull/52645)) + +#### Block Library +- Add back old Navigation and File blocks JavaScript implementation when Gutenberg is not installed. ([52553](https://github.com/WordPress/gutenberg/pull/52553)) +- Home link block: Add 'current-menu-item'. ([51478](https://github.com/WordPress/gutenberg/pull/51478)) + +#### Block Editor +- Add maxLength to LinkControl search item URLs. ([52523](https://github.com/WordPress/gutenberg/pull/52523)) +- i18n: Make the tab labels of `ColorGradientSettingsDropdown` component translatable. ([52669](https://github.com/WordPress/gutenberg/pull/52669)) + +#### NUX +- Restore `@wordpress/nux` to trunk. ([52455](https://github.com/WordPress/gutenberg/pull/52455)) + +#### Block API +- Block Editor: Add support for arrays to `setImmutably` util. ([52280](https://github.com/WordPress/gutenberg/pull/52280)) + +#### Inspector Controls +- Use next40pxDefaultSize on RangeControl components. ([52257](https://github.com/WordPress/gutenberg/pull/52257)) + +#### Fonts API +- Font Face: To generate and print font-face styles for theme.json fonts. ([51770](https://github.com/WordPress/gutenberg/pull/51770)) + + +### New APIs + +#### Nested / Inner Blocks +- Stabilize `defaultBlock`, `directInsert` API's and `getDirectInsertBlock` selector. ([52083](https://github.com/WordPress/gutenberg/pull/52083)) + + +### Bug Fixes + +#### Patterns +- Alternative grid layout to improve keyboard accessibility. ([52357](https://github.com/WordPress/gutenberg/pull/52357)) +- Add handling of sync status to the wp-admin patterns list page. ([52346](https://github.com/WordPress/gutenberg/pull/52346)) +- Add missing decoding entities processing in Patterns and Template/Parts pages. ([52449](https://github.com/WordPress/gutenberg/pull/52449)) +- Add option to set sync status when adding from wp-admin patterns list. ([52352](https://github.com/WordPress/gutenberg/pull/52352)) +- Avoid copying global style presets via the styles compatibility hook. ([52640](https://github.com/WordPress/gutenberg/pull/52640)) +- Command Palette: Update label and icon for Patterns. ([52742](https://github.com/WordPress/gutenberg/pull/52742)) +- Ensure that the unsaved title is not persisted when reopening the modal. ([52473](https://github.com/WordPress/gutenberg/pull/52473)) +- Fix bug with Create Patterns menu not showing in site editor page editing. ([52671](https://github.com/WordPress/gutenberg/pull/52671)) +- Fix renaming in Site View sidebar rename saves all edits for Template Parts and Navigation Menus. ([52373](https://github.com/WordPress/gutenberg/pull/52373)) +- Fix: Patterns & template parts: Remove "apply globally" option from block settings. ([52160](https://github.com/WordPress/gutenberg/pull/52160)) +- Rename edit label to Edit Block Pattern to resolve edge case in Chrome. ([52496](https://github.com/WordPress/gutenberg/pull/52496)) +- Show uncategorized patterns on the Editor > Patterns page. ([52633](https://github.com/WordPress/gutenberg/pull/52633)) +- Site Editor Patterns: Filter out patterns that are not available in the inserter. ([52675](https://github.com/WordPress/gutenberg/pull/52675)) +- Update the title of Pattern block in the block inspector card. ([52010](https://github.com/WordPress/gutenberg/pull/52010)) + +#### Site Editor +- Edit Site: Fix the pattern with the post types becomes the placeholder pattern when editing template part. ([52503](https://github.com/WordPress/gutenberg/pull/52503)) +- Fix "Manage all patterns" link appearance. ([52532](https://github.com/WordPress/gutenberg/pull/52532)) +- Fix document title icon appearance. ([52424](https://github.com/WordPress/gutenberg/pull/52424)) +- Fix entering edit mode in site editor. ([52406](https://github.com/WordPress/gutenberg/pull/52406)) +- Fix missing Add Template Part button in Template Parts page. ([52542](https://github.com/WordPress/gutenberg/pull/52542)) +- Fix undo/redo in site editor code editor's mode. ([52695](https://github.com/WordPress/gutenberg/pull/52695)) +- Remove status icon. ([52457](https://github.com/WordPress/gutenberg/pull/52457)) +- Reset device preview type when exiting the editing mode. ([52566](https://github.com/WordPress/gutenberg/pull/52566)) +- ResizableFrame: Fix styling in Firefox. ([52700](https://github.com/WordPress/gutenberg/pull/52700)) +- Site Editor Pages: Load the appropriate template if posts page set. ([52266](https://github.com/WordPress/gutenberg/pull/52266)) +- Site Editor Patterns: Ensure sidebar does not shrink when long pattern titles are used. ([52547](https://github.com/WordPress/gutenberg/pull/52547)) +- Use lowercase p in in "Manage Patterns". ([52617](https://github.com/WordPress/gutenberg/pull/52617)) +- Do not navigate to the styles pages unless you're in a random listing page. ([52728](https://github.com/WordPress/gutenberg/pull/52728)) +- Fix multiple navigation blocks in pattern template. ([52707](https://github.com/WordPress/gutenberg/pull/52707)) + +#### Block Library +- Fix console warning by improving error handling in Nav block classic menu conversion. ([52591](https://github.com/WordPress/gutenberg/pull/52591)) +- Fix importing classic menus. ([52573](https://github.com/WordPress/gutenberg/pull/52573)) +- Fix: Focus loss on navigation link label editing on Firefox. ([52428](https://github.com/WordPress/gutenberg/pull/52428)) +- List block: Fix selected numbering style option. ([52472](https://github.com/WordPress/gutenberg/pull/52472)) +- Post Title: The changes should be reflected when previewing a post. ([52369](https://github.com/WordPress/gutenberg/pull/52369)) +- Quote block: Add transform to paragraph. ([51809](https://github.com/WordPress/gutenberg/pull/51809)) +- Remove synced blocks from lastBlockInserted. ([52558](https://github.com/WordPress/gutenberg/pull/52558)) +- Rich Text/Footnotes: Fix getRichTextValues for useInnerBlocksProps.save. ([52682](https://github.com/WordPress/gutenberg/pull/52682)) +- Search block: Enqueue view script through block.json. ([52552](https://github.com/WordPress/gutenberg/pull/52552)) +- Use `_get_block_template_file` function and set $area variable. ([52708](https://github.com/WordPress/gutenberg/pull/52708)) + +#### Accessibility +- Change Delete page menu item to Move to trash. ([52641](https://github.com/WordPress/gutenberg/pull/52641)) +- Change password input to type text so contents are visible. ([52622](https://github.com/WordPress/gutenberg/pull/52622)) +- Do not autofocus page title field in the 'Draft a new page' modal dialog. ([52603](https://github.com/WordPress/gutenberg/pull/52603)) +- Fix Shift+Tab to Block Toolbar. ([52613](https://github.com/WordPress/gutenberg/pull/52613)) +- Item: Unify focus style and add default font styles. ([52495](https://github.com/WordPress/gutenberg/pull/52495)) +- Navigation block: Add notice on reduced accessibility. ([52251](https://github.com/WordPress/gutenberg/pull/52251)) +- Password protected field: Remove autofocus and improve placeholder text consistency. ([52634](https://github.com/WordPress/gutenberg/pull/52634)) +- ResizableFrame: Make keyboard accessible. ([52443](https://github.com/WordPress/gutenberg/pull/52443)) +- Site Editor: Fix navigation menu sidebar actions order and label. ([52592](https://github.com/WordPress/gutenberg/pull/52592)) + +#### Block Editor +- Add 'reusable' keyword to Pattern blocks. ([52543](https://github.com/WordPress/gutenberg/pull/52543)) +- Avoid errors in Dimension visualizers when switching between iframed and non-iframed editors. ([52588](https://github.com/WordPress/gutenberg/pull/52588)) +- Ensure synced patterns are accounted for in 'getAllowedBlocks'. ([52546](https://github.com/WordPress/gutenberg/pull/52546)) +- Fix: Remove link action of Link UI for draft pages created from Nav block does not correctly remove link. ([52415](https://github.com/WordPress/gutenberg/pull/52415)) +- LinkControl: Add width to ensure ellipsis truncating works. ([52575](https://github.com/WordPress/gutenberg/pull/52575)) +- LinkControl: Fix mark highlight to bold. ([52517](https://github.com/WordPress/gutenberg/pull/52517)) +- Post Content link color should not be applied to placeholder component links. ([52367](https://github.com/WordPress/gutenberg/pull/52367)) +- Fix highlight change when using transform menu. ([52752](https://github.com/WordPress/gutenberg/pull/52752)) +- Fix: Apply text color selection to link color. ([52379](https://github.com/WordPress/gutenberg/pull/52379)) + +#### Components +- Block Editor: Display variation icon in the 'BlockDraggable' component. ([52502](https://github.com/WordPress/gutenberg/pull/52502)) +- Copy tweak for the reusable block rename hint. ([52581](https://github.com/WordPress/gutenberg/pull/52581)) +- Fix react-dropdown-menu version to avoid breaking change from one of …. ([52356](https://github.com/WordPress/gutenberg/pull/52356)) +- FormTokenField: Fix token overflow when moving cursor left or right. ([52662](https://github.com/WordPress/gutenberg/pull/52662)) +- Site Editor: Make sidebar back button go *back* instead of *up* if possible. ([52456](https://github.com/WordPress/gutenberg/pull/52456)) +- Top Toolbar: Move the preferences selection into the main useSelect. ([52332](https://github.com/WordPress/gutenberg/pull/52332)) + +#### Post Editor +- Editor: Remove a block select button from the multi-entity saving flow. ([52753](https://github.com/WordPress/gutenberg/pull/52753)) +- Fix Site editor page when JS support is disabled. ([52376](https://github.com/WordPress/gutenberg/pull/52376)) +- Fix initial block parsing. ([52417](https://github.com/WordPress/gutenberg/pull/52417)) +- Simplify the code editor of edit-post. ([52751](https://github.com/WordPress/gutenberg/pull/52751)) + +#### Global Styles +- DimensionsPanel: Fix unexpected value decoding/encoding. ([52661](https://github.com/WordPress/gutenberg/pull/52661)) +- Global font sizes: Ensure sizes are unique. ([52483](https://github.com/WordPress/gutenberg/pull/52483)) +- Spacing Sizes: Fix zero size typo and to be output directly. ([52711](https://github.com/WordPress/gutenberg/pull/52711)) + +#### Themes +- Fix admin_url() for preview link of block themes. ([52399](https://github.com/WordPress/gutenberg/pull/52399)) +- Site Editor: Don't allow creating template part on the Patterns page for non-block themes. ([52656](https://github.com/WordPress/gutenberg/pull/52656)) + +#### Fonts API +- Deprecate and make Fonts API non-functional. ([52485](https://github.com/WordPress/gutenberg/pull/52485)) +- Fix font printing regression when metabox exists. ([52343](https://github.com/WordPress/gutenberg/pull/52343)) + +#### Extensibility +- Page Content Focus: Ignore page content within a Query Loop block. ([52351](https://github.com/WordPress/gutenberg/pull/52351)) + +#### Page Content Focus +- Exit template focus when opening the W menu. ([52235](https://github.com/WordPress/gutenberg/pull/52235)) + +#### Block Validation/Deprecation +- Cover Block: Fix block deprecation when fixed background is enabled. ([51612](https://github.com/WordPress/gutenberg/pull/51612)) + + +### Performance + +#### Post Editor +- Fix performance regression for isEditedPostEmpty selector. ([52759](https://github.com/WordPress/gutenberg/pull/52759)) + +#### Patterns +- Add client side pagination to patterns list. ([52538](https://github.com/WordPress/gutenberg/pull/52538)) + +#### Site Editor +- Fix entity cache misses for single posts due to string as recordKey. ([52338](https://github.com/WordPress/gutenberg/pull/52338)) + +#### Block Library +- Experimental: Disable TinyMCE compat with classic editor. ([52325](https://github.com/WordPress/gutenberg/pull/52325)) + + +### Experiments + +#### Project Management +- Github workflow: Add a PHP backport changes action. ([52096](https://github.com/WordPress/gutenberg/pull/52096)) + +#### Interactivity API +- Prevent scripts from loading if behaviors are not used. ([52140](https://github.com/WordPress/gutenberg/pull/52140)) + + +### Documentation + +- (readme.md) Document the new process for releasing point releases for old release branches. ([49968](https://github.com/WordPress/gutenberg/pull/49968)) +- Add layout API documentation. ([52673](https://github.com/WordPress/gutenberg/pull/52673)) +- Added README for the "caption" component. ([52033](https://github.com/WordPress/gutenberg/pull/52033)) +- Added documentation text-transform component #52072. ([52243](https://github.com/WordPress/gutenberg/pull/52243)) +- Block Editor: Add README for `BlockControls`. ([52366](https://github.com/WordPress/gutenberg/pull/52366)) +- Lodash: Remove completely. ([52571](https://github.com/WordPress/gutenberg/pull/52571)) +- Removed line break within the code block. ([46920](https://github.com/WordPress/gutenberg/pull/46920)) +- Theme JSON schema: Add missing block names and unify block properties. ([51293](https://github.com/WordPress/gutenberg/pull/51293)) +- Update layout architecture documentation after stabilisation. ([52316](https://github.com/WordPress/gutenberg/pull/52316)) + + +### Code Quality + +- Add missing `@emotion/react` dep to block-editor. ([52475](https://github.com/WordPress/gutenberg/pull/52475)) +- Code Data: Fix ESLint warning for 'useEntityProp' hook. ([52757](https://github.com/WordPress/gutenberg/pull/52757)) +- Lodash: Deprecate `_.set()`. ([52407](https://github.com/WordPress/gutenberg/pull/52407)) +- Lodash: Remove remaining `_.get()` from block editor and deprecate. ([52561](https://github.com/WordPress/gutenberg/pull/52561)) +- Make use of accessing private APIs from thunks directly. ([52214](https://github.com/WordPress/gutenberg/pull/52214)) + +#### Block Library +- Footnotes: Backport core changes to excerpt trim. ([52709](https://github.com/WordPress/gutenberg/pull/52709)) +- Navigation: Simplify the useSelect for useNavigationMenus. ([51977](https://github.com/WordPress/gutenberg/pull/51977)) +- Query Pagination: Set 'clientId' as useSelect dependency. ([52358](https://github.com/WordPress/gutenberg/pull/52358)) +- Search: Remove unnecessary useEffect. ([52604](https://github.com/WordPress/gutenberg/pull/52604)) +- Template Parts: Set attributes 'area' as useSelect dependency. ([52330](https://github.com/WordPress/gutenberg/pull/52330)) + +#### Global Styles +- Lodash: Refactor away from `_.set()` in `PushChangesToGlobalStylesControl`. ([52404](https://github.com/WordPress/gutenberg/pull/52404)) +- Lodash: Refactor away from `_.set()` in `getNodesWithSettings()`. ([52278](https://github.com/WordPress/gutenberg/pull/52278)) +- Lodash: Refactor away from `_.set()` in global styles. ([52279](https://github.com/WordPress/gutenberg/pull/52279)) +- Lodash: Remove completely from site editor. ([52480](https://github.com/WordPress/gutenberg/pull/52480)) + +#### Post Editor +- EntityRecordItem: Fix ESLint warnings and remove unnecessary memoization. ([52630](https://github.com/WordPress/gutenberg/pull/52630)) +- PostPreviewButton: Rewrite to functional, avoid state transitions in lifecycles. ([44971](https://github.com/WordPress/gutenberg/pull/44971)) +- correct a typo: Sapce -> space. ([52578](https://github.com/WordPress/gutenberg/pull/52578)) + +#### Site Editor +- Fix incorrect 'useSelect' usage. ([52683](https://github.com/WordPress/gutenberg/pull/52683)) +- SiteHub: Combine site data selector hooks. ([52567](https://github.com/WordPress/gutenberg/pull/52567)) + +#### Reusable Blocks +- Update package to use relative path. ([52712](https://github.com/WordPress/gutenberg/pull/52712)) + + +### Tools + +#### Testing +- Iframe: Silence style compat warnings when in a BlockPreview. ([52627](https://github.com/WordPress/gutenberg/pull/52627)) +- Make `query` optional in `visitAdminPage`. ([52413](https://github.com/WordPress/gutenberg/pull/52413)) +- Migrate Adding Inline Token test to Playwright. ([52020](https://github.com/WordPress/gutenberg/pull/52020)) +- Migrate site editor multi-entity save flow tests. ([52372](https://github.com/WordPress/gutenberg/pull/52372)) +- Move request utils reset to global setup. ([52412](https://github.com/WordPress/gutenberg/pull/52412)) +- Nav block: Link text color inheritance fixes and tests. ([51710](https://github.com/WordPress/gutenberg/pull/51710)) +- Refactor and split navigation block end-to-end tests into separate files. ([52647](https://github.com/WordPress/gutenberg/pull/52647)) +- Try fixing block context end-to-end test failure. ([52513](https://github.com/WordPress/gutenberg/pull/52513)) +- Use posts instead of template parts for navigation color tests. ([52654](https://github.com/WordPress/gutenberg/pull/52654)) +- end-to-end Test Utils: Improve test reliability in plugins/themes and login procedures. ([52144](https://github.com/WordPress/gutenberg/pull/52144)) +- test: Enable jest-watch-typeahead for native tests. ([51869](https://github.com/WordPress/gutenberg/pull/51869)) +- test: Expand mobile editor tests. ([52446](https://github.com/WordPress/gutenberg/pull/52446)) + +#### Build Tooling +- Backport tools: Sort PRs to be cherry picked by merged/closed date. ([52667](https://github.com/WordPress/gutenberg/pull/52667)) +- Create block interactive template. ([52612](https://github.com/WordPress/gutenberg/pull/52612)) +- Fix Webpack to watch the `interactivity` package files. ([52642](https://github.com/WordPress/gutenberg/pull/52642)) +- Update caniuse-lite, browserslist and core-js. ([52420](https://github.com/WordPress/gutenberg/pull/52420)) + +#### Project Management +- Update issue gardening automation with new label. ([52173](https://github.com/WordPress/gutenberg/pull/52173)) + +### Various + +- Revert "Update Changelog for 16.1.2". ([52433](https://github.com/WordPress/gutenberg/pull/52433)) + + +## First time contributors + +The following PRs were merged by first time contributors: + +- @andrewhayward: Adding support for defined IDs in `TextControl` component. ([52028](https://github.com/WordPress/gutenberg/pull/52028)) +- @ghorivipul97: Post Content link color should not be applied to placeholder component links. ([52367](https://github.com/WordPress/gutenberg/pull/52367)) +- @sethrubenstein: Stabilize `defaultBlock`, `directInsert` API's and `getDirectInsertBlock` selector. ([52083](https://github.com/WordPress/gutenberg/pull/52083)) + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @afercia @andrewhayward @andrewserong @anomiex @arthur791004 @BenjaminZekavica @bfintal @carolinan @Clorith @dcalhoun @derekblank @diegohaz @draganescu @ellatrix @fluiddot @fullofcaffeine @geriux @getdave @ghorivipul97 @glendaviesnz @hellofromtonya @jameskoster @jeryj @jorgefilipecosta @jsnajdr @juanmaguitar @kevin940726 @luisherranz @MaggieCabrera @Mamaduka @michalczaplinski @mirka @noisysocks @ntsekouras @peterwilsoncc @pooja-muchandikar @Presskopp @priethor @ramonjd @richtabor @SantosGuillamot @SavPhill @SaxonF @scruffian @sethrubenstein @spacedmonkey @swissspidy @t-hamano @tellthemachines @tyxla @walbo @westonruter @youknowriad + + += 16.2.1 = + +## Changelog + +### Enhancements + +#### Footnotes +- Footnotes: show in inserter and placeholder. ([52445](https://github.com/WordPress/gutenberg/pull/52445)) + +### Bug Fixes + +#### Patterns +- Make Pattern title text clickable. ([52599](https://github.com/WordPress/gutenberg/pull/52599)) + +## Contributors + +The following contributors merged PRs in this release: + +@getdave @mcsf + + += 16.2.0 = + +## Changelog + +### Bug Fixes + +- Library - make pattern title clickable. ([51898](https://github.com/WordPress/gutenberg/pull/51898)) + +#### Block Library +- Allow editing existing footnote from RichText formats toolbar. ([52506](https://github.com/WordPress/gutenberg/pull/52506)) +- Revert "Post editor: Require confirmation before removing Footnotes (#52277)". ([52486](https://github.com/WordPress/gutenberg/pull/52486)) +- Trim footnote anchors from excerpts. ([52518](https://github.com/WordPress/gutenberg/pull/52518)) + +#### Interactivity API +- Fix md5 class messed up with new block key. ([52557](https://github.com/WordPress/gutenberg/pull/52557)) + +#### Block Editor +- Site Editor: Restore quick inserter 'Browse all' button. ([52529](https://github.com/WordPress/gutenberg/pull/52529)) + +#### Patterns +- Check that core hasn't already moved sync status meta before moving and unsetting. ([52494](https://github.com/WordPress/gutenberg/pull/52494)) + + +### Various + +- Rename block theme activation nonce variable. ([52398](https://github.com/WordPress/gutenberg/pull/52398)) + + + + +## Contributors + +The following contributors merged PRs in this release: + +@getdave @glendaviesnz @luisherranz @Mamaduka @mcsf @peterwilsoncc @priethor += 16.2.0-rc.3 = + +## Changelog + +### Bug Fixes + +#### Build Tooling +- Revert phpcs testVersion back to PHP 5.6. ([52384](https://github.com/WordPress/gutenberg/pull/52384)) + + += 16.2.0-rc.2 = + +## Changelog + +### Bug Fixes + +#### Block Library +- RichText/Footnotes: Make getRichTextValues work with InnerBlocks.Content. ([52241](https://github.com/WordPress/gutenberg/pull/52241)) + +#### Blocks +- Post and Comment Template blocks: Change render_block_context priority to 1 (https://github.com/WordPress/gutenberg/pull/52364) +- Footnotes: Fix incorrect anchor position in Firefox (https://github.com/WordPress/gutenberg/pull/52425) +- Footnotes: fix lingering format boundary attr (https://github.com/WordPress/gutenberg/pull/52439) +- Footnotes: save numbering through the entity provider (https://github.com/WordPress/gutenberg/pull/52423) + +#### Code Quality / Performance +- Iframe: avoid asset parsing & fix script localisation + +## Contributors + +The following contributors merged PRs in this release: + +@ellatrix @ockham @t-hamano + + += 16.2.0-rc.1 = + + + +## Changelog + +### Features + +#### Patterns +- feat: Rename Reusable blocks to Patterns. ([51704](https://github.com/WordPress/gutenberg/pull/51704)) + + +### Enhancements + +- Export store for the core/customize-widgets package. ([52189](https://github.com/WordPress/gutenberg/pull/52189)) + +#### Patterns +- Library: Add sync status to pattern details screen. ([51954](https://github.com/WordPress/gutenberg/pull/51954)) +- Rename Library to Patterns. ([52102](https://github.com/WordPress/gutenberg/pull/52102)) +- Update custom patterns label to 'My patterns'. ([51949](https://github.com/WordPress/gutenberg/pull/51949)) +- Update pattern creation modal in library. ([51946](https://github.com/WordPress/gutenberg/pull/51946)) +- Update template part icons in the library mosaic (grid items). ([51963](https://github.com/WordPress/gutenberg/pull/51963)) + +#### Site Editor +- Change "Home" template name to "Blog home". ([52048](https://github.com/WordPress/gutenberg/pull/52048)) +- Edit Site: Make loading spinner colors consistent. ([51857](https://github.com/WordPress/gutenberg/pull/51857)) +- Update the icon used to reference the blog. ([52075](https://github.com/WordPress/gutenberg/pull/52075)) + +#### Interactivity API +- Image block: Remove extra lookup for external image dimensions in lightbox. ([52178](https://github.com/WordPress/gutenberg/pull/52178)) +- Image block: Use built-in directive for mouseover event in lightbox. ([52067](https://github.com/WordPress/gutenberg/pull/52067)) + +#### Block Library +- Force full height for editor in Navigation focus mode. ([51798](https://github.com/WordPress/gutenberg/pull/51798)) +- Social links: Updating class and style attributes. ([51997](https://github.com/WordPress/gutenberg/pull/51997)) + +#### Themes +- Add border theme_support. ([51777](https://github.com/WordPress/gutenberg/pull/51777)) +- Add link color theme_support. ([51775](https://github.com/WordPress/gutenberg/pull/51775)) + +#### Global Styles +- Style Book: Show tabs and make blocks clickable when entering edit mode from the Styles menu. ([52222](https://github.com/WordPress/gutenberg/pull/52222)) + +#### Widgets Editor +- Add @example tags to the customize-widgets package. ([52141](https://github.com/WordPress/gutenberg/pull/52141)) + +#### NUX +- Page Content Focus: Add welcome guides. ([52014](https://github.com/WordPress/gutenberg/pull/52014)) + +#### Block Editor +- Use block label and icon for the inserter draggable chip.. ([51048](https://github.com/WordPress/gutenberg/pull/51048)) + +#### Design Tools +- Add Typography: Text orientation (writing mode). ([50822](https://github.com/WordPress/gutenberg/pull/50822)) + +#### Components +- RangeControl: Add support for large 40px number input size. ([49105](https://github.com/WordPress/gutenberg/pull/49105)) + + +### New APIs + +#### Block Editor +- Add new `registerInserterMediaCategory` API to make media categories extensible. ([51542](https://github.com/WordPress/gutenberg/pull/51542)) + + +### Bug Fixes + +- Adjust the position of sticky headings in preferences modal. ([52248](https://github.com/WordPress/gutenberg/pull/52248)) +- BlockRemovalWarningModal: Fix incorrect '_n' usage. ([52164](https://github.com/WordPress/gutenberg/pull/52164)) +- Editor initrial appender: Zero out margins in constrained layouts. ([52026](https://github.com/WordPress/gutenberg/pull/52026)) +- Export store from the edit-site package. ([51986](https://github.com/WordPress/gutenberg/pull/51986)) +- Fix disable DFM when opening styles command. ([52165](https://github.com/WordPress/gutenberg/pull/52165)) +- Fix unintentional toggling on of distraction free. ([52090](https://github.com/WordPress/gutenberg/pull/52090)) +- Footnotes: Increase selector specificity for anchor. ([52179](https://github.com/WordPress/gutenberg/pull/52179)) +- Respect custom aspect ratio. ([52286](https://github.com/WordPress/gutenberg/pull/52286)) +- Turn off DFM for style book and style editing. ([52117](https://github.com/WordPress/gutenberg/pull/52117)) +- Update fixed block toolbar. ([52123](https://github.com/WordPress/gutenberg/pull/52123)) +- Updating the BlockEditorProvider settings prop should reset the store's settings entirely. ([51904](https://github.com/WordPress/gutenberg/pull/51904)) +- [Command Palette]: Remove suggestion for deleting templates/parts. ([52168](https://github.com/WordPress/gutenberg/pull/52168)) +- [Command center]: Add preferences and keyboard shortcuts commands. ([51862](https://github.com/WordPress/gutenberg/pull/51862)) +- [Edit Post]: Add toggle fullscreen mode and list view commands. ([52184](https://github.com/WordPress/gutenberg/pull/52184)) + +#### Block Library +- Fix default block dimensions visibility. ([52256](https://github.com/WordPress/gutenberg/pull/52256)) +- Fix fetching Nav fallback ID flushing Navigation entity cache. ([52069](https://github.com/WordPress/gutenberg/pull/52069)) +- Fix flaky tests in `navigation.spec.js` and other tests related to the Post Editor Template mode. ([51790](https://github.com/WordPress/gutenberg/pull/51790)) +- Fix: Term Description block should only be available in the site editor. ([51053](https://github.com/WordPress/gutenberg/pull/51053)) +- Footnotes: Register meta field for pages. ([52024](https://github.com/WordPress/gutenberg/pull/52024)) +- Image block: Fix cursor style when lightbox is opened. ([52187](https://github.com/WordPress/gutenberg/pull/52187)) +- Navigation: Add the draft status to the navigation title. ([51967](https://github.com/WordPress/gutenberg/pull/51967)) +- Navigation: Fix end-to-end test failures caused by sidebar title change. ([52308](https://github.com/WordPress/gutenberg/pull/52308)) +- Navigation: Fix sidebar title. ([52167](https://github.com/WordPress/gutenberg/pull/52167)) +- Navigation: Remove one preloaded endpoint. ([52115](https://github.com/WordPress/gutenberg/pull/52115)) +- Page List: Fix parent block selection when converting to link. ([52193](https://github.com/WordPress/gutenberg/pull/52193)) +- Post editor: Require confirmation before removing Footnotes. ([52277](https://github.com/WordPress/gutenberg/pull/52277)) +- fix: Display heading level dropdown icons and labels. ([52004](https://github.com/WordPress/gutenberg/pull/52004)) + +#### Site Editor +- Add confirmation step when deleting a Template. ([52236](https://github.com/WordPress/gutenberg/pull/52236)) +- Command Palette: Fix incorrect path and snackbar message when template part is deleted. ([52034](https://github.com/WordPress/gutenberg/pull/52034)) +- Default to showing status slug in sidebar. ([52226](https://github.com/WordPress/gutenberg/pull/52226)) +- Fix missing MenuGroup segment in Site Editor header more menu. ([51860](https://github.com/WordPress/gutenberg/pull/51860)) +- Fix missing snackbars in Library. ([52021](https://github.com/WordPress/gutenberg/pull/52021)) +- Fix stepper styling in Home template sidebar. ([52025](https://github.com/WordPress/gutenberg/pull/52025)) +- Get the top toolbar preference from the correct scope. ([51840](https://github.com/WordPress/gutenberg/pull/51840)) +- Hide word count and reading time meta data for the Posts Page details panel. ([52186](https://github.com/WordPress/gutenberg/pull/52186)) +- Modal: Add small top padding to the content so that avoid cutting off the visible outline when hovering items. ([51829](https://github.com/WordPress/gutenberg/pull/51829)) +- Site Editor Frame: Ignore Spotlight in view mode. ([52262](https://github.com/WordPress/gutenberg/pull/52262)) +- Try restoring the site editor animation. ([51956](https://github.com/WordPress/gutenberg/pull/51956)) + +#### Patterns +- Fix custom patterns console error. ([51947](https://github.com/WordPress/gutenberg/pull/51947)) +- Fix history back after entering edit mode from Patterns. ([52112](https://github.com/WordPress/gutenberg/pull/52112)) +- Fix setting of sync status for fully synced patterns. ([51952](https://github.com/WordPress/gutenberg/pull/51952)) +- Fix sidebar tab label. ([51953](https://github.com/WordPress/gutenberg/pull/51953)) +- Fix: Pattern focus mode DocumentActions should use the pattern icon. ([52031](https://github.com/WordPress/gutenberg/pull/52031)) +- Include template parts for custom areas in Uncategorized category. ([52159](https://github.com/WordPress/gutenberg/pull/52159)) +- Remove ability for user to toggle sync status after pattern creation. ([51998](https://github.com/WordPress/gutenberg/pull/51998)) +- Rename sync_status and move to top level field on rest return instead of a meta field. ([52146](https://github.com/WordPress/gutenberg/pull/52146)) + +#### Interactivity API +- Block Image: Lightbox - Hide animation selector if behavior is Default or None. ([51748](https://github.com/WordPress/gutenberg/pull/51748)) +- Image block: Fix responsive sizing in lightbox. ([51823](https://github.com/WordPress/gutenberg/pull/51823)) +- Image block: Lightbox animation improvements. ([51721](https://github.com/WordPress/gutenberg/pull/51721)) +- Navigation block: Check that the modal is set before using `contains`. ([51962](https://github.com/WordPress/gutenberg/pull/51962)) + +#### Accessibility +- Fix incorrect aria-describedby attributes for theme patterns. ([52263](https://github.com/WordPress/gutenberg/pull/52263)) +- Guide: Place focus on the guide's container instead of its first tabbable. ([52300](https://github.com/WordPress/gutenberg/pull/52300)) +- Site Editor: Update headings hierarchy in the 'Manage all' screens. ([52271](https://github.com/WordPress/gutenberg/pull/52271)) + +#### Global Styles +- Check if experiment enabled for realsies this time. ([52315](https://github.com/WordPress/gutenberg/pull/52315)) +- Check randomizer experiment is enabled before rendering button. ([52306](https://github.com/WordPress/gutenberg/pull/52306)) + +#### Navigation Menu Sidebar +- Make the entire preview clickable in order to enter "edit" mode in focus mode. ([51973](https://github.com/WordPress/gutenberg/pull/51973)) +- Restore sidebar in focus mode on Pattern click through in Browse Mode `Library`. ([51897](https://github.com/WordPress/gutenberg/pull/51897)) + +#### Page Content Focus +- Hide parent selector when parent's block editing mode is 'disabled' or 'contentOnly'. ([52264](https://github.com/WordPress/gutenberg/pull/52264)) + +#### Post Editor +- Editor: Avoid remounting pre-publish sidebar contents during autosave. ([52208](https://github.com/WordPress/gutenberg/pull/52208)) + +#### Block Editor +- Enable draft entity creation in Nav block offcanvas. ([52166](https://github.com/WordPress/gutenberg/pull/52166)) + +#### History +- Update the behavior of the cached undo/redo stack. ([51644](https://github.com/WordPress/gutenberg/pull/51644)) + +#### Components +- DropdownMenu: Fix icon style when dashicon is used. ([43574](https://github.com/WordPress/gutenberg/pull/43574)) + + +### Performance + +- Migrate performance tests to Playwright. ([51084](https://github.com/WordPress/gutenberg/pull/51084)) +- Social links: Reverts updating class and style attributes. ([52019](https://github.com/WordPress/gutenberg/pull/52019)) +- tests: Configure as a production environment. ([52016](https://github.com/WordPress/gutenberg/pull/52016)) + +#### Block Library +- Try: Aggressive TinyMCE deprecation. ([50387](https://github.com/WordPress/gutenberg/pull/50387)) + + +### Experiments + +#### Interactivity API +- Create @wordpress/interactivity with the Interactivity API. ([50906](https://github.com/WordPress/gutenberg/pull/50906)) + + +### Documentation + +- Add @examples to the @wordpress/rich-text package selectors and hide the actions from documentation. ([52089](https://github.com/WordPress/gutenberg/pull/52089)) +- Add examples for core/keyboard-shortcut package. ([42831](https://github.com/WordPress/gutenberg/pull/42831)) +- Block Editor: Add README for FontFamilyControl component. ([52118](https://github.com/WordPress/gutenberg/pull/52118)) +- Block Editor: Add README for `PanelColorSettings` component. ([52327](https://github.com/WordPress/gutenberg/pull/52327)) +- Block Editor: Add README for `RecursionProvider`. ([52334](https://github.com/WordPress/gutenberg/pull/52334)) +- Docs: Update release documentation to use the right cherry-picking command. ([51935](https://github.com/WordPress/gutenberg/pull/51935)) + + +### Code Quality + +- Lodash: Refactor away from `_.kebabCase()` in `getCleanTemplatePartSlug`. ([51906](https://github.com/WordPress/gutenberg/pull/51906)) +- Lodash: Refactor away from `_.kebabCase()` in add page modal. ([51911](https://github.com/WordPress/gutenberg/pull/51911)) +- Lodash: Refactor away from `_.kebabCase()` in generic template modal. ([51910](https://github.com/WordPress/gutenberg/pull/51910)) +- Lodash: Remove completely from `@wordpress/style-engine` package. ([51726](https://github.com/WordPress/gutenberg/pull/51726)) + +#### Block Library +- Heading Block: Remove unused `HeadingLevelIcon` component. ([52008](https://github.com/WordPress/gutenberg/pull/52008)) +- Image block and behaviors: Fix some warnings. ([52109](https://github.com/WordPress/gutenberg/pull/52109)) +- Lodash: Refactor embed block away from `_.kebabCase()`. ([51916](https://github.com/WordPress/gutenberg/pull/51916)) +- Lodash: Remove dependency from block library package. ([51976](https://github.com/WordPress/gutenberg/pull/51976)) +- Make Navigation fallback selector private. ([51413](https://github.com/WordPress/gutenberg/pull/51413)) +- Page List: Fix ESLint warnings. ([52267](https://github.com/WordPress/gutenberg/pull/52267)) +- Refactor, document, and fix image block deprecations. ([52081](https://github.com/WordPress/gutenberg/pull/52081)) + +#### Page Content Focus +- Add basic test for the page content focus flow. ([52231](https://github.com/WordPress/gutenberg/pull/52231)) + +#### List View +- Return primitive value for 'hideInserter' in Appender component. ([52161](https://github.com/WordPress/gutenberg/pull/52161)) + +#### Interactivity API +- Fix the `exsisting` -> `existing` typo. ([52110](https://github.com/WordPress/gutenberg/pull/52110)) + +#### Navigation Menu Sidebar +- Remove redundant call to Navigation selector in Browse Mode. ([51988](https://github.com/WordPress/gutenberg/pull/51988)) + +#### Site Editor +- Block removal prompt: Let consumers pass their own rules. ([51841](https://github.com/WordPress/gutenberg/pull/51841)) + +#### Block Editor +- Revise LinkControl suggestions UI to use MenuItem. ([50978](https://github.com/WordPress/gutenberg/pull/50978)) + + +### Tools + +#### Testing +- Drops PHP 5.6 CI jobs. ([52345](https://github.com/WordPress/gutenberg/pull/52345)) +- Fix flakiness of saving entities in the site editor. ([51728](https://github.com/WordPress/gutenberg/pull/51728)) +- Fix flaky Site Editor pages end-to-end test. ([52283](https://github.com/WordPress/gutenberg/pull/52283)) +- Have `createNewPost` wait for editor canvas contents. ([51824](https://github.com/WordPress/gutenberg/pull/51824)) + +#### Build Tooling +- Fix phpunit failures. ([51950](https://github.com/WordPress/gutenberg/pull/51950)) +- Use moment-timezone-data-webpack-plugin to optimize timezones shipped in wp/date. ([51519](https://github.com/WordPress/gutenberg/pull/51519)) + + +### Various + +- Add caching to schema of REST API. ([52045](https://github.com/WordPress/gutenberg/pull/52045)) +- Add code owners for the Interactivity API runtime. ([52174](https://github.com/WordPress/gutenberg/pull/52174)) +- Backport from core: Rename `gutenberg_get_remote_theme_patterns` to `gutenberg_get_theme_directory_pattern_slugs`. ([51784](https://github.com/WordPress/gutenberg/pull/51784)) +- Block editor store: Also attach private APIs to old store descriptor. ([52088](https://github.com/WordPress/gutenberg/pull/52088)) +- Blocks: Remove gutenberg refs in PHP files. ([51978](https://github.com/WordPress/gutenberg/pull/51978)) +- Command palette: Rename. ([52153](https://github.com/WordPress/gutenberg/pull/52153)) +- Drop-indicator: Remove white border. ([52122](https://github.com/WordPress/gutenberg/pull/52122)) +- First version of the Interactivity API README. ([52104](https://github.com/WordPress/gutenberg/pull/52104)) +- Global Styles Revisions API: Backport changes from Core. ([52095](https://github.com/WordPress/gutenberg/pull/52095)) +- Global Styles Sidebar: Re-add Colors: Heading to selected blocks. ([49131](https://github.com/WordPress/gutenberg/pull/49131)) +- Image block: Update lightbox animation tests. ([52290](https://github.com/WordPress/gutenberg/pull/52290)) +- Patterns: Update section heading levels. ([52273](https://github.com/WordPress/gutenberg/pull/52273)) +- Perf logging: Change date to ISO 8601. ([51833](https://github.com/WordPress/gutenberg/pull/51833)) +- Refactor use-tab-nav shift+tab to use existing utils. ([51817](https://github.com/WordPress/gutenberg/pull/51817)) +- Remove serverSideBlockDefinitions from a test. ([52215](https://github.com/WordPress/gutenberg/pull/52215)) +- Restore "Buttons > can resize width" test. ([51865](https://github.com/WordPress/gutenberg/pull/51865)) +- Update delete page button label. ([51812](https://github.com/WordPress/gutenberg/pull/51812)) +- Update versions in WP for 6.3. ([51984](https://github.com/WordPress/gutenberg/pull/51984)) +- Wrap "Move to trash" and "Switch to draft" buttons when labels are too long to fit on a single row. ([52249](https://github.com/WordPress/gutenberg/pull/52249)) +- [Github-Actions-Workflows][Plugin-Release] Allow shipping a point-release for an older stable release. ([49082](https://github.com/WordPress/gutenberg/pull/49082)) + +#### Block Library +- Block Editor: Unify texts for Create pattern modal. ([52151](https://github.com/WordPress/gutenberg/pull/52151)) +- Block Supports: Change prefix in gutenberg_apply_colors_support to wp_ in dynamic blocks. ([51989](https://github.com/WordPress/gutenberg/pull/51989)) +- Navigation in Site View: Readd the edit button. ([52111](https://github.com/WordPress/gutenberg/pull/52111)) +- Navigation submenu: Remove unused doc block. ([52152](https://github.com/WordPress/gutenberg/pull/52152)) +- Page List: Change modal text. ([52116](https://github.com/WordPress/gutenberg/pull/52116)) +- i18n: Add context to the word "Filters". ([52198](https://github.com/WordPress/gutenberg/pull/52198)) + +#### Site Editor +- Library: Update icons in the creation menu. ([52108](https://github.com/WordPress/gutenberg/pull/52108)) +- Polish welcome guide copy for page / template editing. ([52282](https://github.com/WordPress/gutenberg/pull/52282)) +- Try: Update template titles. ([51428](https://github.com/WordPress/gutenberg/pull/51428)) +- Update stepper styling in Home template details panel. ([51972](https://github.com/WordPress/gutenberg/pull/51972)) +- Update text color of active menu items. ([51965](https://github.com/WordPress/gutenberg/pull/51965)) + +#### Patterns +- Add a hint about the rename of reusable blocks to menu and inserter. ([51771](https://github.com/WordPress/gutenberg/pull/51771)) +- Copy: "Detach pattern" instead of "Covert to regular block". ([51993](https://github.com/WordPress/gutenberg/pull/51993)) +- Library: Reinstate manage all template parts page. ([51961](https://github.com/WordPress/gutenberg/pull/51961)) +- [Library] Add lock icon for theme patterns. ([51990](https://github.com/WordPress/gutenberg/pull/51990)) + +#### Accessibility +- Navigation block: Do not toggle aria-expanded on hover when the overlay menu is opened. ([52170](https://github.com/WordPress/gutenberg/pull/52170)) +- Navigation block: Don't close submenu when it has focus. ([52177](https://github.com/WordPress/gutenberg/pull/52177)) + +#### Widgets Editor +- Export the store for the core/edit-widgets pacakage. ([52190](https://github.com/WordPress/gutenberg/pull/52190)) + +#### Post Editor +- Move block editor settings filter into 6.3 compat folder. ([52100](https://github.com/WordPress/gutenberg/pull/52100)) + +#### Layout +- Move grid function kses patch into 6.3 compat folder. ([52098](https://github.com/WordPress/gutenberg/pull/52098)) + +#### Data Layer +- hasResolvingSelectors: Exclude from result of resolveSelect. ([52038](https://github.com/WordPress/gutenberg/pull/52038)) + +#### Icons +- Remove fill="none" from pinSmall icon. ([51979](https://github.com/WordPress/gutenberg/pull/51979)) + +#### Navigation Menu Sidebar +- Sidebar Navigation: Refactor delete modal with `ConfirmDialog` component. ([51867](https://github.com/WordPress/gutenberg/pull/51867)) + +#### Templates API +- Template revisions API: Move back to experimental. ([51774](https://github.com/WordPress/gutenberg/pull/51774)) + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @ajlende @annezazu @artemiomorales @c4rl0sbr4v0 @carolinan @DAreRodz @dcalhoun @draganescu @ellatrix @fullofcaffeine @getdave @glendaviesnz @hellofromtonya @jameskoster @jasmussen @jeryj @jsnajdr @juanfra @juanmaguitar @kevin940726 @luisherranz @Mamaduka @mcsf @michalczaplinski @miminari @noisysocks @ntsekouras @oandregal @ockham @priethor @ramonjd @richtabor @ryanwelcher @SaxonF @scruffian @spacedmonkey @stokesman @t-hamano @talldan @tellthemachines @tyxla @WunderBart @youknowriad + + + + += 16.1.2 = + +### Bug fixes + +* Footnotes: increase selector specificity for anchor (https://github.com/WordPress/gutenberg/pull/52179) + + += 16.2.0-rc.1 = + + + +## Changelog + +### Features + +#### Patterns +- feat: Rename Reusable blocks to Patterns. ([51704](https://github.com/WordPress/gutenberg/pull/51704)) + + +### Enhancements + +- Export store for the core/customize-widgets package. ([52189](https://github.com/WordPress/gutenberg/pull/52189)) + +#### Patterns +- Library: Add sync status to pattern details screen. ([51954](https://github.com/WordPress/gutenberg/pull/51954)) +- Rename Library to Patterns. ([52102](https://github.com/WordPress/gutenberg/pull/52102)) +- Update custom patterns label to 'My patterns'. ([51949](https://github.com/WordPress/gutenberg/pull/51949)) +- Update pattern creation modal in library. ([51946](https://github.com/WordPress/gutenberg/pull/51946)) +- Update template part icons in the library mosaic (grid items). ([51963](https://github.com/WordPress/gutenberg/pull/51963)) + +#### Site Editor +- Change "Home" template name to "Blog home". ([52048](https://github.com/WordPress/gutenberg/pull/52048)) +- Edit Site: Make loading spinner colors consistent. ([51857](https://github.com/WordPress/gutenberg/pull/51857)) +- Update the icon used to reference the blog. ([52075](https://github.com/WordPress/gutenberg/pull/52075)) + +#### Interactivity API +- Image block: Remove extra lookup for external image dimensions in lightbox. ([52178](https://github.com/WordPress/gutenberg/pull/52178)) +- Image block: Use built-in directive for mouseover event in lightbox. ([52067](https://github.com/WordPress/gutenberg/pull/52067)) + +#### Block Library +- Force full height for editor in Navigation focus mode. ([51798](https://github.com/WordPress/gutenberg/pull/51798)) +- Social links: Updating class and style attributes. ([51997](https://github.com/WordPress/gutenberg/pull/51997)) + +#### Themes +- Add border theme_support. ([51777](https://github.com/WordPress/gutenberg/pull/51777)) +- Add link color theme_support. ([51775](https://github.com/WordPress/gutenberg/pull/51775)) + +#### Global Styles +- Style Book: Show tabs and make blocks clickable when entering edit mode from the Styles menu. ([52222](https://github.com/WordPress/gutenberg/pull/52222)) + +#### Widgets Editor +- Add @example tags to the customize-widgets package. ([52141](https://github.com/WordPress/gutenberg/pull/52141)) + +#### NUX +- Page Content Focus: Add welcome guides. ([52014](https://github.com/WordPress/gutenberg/pull/52014)) + +#### Block Editor +- Use block label and icon for the inserter draggable chip.. ([51048](https://github.com/WordPress/gutenberg/pull/51048)) + +#### Design Tools +- Add Typography: Text orientation (writing mode). ([50822](https://github.com/WordPress/gutenberg/pull/50822)) + +#### Components +- RangeControl: Add support for large 40px number input size. ([49105](https://github.com/WordPress/gutenberg/pull/49105)) + + +### New APIs + +#### Block Editor +- Add new `registerInserterMediaCategory` API to make media categories extensible. ([51542](https://github.com/WordPress/gutenberg/pull/51542)) + + +### Bug Fixes + +- Adjust the position of sticky headings in preferences modal. ([52248](https://github.com/WordPress/gutenberg/pull/52248)) +- BlockRemovalWarningModal: Fix incorrect '_n' usage. ([52164](https://github.com/WordPress/gutenberg/pull/52164)) +- Editor initrial appender: Zero out margins in constrained layouts. ([52026](https://github.com/WordPress/gutenberg/pull/52026)) +- Export store from the edit-site package. ([51986](https://github.com/WordPress/gutenberg/pull/51986)) +- Fix disable DFM when opening styles command. ([52165](https://github.com/WordPress/gutenberg/pull/52165)) +- Fix unintentional toggling on of distraction free. ([52090](https://github.com/WordPress/gutenberg/pull/52090)) +- Footnotes: Increase selector specificity for anchor. ([52179](https://github.com/WordPress/gutenberg/pull/52179)) +- Respect custom aspect ratio. ([52286](https://github.com/WordPress/gutenberg/pull/52286)) +- Turn off DFM for style book and style editing. ([52117](https://github.com/WordPress/gutenberg/pull/52117)) +- Update fixed block toolbar. ([52123](https://github.com/WordPress/gutenberg/pull/52123)) +- Updating the BlockEditorProvider settings prop should reset the store's settings entirely. ([51904](https://github.com/WordPress/gutenberg/pull/51904)) +- [Command Palette]: Remove suggestion for deleting templates/parts. ([52168](https://github.com/WordPress/gutenberg/pull/52168)) +- [Command center]: Add preferences and keyboard shortcuts commands. ([51862](https://github.com/WordPress/gutenberg/pull/51862)) +- [Edit Post]: Add toggle fullscreen mode and list view commands. ([52184](https://github.com/WordPress/gutenberg/pull/52184)) + +#### Block Library +- Fix default block dimensions visibility. ([52256](https://github.com/WordPress/gutenberg/pull/52256)) +- Fix fetching Nav fallback ID flushing Navigation entity cache. ([52069](https://github.com/WordPress/gutenberg/pull/52069)) +- Fix flaky tests in `navigation.spec.js` and other tests related to the Post Editor Template mode. ([51790](https://github.com/WordPress/gutenberg/pull/51790)) +- Fix: Term Description block should only be available in the site editor. ([51053](https://github.com/WordPress/gutenberg/pull/51053)) +- Footnotes: Register meta field for pages. ([52024](https://github.com/WordPress/gutenberg/pull/52024)) +- Image block: Fix cursor style when lightbox is opened. ([52187](https://github.com/WordPress/gutenberg/pull/52187)) +- Navigation: Add the draft status to the navigation title. ([51967](https://github.com/WordPress/gutenberg/pull/51967)) +- Navigation: Fix end-to-end test failures caused by sidebar title change. ([52308](https://github.com/WordPress/gutenberg/pull/52308)) +- Navigation: Fix sidebar title. ([52167](https://github.com/WordPress/gutenberg/pull/52167)) +- Navigation: Remove one preloaded endpoint. ([52115](https://github.com/WordPress/gutenberg/pull/52115)) +- Page List: Fix parent block selection when converting to link. ([52193](https://github.com/WordPress/gutenberg/pull/52193)) +- Post editor: Require confirmation before removing Footnotes. ([52277](https://github.com/WordPress/gutenberg/pull/52277)) +- fix: Display heading level dropdown icons and labels. ([52004](https://github.com/WordPress/gutenberg/pull/52004)) + +#### Site Editor +- Add confirmation step when deleting a Template. ([52236](https://github.com/WordPress/gutenberg/pull/52236)) +- Command Palette: Fix incorrect path and snackbar message when template part is deleted. ([52034](https://github.com/WordPress/gutenberg/pull/52034)) +- Default to showing status slug in sidebar. ([52226](https://github.com/WordPress/gutenberg/pull/52226)) +- Fix missing MenuGroup segment in Site Editor header more menu. ([51860](https://github.com/WordPress/gutenberg/pull/51860)) +- Fix missing snackbars in Library. ([52021](https://github.com/WordPress/gutenberg/pull/52021)) +- Fix stepper styling in Home template sidebar. ([52025](https://github.com/WordPress/gutenberg/pull/52025)) +- Get the top toolbar preference from the correct scope. ([51840](https://github.com/WordPress/gutenberg/pull/51840)) +- Hide word count and reading time meta data for the Posts Page details panel. ([52186](https://github.com/WordPress/gutenberg/pull/52186)) +- Modal: Add small top padding to the content so that avoid cutting off the visible outline when hovering items. ([51829](https://github.com/WordPress/gutenberg/pull/51829)) +- Site Editor Frame: Ignore Spotlight in view mode. ([52262](https://github.com/WordPress/gutenberg/pull/52262)) +- Try restoring the site editor animation. ([51956](https://github.com/WordPress/gutenberg/pull/51956)) + +#### Patterns +- Fix custom patterns console error. ([51947](https://github.com/WordPress/gutenberg/pull/51947)) +- Fix history back after entering edit mode from Patterns. ([52112](https://github.com/WordPress/gutenberg/pull/52112)) +- Fix setting of sync status for fully synced patterns. ([51952](https://github.com/WordPress/gutenberg/pull/51952)) +- Fix sidebar tab label. ([51953](https://github.com/WordPress/gutenberg/pull/51953)) +- Fix: Pattern focus mode DocumentActions should use the pattern icon. ([52031](https://github.com/WordPress/gutenberg/pull/52031)) +- Include template parts for custom areas in Uncategorized category. ([52159](https://github.com/WordPress/gutenberg/pull/52159)) +- Remove ability for user to toggle sync status after pattern creation. ([51998](https://github.com/WordPress/gutenberg/pull/51998)) +- Rename sync_status and move to top level field on rest return instead of a meta field. ([52146](https://github.com/WordPress/gutenberg/pull/52146)) + +#### Interactivity API +- Block Image: Lightbox - Hide animation selector if behavior is Default or None. ([51748](https://github.com/WordPress/gutenberg/pull/51748)) +- Image block: Fix responsive sizing in lightbox. ([51823](https://github.com/WordPress/gutenberg/pull/51823)) +- Image block: Lightbox animation improvements. ([51721](https://github.com/WordPress/gutenberg/pull/51721)) +- Navigation block: Check that the modal is set before using `contains`. ([51962](https://github.com/WordPress/gutenberg/pull/51962)) + +#### Accessibility +- Fix incorrect aria-describedby attributes for theme patterns. ([52263](https://github.com/WordPress/gutenberg/pull/52263)) +- Guide: Place focus on the guide's container instead of its first tabbable. ([52300](https://github.com/WordPress/gutenberg/pull/52300)) +- Site Editor: Update headings hierarchy in the 'Manage all' screens. ([52271](https://github.com/WordPress/gutenberg/pull/52271)) + +#### Global Styles +- Check if experiment enabled for realsies this time. ([52315](https://github.com/WordPress/gutenberg/pull/52315)) +- Check randomizer experiment is enabled before rendering button. ([52306](https://github.com/WordPress/gutenberg/pull/52306)) + +#### Navigation Menu Sidebar +- Make the entire preview clickable in order to enter "edit" mode in focus mode. ([51973](https://github.com/WordPress/gutenberg/pull/51973)) +- Restore sidebar in focus mode on Pattern click through in Browse Mode `Library`. ([51897](https://github.com/WordPress/gutenberg/pull/51897)) + +#### Page Content Focus +- Hide parent selector when parent's block editing mode is 'disabled' or 'contentOnly'. ([52264](https://github.com/WordPress/gutenberg/pull/52264)) + +#### Post Editor +- Editor: Avoid remounting pre-publish sidebar contents during autosave. ([52208](https://github.com/WordPress/gutenberg/pull/52208)) + +#### Block Editor +- Enable draft entity creation in Nav block offcanvas. ([52166](https://github.com/WordPress/gutenberg/pull/52166)) + +#### History +- Update the behavior of the cached undo/redo stack. ([51644](https://github.com/WordPress/gutenberg/pull/51644)) + +#### Components +- DropdownMenu: Fix icon style when dashicon is used. ([43574](https://github.com/WordPress/gutenberg/pull/43574)) + + +### Performance + +- Migrate performance tests to Playwright. ([51084](https://github.com/WordPress/gutenberg/pull/51084)) +- Social links: Reverts updating class and style attributes. ([52019](https://github.com/WordPress/gutenberg/pull/52019)) +- tests: Configure as a production environment. ([52016](https://github.com/WordPress/gutenberg/pull/52016)) + +#### Block Library +- Try: Aggressive TinyMCE deprecation. ([50387](https://github.com/WordPress/gutenberg/pull/50387)) + + +### Experiments + +#### Interactivity API +- Create @wordpress/interactivity with the Interactivity API. ([50906](https://github.com/WordPress/gutenberg/pull/50906)) + + +### Documentation + +- Add @examples to the @wordpress/rich-text package selectors and hide the actions from documentation. ([52089](https://github.com/WordPress/gutenberg/pull/52089)) +- Add examples for core/keyboard-shortcut package. ([42831](https://github.com/WordPress/gutenberg/pull/42831)) +- Block Editor: Add README for FontFamilyControl component. ([52118](https://github.com/WordPress/gutenberg/pull/52118)) +- Block Editor: Add README for `PanelColorSettings` component. ([52327](https://github.com/WordPress/gutenberg/pull/52327)) +- Block Editor: Add README for `RecursionProvider`. ([52334](https://github.com/WordPress/gutenberg/pull/52334)) +- Docs: Update release documentation to use the right cherry-picking command. ([51935](https://github.com/WordPress/gutenberg/pull/51935)) + + +### Code Quality + +- Lodash: Refactor away from `_.kebabCase()` in `getCleanTemplatePartSlug`. ([51906](https://github.com/WordPress/gutenberg/pull/51906)) +- Lodash: Refactor away from `_.kebabCase()` in add page modal. ([51911](https://github.com/WordPress/gutenberg/pull/51911)) +- Lodash: Refactor away from `_.kebabCase()` in generic template modal. ([51910](https://github.com/WordPress/gutenberg/pull/51910)) +- Lodash: Remove completely from `@wordpress/style-engine` package. ([51726](https://github.com/WordPress/gutenberg/pull/51726)) + +#### Block Library +- Heading Block: Remove unused `HeadingLevelIcon` component. ([52008](https://github.com/WordPress/gutenberg/pull/52008)) +- Image block and behaviors: Fix some warnings. ([52109](https://github.com/WordPress/gutenberg/pull/52109)) +- Lodash: Refactor embed block away from `_.kebabCase()`. ([51916](https://github.com/WordPress/gutenberg/pull/51916)) +- Lodash: Remove dependency from block library package. ([51976](https://github.com/WordPress/gutenberg/pull/51976)) +- Make Navigation fallback selector private. ([51413](https://github.com/WordPress/gutenberg/pull/51413)) +- Page List: Fix ESLint warnings. ([52267](https://github.com/WordPress/gutenberg/pull/52267)) +- Refactor, document, and fix image block deprecations. ([52081](https://github.com/WordPress/gutenberg/pull/52081)) + +#### Page Content Focus +- Add basic test for the page content focus flow. ([52231](https://github.com/WordPress/gutenberg/pull/52231)) + +#### List View +- Return primitive value for 'hideInserter' in Appender component. ([52161](https://github.com/WordPress/gutenberg/pull/52161)) + +#### Interactivity API +- Fix the `exsisting` -> `existing` typo. ([52110](https://github.com/WordPress/gutenberg/pull/52110)) + +#### Navigation Menu Sidebar +- Remove redundant call to Navigation selector in Browse Mode. ([51988](https://github.com/WordPress/gutenberg/pull/51988)) + +#### Site Editor +- Block removal prompt: Let consumers pass their own rules. ([51841](https://github.com/WordPress/gutenberg/pull/51841)) + +#### Block Editor +- Revise LinkControl suggestions UI to use MenuItem. ([50978](https://github.com/WordPress/gutenberg/pull/50978)) + + +### Tools + +#### Testing +- Drops PHP 5.6 CI jobs. ([52345](https://github.com/WordPress/gutenberg/pull/52345)) +- Fix flakiness of saving entities in the site editor. ([51728](https://github.com/WordPress/gutenberg/pull/51728)) +- Fix flaky Site Editor pages end-to-end test. ([52283](https://github.com/WordPress/gutenberg/pull/52283)) +- Have `createNewPost` wait for editor canvas contents. ([51824](https://github.com/WordPress/gutenberg/pull/51824)) + +#### Build Tooling +- Fix phpunit failures. ([51950](https://github.com/WordPress/gutenberg/pull/51950)) +- Use moment-timezone-data-webpack-plugin to optimize timezones shipped in wp/date. ([51519](https://github.com/WordPress/gutenberg/pull/51519)) + + +### Various + +- Add caching to schema of REST API. ([52045](https://github.com/WordPress/gutenberg/pull/52045)) +- Add code owners for the Interactivity API runtime. ([52174](https://github.com/WordPress/gutenberg/pull/52174)) +- Backport from core: Rename `gutenberg_get_remote_theme_patterns` to `gutenberg_get_theme_directory_pattern_slugs`. ([51784](https://github.com/WordPress/gutenberg/pull/51784)) +- Block editor store: Also attach private APIs to old store descriptor. ([52088](https://github.com/WordPress/gutenberg/pull/52088)) +- Blocks: Remove gutenberg refs in PHP files. ([51978](https://github.com/WordPress/gutenberg/pull/51978)) +- Command palette: Rename. ([52153](https://github.com/WordPress/gutenberg/pull/52153)) +- Drop-indicator: Remove white border. ([52122](https://github.com/WordPress/gutenberg/pull/52122)) +- First version of the Interactivity API README. ([52104](https://github.com/WordPress/gutenberg/pull/52104)) +- Global Styles Revisions API: Backport changes from Core. ([52095](https://github.com/WordPress/gutenberg/pull/52095)) +- Global Styles Sidebar: Re-add Colors: Heading to selected blocks. ([49131](https://github.com/WordPress/gutenberg/pull/49131)) +- Image block: Update lightbox animation tests. ([52290](https://github.com/WordPress/gutenberg/pull/52290)) +- Patterns: Update section heading levels. ([52273](https://github.com/WordPress/gutenberg/pull/52273)) +- Perf logging: Change date to ISO 8601. ([51833](https://github.com/WordPress/gutenberg/pull/51833)) +- Refactor use-tab-nav shift+tab to use existing utils. ([51817](https://github.com/WordPress/gutenberg/pull/51817)) +- Remove serverSideBlockDefinitions from a test. ([52215](https://github.com/WordPress/gutenberg/pull/52215)) +- Restore "Buttons > can resize width" test. ([51865](https://github.com/WordPress/gutenberg/pull/51865)) +- Update delete page button label. ([51812](https://github.com/WordPress/gutenberg/pull/51812)) +- Update versions in WP for 6.3. ([51984](https://github.com/WordPress/gutenberg/pull/51984)) +- Wrap "Move to trash" and "Switch to draft" buttons when labels are too long to fit on a single row. ([52249](https://github.com/WordPress/gutenberg/pull/52249)) +- [Github-Actions-Workflows][Plugin-Release] Allow shipping a point-release for an older stable release. ([49082](https://github.com/WordPress/gutenberg/pull/49082)) + +#### Block Library +- Block Editor: Unify texts for Create pattern modal. ([52151](https://github.com/WordPress/gutenberg/pull/52151)) +- Block Supports: Change prefix in gutenberg_apply_colors_support to wp_ in dynamic blocks. ([51989](https://github.com/WordPress/gutenberg/pull/51989)) +- Navigation in Site View: Readd the edit button. ([52111](https://github.com/WordPress/gutenberg/pull/52111)) +- Navigation submenu: Remove unused doc block. ([52152](https://github.com/WordPress/gutenberg/pull/52152)) +- Page List: Change modal text. ([52116](https://github.com/WordPress/gutenberg/pull/52116)) +- i18n: Add context to the word "Filters". ([52198](https://github.com/WordPress/gutenberg/pull/52198)) + +#### Site Editor +- Library: Update icons in the creation menu. ([52108](https://github.com/WordPress/gutenberg/pull/52108)) +- Polish welcome guide copy for page / template editing. ([52282](https://github.com/WordPress/gutenberg/pull/52282)) +- Try: Update template titles. ([51428](https://github.com/WordPress/gutenberg/pull/51428)) +- Update stepper styling in Home template details panel. ([51972](https://github.com/WordPress/gutenberg/pull/51972)) +- Update text color of active menu items. ([51965](https://github.com/WordPress/gutenberg/pull/51965)) + +#### Patterns +- Add a hint about the rename of reusable blocks to menu and inserter. ([51771](https://github.com/WordPress/gutenberg/pull/51771)) +- Copy: "Detach pattern" instead of "Covert to regular block". ([51993](https://github.com/WordPress/gutenberg/pull/51993)) +- Library: Reinstate manage all template parts page. ([51961](https://github.com/WordPress/gutenberg/pull/51961)) +- [Library] Add lock icon for theme patterns. ([51990](https://github.com/WordPress/gutenberg/pull/51990)) + +#### Accessibility +- Navigation block: Do not toggle aria-expanded on hover when the overlay menu is opened. ([52170](https://github.com/WordPress/gutenberg/pull/52170)) +- Navigation block: Don't close submenu when it has focus. ([52177](https://github.com/WordPress/gutenberg/pull/52177)) + +#### Widgets Editor +- Export the store for the core/edit-widgets pacakage. ([52190](https://github.com/WordPress/gutenberg/pull/52190)) + +#### Post Editor +- Move block editor settings filter into 6.3 compat folder. ([52100](https://github.com/WordPress/gutenberg/pull/52100)) + +#### Layout +- Move grid function kses patch into 6.3 compat folder. ([52098](https://github.com/WordPress/gutenberg/pull/52098)) + +#### Data Layer +- hasResolvingSelectors: Exclude from result of resolveSelect. ([52038](https://github.com/WordPress/gutenberg/pull/52038)) + +#### Icons +- Remove fill="none" from pinSmall icon. ([51979](https://github.com/WordPress/gutenberg/pull/51979)) + +#### Navigation Menu Sidebar +- Sidebar Navigation: Refactor delete modal with `ConfirmDialog` component. ([51867](https://github.com/WordPress/gutenberg/pull/51867)) + +#### Templates API +- Template revisions API: Move back to experimental. ([51774](https://github.com/WordPress/gutenberg/pull/51774)) + + + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @ajlende @annezazu @artemiomorales @c4rl0sbr4v0 @carolinan @DAreRodz @dcalhoun @draganescu @ellatrix @fullofcaffeine @getdave @glendaviesnz @hellofromtonya @jameskoster @jasmussen @jeryj @jsnajdr @juanfra @juanmaguitar @kevin940726 @luisherranz @Mamaduka @mcsf @michalczaplinski @miminari @noisysocks @ntsekouras @oandregal @ockham @priethor @ramonjd @richtabor @ryanwelcher @SaxonF @scruffian @spacedmonkey @stokesman @t-hamano @talldan @tellthemachines @tyxla @WunderBart @youknowriad + + = 16.1.1 = ## Changelog diff --git a/docs/assets/text-transform-component.png b/docs/assets/text-transform-component.png new file mode 100644 index 0000000000000000000000000000000000000000..2155fbdb8ebef6b747189e0982ad3219d519e3cf GIT binary patch literal 5936 zcmaJ_c_5Vg*B@Jp%B>`2d6LS$k1<1}vSm${kfrM~!_X{-v2%+`5=kzyMh&u8vJazD zvXwpim`FxwLNul4eQdqA-`o3p{+aLjKIfe8`JVGR%QKPYW(GTWg?M2w*bc)}x)v}P zClLnYz;EG#cGSLhPe4EWee`U7ED0DNKW7pecEXkL7h22^=j?{IKs&nzc)dbv!eCqy zSSwo}TT>Ga7XnVfd4)&89|x&|9!+h3qO*%9+DGg!v>O($B|ckSCoYC{)e^Tsm@1hP zbYl4M+hF?c*%wkGp{P*6`O7U)8Gttyi`c#l==x zd_1+p*Nn0?H5bz%kkDf43d-^>N=nLNNOc8gl&dNd0asO*6H`%Afh#J(6;POfkNr2>mu}&R1m64%F23rCsE3J`ugf85IQJ?3LLJpA!~^D z_Ho9$pf_Z(KV^0G|EsKq4hik-Lm*iZ2p2X2Xzot%A$YqJh+-DFjt2HO2B;$S^mLJM1Qe`Q<4>G`E9u)Z;F01zc$|^#}D6T~IUy&_KHejzG$tkn>`u0}wniWjHrJmlKD)ZO zxCoDFifaRxmzSGAg7J@g7z~EAv~)5#t+KMxKow|dX<>q3yfF|9xPxtRJv}|D(RIB*hs4~`@y85tUSB^xAnxfe2`;Baud z{5*hXc2m><01h|+03rqsEy&Y$Hm1B z4Glr1t@RebEWv>9OF*y{Q^G8H54M6J28bIR9AtoC2^NFFmMno_O;l9F5;!+E2ZBrB zQbQ)090$bVN}0@39HS7^T7n@*)wH(7vEJ1J*i7(U$p9q#4kVK^mspH}l5p^CCZ?pd zbsl7}Sh@CwB_&MT>{eD=E}0w!f&*XJAo#vKxw{K)ld9(YPjVY86ARQcJ{iJ;4T12k zd61u6~rn-~GVqE%Do5h0GnM~Faqq~7oO3tjvtepXY$Xe!$@}-dy z7RWjZ9R1Q19Ti1R&cd)*ORXun%=y;Vk`e|4oB0NCwso>P69d4>cE<+pTEgMB!^6X@ zx4G#i$fa&d&EzD=>RNhV4!$eLl=NnzF>y(?1M?sNKY@drgD@BbzRAhSvux#J7;I;} zq3#JQ|Nf6dTYap$MPGWwnQSpO5o>Y3dYFrUXNO7LrP#~k{UzMF*QK_`@4AoReJiAQ zyXtO2)#bbLJqmB^T(WkZxN;6R?O`+Eft|(&_4h~j%ygIaQ%e(vzYQP0bNF`b{F(CeOhy+~zPo;zcfz{Srj=foi8yb@)0)&ne7WCOs1nMgf8zqLA!O3yPTS}c=h_U zBOTbXz_AHM(x>w#&g0Kp?AzMb{pUZINcY}Mh6#4jYorT1B%e!mV`jTP?~WL8xv+R{ zhx3nb+O9R$29)3}#EVl~i)cO7eP?%WZ3FM>XcTtTX^wepX`;F9L7k8Eu+Xjz>~y+2 z=uz>JdqNuF*K5JCxgwNCexiS|(*@<3qh)&9@4?(Z%7w2!eXypq*m(G+I|{VDEi&w& za2C}mJgI)@mY8RV^DxaS5h|0qDaUV-gOAv*5T&c8=df24s0uM+t0Va=_E`h+qETJ+8e z%Sznl+lrOgi^2zqznA$%1qY;aRpWMnitzhxFoP@io@oFeZHwBnT690sh@v>2*xJ+Uyl+@&7xBRiKVMrtE3fjjqnoO8a5PQ#3AW@ndYs_~nef z1XXe(WcE9C8`t0U~Wzv_5w0sf7^ zjCpU-aGdW@#-=94vmHjyHg{0s-qNI1r@oX6bHUw08gnFfwXq#j_9!HBm{IFg56PZ) zC-U(-nB!!;k~VY3Jl}+G?RkxNl#k{`QbTPjo{DN@-?b(v*NgV~O?RdV%$!1^=8n^D zy`{b4zdbWZp>>P&x*)x``#HXp91d7OOdwO}GoKv3QRh+wD!y6N9rbz{l}2yj;xPD& z+s}36s*KixX7PB!1m!z5nNs9)>Q3#Cv997II$v?q2O3Izr7sm)WP6Xm?gFnne4m#U>FB z5cAw{m7laoFyiRS!5=NY;&yRD@cR`I|4D9ua|@keXm+MgT$m0^%GDVJ1*N>4w=CA?77=_kRF-DP zbcZcQ`f2rMf)2)@a;aun-B6w8bM!8iV0xT;=Id-dn**VXZ$6u$HI zihggkGgXIDB2f>@rGGnUJzY2a)hI?2*lTL|bdLZh{xVG<&FDp*Z{8N-A#S`8>B~LK z)kNIj{h51rVXG_$Mp$eggi=LqagCkz~EN3{fzX}*naNxIx z*|XdAC)s8Wbbzf#;h?425{qtFEm%r(Al2$yqsm{CwB|!XF0fj@M)F81rt~ErJI#r| z5EfOg(?$Qme#1MCaS9Bpe>MLAF3f>E`w|jHRqOWZ$XaE#-;CVk_Tv!8pluqZO67gS z)*^q*J@#9EK76+@rU*8;uU5{YASlm_)n(VcU@65RTg#Od5D-~*2S`ga%3EhE4oUWZdB6nfZJ)qvIPa=J}SHf+!)x;Ra^T?D@O2 zX$Q@FI^+innT_%UCy6M6eYVh^it{67$1fc{*MSe?&w36G0PaM7OylF>A&;0*jrn~u zP}Q&-tt$WcHuulk>W53#`J59duXcM(M6d*0MkI2F14|BZ!rkufR8p5{c6}qPmx^wE zu5L{V3zWq8w+M!E z{JX#XtHrE$9Aq0Ux@A#SH}o_oJ&t#Y?!g@#lWy55mT&!t}84B-OW%ni7<- zu)*UQHFP{7 zNc;{JTZv;P5rm$9Xd>Se^c|;1`J~rBGd>1=eHjG z(I+;aHy$`iI&kfACNvt1eG9nqYqjs6%q*1ioh z8`toyR-VJ4t-$!n7jCywDPm_GJIiAwmp<0wpS>>2JGKy^o#9*NG&yMdNrdRyD3iK# zFIb%v3|(XnLe@9t^TQXR~ME^K7{Vp=S?-vP(qsu~yMX;h!7sm*d)x77Ty` zHhRqi8Ayewc+ME&sNmX2xLyp`>fIXnq1yOwa-$`s&qSdo93A`DQ*!u`*g}q0k)leQ_-^bcRGHrcq_~XGGHOY zzjr5=HI%9rW!luN;|fb|Z}aJH#%38~st$H6695f{Ur=bgtA= zNU}qVl=h~q`J)=Go$|#rU8=c7uJf5fGiKq7!x`ZnsZ{KM^pkw`tKNRmV_B)WO2Kxn z)1P9-V92xe-N`=5PwB3{zI%uX)3=)+O;y#S^*DOTkhPCFieS)J&FzS3{IXR74qo5) zNd62BVOYI$6G|(%x%uHDPj5(;#yiHTLMrD9Bk`byjp<%F^gvmfJ;v$w6c7G!-i4VX zo2U2Mok^gcs=9Y$wCc2#R(xe_;D;QnaRG}O^nwYZqOE24)CK|7WxhMY&QLHAid+?x(A?C)01Q=2UK+k-va#aCsuTejja$7WSjewQ|H>d3u1UyE?>Q|Ch( z5f1|_(2~dH-@qgbB5cNAoAQW6?i!q@ZVS4!ExuT-ETY~p;PvzP7e%}|Y+L?RD;Fx; g(M4zw

+

``` This is what we refer to as "user-provided block styles", also know as "local styles" or "serialized styles". Essentially, each tool (font size, color, etc) ends up adding some classes and/or inline styles to the block markup. The CSS styling for these classes is part of the block, global, or theme stylesheets. @@ -511,9 +509,7 @@ The global styles UI in the site editor has a screen for per-block styles. The l In addition to styles at the individual block level and in global styles, there is the concept of layout styles that are output for both blocks-based and classic themes. -The layout block support is an experimental approach for outputting common layout styles that are shared between blocks that are used for creating layouts. Layout styles are useful for providing common styling for any block that is a container for other blocks. Examples of blocks that depend on these layout styles include Group, Row, Columns, Buttons, and Social Icons. - -While the feature is part of WordPress core, it is still flagged as experimental in the sense that the features and output are still undergoing active development. It is therefore not yet a stable feature from the perspective of 3rd party blocks, as the API is likely to change. The feature is enabled in core blocks via the `layout` setting under `supports` in a block's `block.json` file. +The layout block support outputs common layout styles that are shared between blocks used for creating layouts. Layout styles are useful for providing common styling for any block that is a container for other blocks. Examples of blocks that depend on these layout styles include Group, Row, Columns, Buttons, and Social Icons. The feature is enabled in core blocks via the `layout` setting under `supports` in a block's `block.json` file. There are two primary places where Layout styles are output: @@ -523,22 +519,23 @@ Base layout styles are those styles that are common to all blocks that opt in to Base layout styles are output from within [the main PHP class](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/class-wp-theme-json.php) that handles global styles, and form part of the global styles stylesheet. In order to provide support for core blocks in classic themes, these styles are always output, irrespective of whether the theme provides its own `theme.json` file. -Common layout definitions are stored in [the core `theme.json` file](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/theme.json), but are not intended to be extended or overridden by themes. +Common layout definitions are stored in [the core layout block support file](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php). #### Individual layout styles -When a block that opts in to the experimental layout support is rendered, two things are processed and added to the output via [`layout.php`](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php): +When a block that opts in to layout support is rendered, two things are processed and added to the output via [`layout.php`](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php): - Semantic class names are added to the block markup to indicate which layout settings are in use. For example, `is-layout-flow` is for blocks (such as Group) that use the default/flow layout, and `is-content-justification-right` is added when a user sets a block to use right justification. - Individual styles are generated for non-default layout values that are set on the individual block being rendered. These styles are attached to the block via a container class name using the form `wp-container-$id` where the `$id` is a [unique number](https://developer.wordpress.org/reference/functions/wp_unique_id/). #### Available layout types -There are currently three layout types in use: +There are currently four layout types in use: - Default/Flow: Items are stacked vertically. The parent container block is set to `display: flow` and the spacing between children is handled via vertical margins. - Constrained: Items are stacked vertically, using the same spacing logic as the Flow layout. Features constrained widths for child content, outputting widths for standard content size and wide size. Defaults to using global `contentSize` and `wideSize` values set in `settings.layout` in the `theme.json`. - Flex: Items are displayed using a Flexbox layout. Defaults to a horizontal orientation. Spacing between children is handled via the `gap` CSS property. +- Grid: Items are displayed using a Grid layout. Defaults to an `auto-fill` approach to column generation but can also be set to a fixed number of columns. Spacing between children is handled via the `gap` CSS property. For controlling spacing between blocks, and enabling block spacing controls see: [What is blockGap and how can I use it?](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/#what-is-blockgap-and-how-can-i-use-it). @@ -546,7 +543,7 @@ For controlling spacing between blocks, and enabling block spacing controls see: The layout block support is designed to enable control over layout features from within the block and site editors. Where possible, try to use the features of the blocks to determine particular layout requirements rather than relying upon additional stylesheets. -For themes that wish to target container blocks in order to add or adjust particular styles, the block's class name is often the best class name to use. Class names such as `wp-block-group` or `wp-block-columns` are usually reliable class names for targeting a particular block. +For themes that wish to target container blocks in order to add or adjust particular styles, the block's class name is often the best class name to use. Class names such as `wp-block-group` or `wp-block-columns` are usually reliable class names for targeting a particular block. In addition to block and layout classnames, there is also a classname composed of block and layout together: for example, for a Group block with a constrained layout it will be `wp-block-group-is-layout-constrained`. For targeting a block that uses a particular layout type, avoid targeting `wp-container-` as container classes may not always be present in the rendered markup. @@ -559,6 +556,7 @@ The current semantic class names that can be output by the Layout block support - `is-layout-flow`: Blocks that use the Default/Flow layout type. - `is-layout-constrained`: Blocks that use the Constrained layout type. - `is-layout-flex`: Blocks that use the Flex layout type. +- `is-layout-grid`: Blocks that used the Grid layout type. - `wp-container-$id`: Where `$id` is a semi-random number. A container class that only exists when the block contains non-default Layout values. This class should not be used directly for any CSS targeting as it may or may not be present. - `is-horizontal`: When a block explicitly sets `orientation` to `horizontal`. - `is-vertical`: When a block explicitly sets `orientation` to `vertical`. diff --git a/docs/manifest.json b/docs/manifest.json index aaadd15a570790..5f75e49924f355 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1505,6 +1505,12 @@ "markdown_source": "../packages/core-data/README.md", "parent": "packages" }, + { + "title": "@wordpress/create-block-interactive-template", + "slug": "packages-create-block-interactive-template", + "markdown_source": "../packages/create-block-interactive-template/README.md", + "parent": "packages" + }, { "title": "@wordpress/create-block-tutorial-template", "slug": "packages-create-block-tutorial-template", @@ -1751,6 +1757,12 @@ "markdown_source": "../packages/npm-package-json-lint-config/README.md", "parent": "packages" }, + { + "title": "@wordpress/nux", + "slug": "packages-nux", + "markdown_source": "../packages/nux/README.md", + "parent": "packages" + }, { "title": "@wordpress/plugins", "slug": "packages-plugins", @@ -1997,6 +2009,12 @@ "markdown_source": "../docs/reference-guides/data/data-core-notices.md", "parent": "data" }, + { + "title": "The NUX (New User Experience) Data", + "slug": "data-core-nux", + "markdown_source": "../docs/reference-guides/data/data-core-nux.md", + "parent": "data" + }, { "title": "Preferences", "slug": "data-core-preferences", diff --git a/docs/reference-guides/README.md b/docs/reference-guides/README.md index f13c838697f2de..33fdd9aa602414 100644 --- a/docs/reference-guides/README.md +++ b/docs/reference-guides/README.md @@ -63,6 +63,7 @@ - [**core/editor**: The Post Editor’s Data](/docs/reference-guides/data/data-core-editor.md) - [**core/keyboard-shortcuts**: The Keyboard Shortcuts Data](/docs/reference-guides/data/data-core-keyboard-shortcuts.md) - [**core/notices**: Notices Data](/docs/reference-guides/data/data-core-notices.md) + - [**core/nux**: The NUX (New User Experience) Data](/docs/reference-guides/data/data-core-nux.md) - [**core/preferences**: Preferences](/docs/reference-guides/data/data-core-preferences.md) - [**core/reusable-blocks**: Reusable blocks](/docs/reference-guides/data/data-core-reusable-blocks.md) - [**core/rich-text**: Rich Text](/docs/reference-guides/data/data-core-rich-text.md) diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md index 7e68e18eb14bf8..0995f4d86cfd76 100644 --- a/docs/reference-guides/block-api/block-supports.md +++ b/docs/reference-guides/block-api/block-supports.md @@ -543,6 +543,79 @@ supports: { } ``` +## layout + +- Type: `boolean` or `Object` +- Default value: null +- Subproperties: + - `default`: type `Object`, default value null + - `allowSwitching`: type `boolean`, default value `false` + - `allowEditing`: type `boolean`, default value `true` + - `allowInheriting`: type `boolean`, default value `true` + - `allowSizingOnChildren`: type `boolean`, default value `false` + - `allowVerticalAlignment`: type `boolean`, default value `true` + - `allowJustification`: type `boolean`, default value `true` + - `allowOrientation`: type `boolean`, default value `true` + +This value only applies to blocks that are containers for inner blocks. If set to `true` the layout type will be `flow`. For other layout types it's necessary to set the `type` explicitly inside the `default` object. + +### layout.default + +- Type: `Object` +- Default value: null + +Allows setting the `type` property to define what layout type is default for the block, and also default values for any properties inherent to that layout type, e.g., for a `flex` layout, a default value can be set for `flexWrap`. + +### layout.allowSwitching + +- Type: `boolean` +- Default value: `false` + +Exposes a switcher control that allows toggling between all existing layout types. + +### layout.allowEditing + +- Type: `boolean` +- Default value: `true` + +Determines display of layout controls in the block sidebar. If set to false, layout controls will be hidden. + +### layout.allowInheriting + +- Type: `boolean` +- Default value: `true` + +For the `flow` layout type only, determines display of the "Inner blocks use content width" toggle. + +### layout.allowSizingOnChildren + +- Type: `boolean` +- Default value: `false` + +For the `flex` layout type only, determines display of sizing controls (Fit/Fill/Fixed) on all child blocks of the flex block. + +### layout.allowVerticalAlignment + +- Type: `boolean` +- Default value: `true` + +For the `flex` layout type only, determines display of the vertical alignment control in the block toolbar. + +### layout.allowJustification + +- Type: `boolean` +- Default value: `true` + +For the `flex` layout type, determines display of the justification control in the block toolbar and block sidebar. For the `constrained` layout type, determines display of justification control in the block sidebar. + +### layout.allowOrientation + +- Type: `boolean` +- Default value: `true` + +For the `flex` layout type only, determines display of the orientation control in the block toolbar. + + ## multiple - Type: `boolean` diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index a4e307c94df87e..d0a6ca7d356b2f 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -38,7 +38,7 @@ Add a user’s avatar. ([Source](https://github.com/WordPress/gutenberg/tree/tru ## Pattern -Create and save content to reuse across your site. Update the block, and the changes apply everywhere it’s used. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/block)) +Create and save content to reuse across your site. Update the pattern, and the changes apply everywhere it’s used. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/block)) - **Name:** core/block - **Category:** reusable @@ -266,7 +266,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb - **Name:** core/file - **Category:** media -- **Supports:** align, anchor, color (background, gradients, link, ~~text~~), interactivity +- **Supports:** align, anchor, color (background, gradients, link, ~~text~~) - **Attributes:** displayPreview, downloadButtonText, fileId, fileName, href, id, previewHeight, showDownloadButton, textLinkHref, textLinkTarget ## Footnotes @@ -275,7 +275,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb - **Name:** core/footnotes - **Category:** text -- **Supports:** ~~html~~, ~~inserter~~, ~~multiple~~, ~~reusable~~ +- **Supports:** ~~html~~, ~~multiple~~, ~~reusable~~ - **Attributes:** ## Classic @@ -421,7 +421,7 @@ A collection of blocks that allow visitors to get around your site. ([Source](ht - **Name:** core/navigation - **Category:** theme -- **Supports:** align (full, wide), inserter, interactivity, layout (allowSizingOnChildren, default, ~~allowInheriting~~, ~~allowSwitching~~, ~~allowVerticalAlignment~~), spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~ +- **Supports:** align (full, wide), inserter, layout (allowSizingOnChildren, default, ~~allowInheriting~~, ~~allowSwitching~~, ~~allowVerticalAlignment~~), spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, icon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, templateLock, textColor ## Custom Link diff --git a/docs/reference-guides/data/README.md b/docs/reference-guides/data/README.md index 5f4d8d92d4bd49..1134c1d5ddd307 100644 --- a/docs/reference-guides/data/README.md +++ b/docs/reference-guides/data/README.md @@ -12,6 +12,7 @@ - [**core/editor**: The Post Editor’s Data](/docs/reference-guides/data/data-core-editor.md) - [**core/keyboard-shortcuts**: The Keyboard Shortcuts Data](/docs/reference-guides/data/data-core-keyboard-shortcuts.md) - [**core/notices**: Notices Data](/docs/reference-guides/data/data-core-notices.md) +- [**core/nux**: The NUX (New User Experience) Data](/docs/reference-guides/data/data-core-nux.md) - [**core/preferences**: Preferences](/docs/reference-guides/data/data-core-preferences.md) - [**core/reusable-blocks**: Reusable blocks](/docs/reference-guides/data/data-core-reusable-blocks.md) - [**core/rich-text**: Rich Text](/docs/reference-guides/data/data-core-rich-text.md) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 67dbc48ccf29d8..fd29b1c6e1388c 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -485,6 +485,29 @@ _Returns_ - `Array`: ids of top-level and descendant blocks. +### getDirectInsertBlock + +Returns the block to be directly inserted by the block appender. + +_Parameters_ + +- _state_ `Object`: Editor state. +- _rootClientId_ `?string`: Optional root client ID of block list. + +_Returns_ + +- `?WPDirectInsertBlock`: The block type to be directly inserted. + +_Type Definition_ + +- _WPDirectInsertBlock_ `Object` + +_Properties_ + +- _name_ `string`: The type of block. +- _attributes_ `?Object`: Attributes to pass to the newly created block. +- _attributesToCopy_ `?Array`: Attributes to be copied from adjecent blocks when inserted. + ### getDraggedBlockClientIds Returns the client ids of any blocks being directly dragged. diff --git a/docs/reference-guides/data/data-core-nux.md b/docs/reference-guides/data/data-core-nux.md new file mode 100644 index 00000000000000..eb6a1c3b5c9a5b --- /dev/null +++ b/docs/reference-guides/data/data-core-nux.md @@ -0,0 +1,93 @@ +# The NUX (New User Experience) Data + +Namespace: `core/nux`. + +## Selectors + + + +### areTipsEnabled + +Returns whether or not tips are globally enabled. + +_Parameters_ + +- _state_ `Object`: Global application state. + +_Returns_ + +- `boolean`: Whether tips are globally enabled. + +### getAssociatedGuide + +Returns an object describing the guide, if any, that the given tip is a part of. + +_Parameters_ + +- _state_ `Object`: Global application state. +- _tipId_ `string`: The tip to query. + +_Returns_ + +- `?NUXGuideInfo`: Information about the associated guide. + +### isTipVisible + +Determines whether or not the given tip is showing. Tips are hidden if they are disabled, have been dismissed, or are not the current tip in any guide that they have been added to. + +_Parameters_ + +- _state_ `Object`: Global application state. +- _tipId_ `string`: The tip to query. + +_Returns_ + +- `boolean`: Whether or not the given tip is showing. + + + +## Actions + + + +### disableTips + +Returns an action object that, when dispatched, prevents all tips from showing again. + +_Returns_ + +- `Object`: Action object. + +### dismissTip + +Returns an action object that, when dispatched, dismisses the given tip. A dismissed tip will not show again. + +_Parameters_ + +- _id_ `string`: The tip to dismiss. + +_Returns_ + +- `Object`: Action object. + +### enableTips + +Returns an action object that, when dispatched, makes all tips show again. + +_Returns_ + +- `Object`: Action object. + +### triggerGuide + +Returns an action object that, when dispatched, presents a guide that takes the user through a series of tips step by step. + +_Parameters_ + +- _tipIds_ `string[]`: Which tips to show in the guide. + +_Returns_ + +- `Object`: Action object. + + diff --git a/docs/reference-guides/filters/block-filters.md b/docs/reference-guides/filters/block-filters.md index e41215d67065a1..66e89b0a1d614a 100644 --- a/docs/reference-guides/filters/block-filters.md +++ b/docs/reference-guides/filters/block-filters.md @@ -63,11 +63,13 @@ function addListBlockClassName( settings, name ) { return settings; } - return lodash.assign( {}, settings, { - supports: lodash.assign( {}, settings.supports, { + return { + ...settings, + supports: { + ...settings.supports, className: true, - } ), - } ); + }, + }; } wp.hooks.addFilter( @@ -126,7 +128,10 @@ Adding a background by default to all blocks. ```js function addBackgroundColorStyle( props ) { - return lodash.assign( props, { style: { backgroundColor: 'red' } } ); + return { + ...props, + style: { backgroundColor: 'red' }, + }; } wp.hooks.addFilter( @@ -276,9 +281,10 @@ var withClientIdClassName = wp.compose.createHigherOrderComponent( function ( BlockListBlock ) { return function ( props ) { - var newProps = lodash.assign( {}, props, { + var newProps = { + ...props, className: 'block-' + props.clientId, - } ); + }; return el( BlockListBlock, newProps ); }; diff --git a/docs/toc.json b/docs/toc.json index 085bbb536ece2b..1660afdcc29497 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -284,6 +284,7 @@ "docs/reference-guides/data/data-core-keyboard-shortcuts.md": [] }, { "docs/reference-guides/data/data-core-notices.md": [] }, + { "docs/reference-guides/data/data-core-nux.md": [] }, { "docs/reference-guides/data/data-core-preferences.md": [] }, diff --git a/gutenberg.php b/gutenberg.php index c90c1d0bb22e89..cb3300bfae0b4f 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.1 * Requires PHP: 5.6 - * Version: 16.2.0-rc.1 + * Version: 16.3.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 6e9d05cd7f238b..1e825e3c6bbe4f 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -275,7 +275,9 @@ public static function get_theme_data( $deprecated = array(), $options = array() } // BEGIN OF EXPERIMENTAL CODE. Not to backport to core. - static::$theme = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( static::$theme ); + if ( ! class_exists( 'WP_Font_Face' ) && class_exists( 'WP_Fonts_Resolver' ) ) { + static::$theme = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( static::$theme ); + } // END OF EXPERIMENTAL CODE. } diff --git a/lib/client-assets.php b/lib/client-assets.php index 99aa7f147ecbfc..891eb05eabdbeb 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -368,6 +368,15 @@ function gutenberg_register_packages_styles( $styles ) { ); $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' ); + gutenberg_override_style( + $styles, + 'wp-nux', + gutenberg_url( 'build/nux/style.css' ), + array( 'wp-components' ), + $version + ); + $styles->add_data( 'wp-nux', 'rtl', 'replace' ); + gutenberg_override_style( $styles, 'wp-block-library-theme', diff --git a/lib/compat/wordpress-6.3/blocks.php b/lib/compat/wordpress-6.3/blocks.php index ccc68786dc6adb..962c9ee3e07ef3 100644 --- a/lib/compat/wordpress-6.3/blocks.php +++ b/lib/compat/wordpress-6.3/blocks.php @@ -45,7 +45,7 @@ function gutenberg_rename_reusable_block_cpt_to_pattern( $args, $post_type ) { $args['labels']['singular_name'] = _x( 'Pattern', 'post type singular name' ); $args['labels']['add_new_item'] = __( 'Add new Pattern' ); $args['labels']['new_item'] = __( 'New Pattern' ); - $args['labels']['edit_item'] = __( 'Edit Pattern' ); + $args['labels']['edit_item'] = __( 'Edit Block Pattern' ); $args['labels']['view_item'] = __( 'View Pattern' ); $args['labels']['view_items'] = __( 'View Patterns' ); $args['labels']['all_items'] = __( 'All Patterns' ); diff --git a/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php b/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php index 91417971e22c73..fcf6e13b0954d7 100644 --- a/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php @@ -23,9 +23,18 @@ class Gutenberg_Navigation_Fallback { */ public static function get_fallback() { + /** + * Filters whether or not a fallback should be created. + * + * @since 6.3.0 + * + * @param bool Whether or not to create a fallback. + */ + $should_create_fallback = apply_filters( 'gutenberg_navigation_should_create_fallback', true ); + $fallback = static::get_most_recently_published_navigation(); - if ( $fallback ) { + if ( $fallback || ! $should_create_fallback ) { return $fallback; } diff --git a/lib/compat/wordpress-6.3/class-gutenberg-rest-blocks-controller.php b/lib/compat/wordpress-6.3/class-gutenberg-rest-blocks-controller.php index 08108e1638334c..5279a2c3a829ec 100644 --- a/lib/compat/wordpress-6.3/class-gutenberg-rest-blocks-controller.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-rest-blocks-controller.php @@ -39,6 +39,10 @@ public function filter_response_by_context( $data, $context ) { unset( $data['title']['rendered'] ); unset( $data['content']['rendered'] ); + // If `$data['wp_pattern_sync_status']` is already set core 6.3 + has already done the job of setting this so return early. + if ( isset( $data['wp_pattern_sync_status'] ) ) { + return $data; + } // Add the core wp_pattern_sync_status meta as top level property to the response. $data['wp_pattern_sync_status'] = isset( $data['meta']['wp_pattern_sync_status'] ) ? $data['meta']['wp_pattern_sync_status'] : ''; unset( $data['meta']['wp_pattern_sync_status'] ); diff --git a/lib/compat/wordpress-6.3/footnotes.php b/lib/compat/wordpress-6.3/footnotes.php new file mode 100644 index 00000000000000..6225b280e1b6c2 --- /dev/null +++ b/lib/compat/wordpress-6.3/footnotes.php @@ -0,0 +1,32 @@ +\s*\d+\s*_', + '', + $content + ); +} + +add_filter( 'the_content', 'gutenberg_trim_footnotes' ); diff --git a/lib/compat/wordpress-6.3/script-loader.php b/lib/compat/wordpress-6.3/script-loader.php index c515eb10fdc6bb..8f7bda2a648114 100644 --- a/lib/compat/wordpress-6.3/script-loader.php +++ b/lib/compat/wordpress-6.3/script-loader.php @@ -81,7 +81,6 @@ function _gutenberg_get_iframed_editor_assets() { ob_start(); wp_print_styles(); - wp_print_fonts( true ); $styles = ob_get_clean(); ob_start(); diff --git a/lib/compat/wordpress-6.3/theme-previews.php b/lib/compat/wordpress-6.3/theme-previews.php index eab05c5824b1ff..26153d74878b58 100644 --- a/lib/compat/wordpress-6.3/theme-previews.php +++ b/lib/compat/wordpress-6.3/theme-previews.php @@ -88,7 +88,7 @@ function addLivePreviewButton() { livePreviewButton.setAttribute('class', 'button button-primary'); livePreviewButton.setAttribute( 'href', - `/wp-admin/site-editor.php?wp_theme_preview=${themePath}&return=themes.php` + `?wp_theme_preview=${themePath}&return=themes.php` ); livePreviewButton.innerHTML = ''; themeInfo.querySelector('.theme-actions').appendChild(livePreviewButton); @@ -107,7 +107,7 @@ function block_theme_activate_nonce() { $nonce_handle = 'switch-theme_' . gutenberg_get_theme_preview_path(); ?> post_content; } + // Check if block editor is disabled by "Classic Editor" or another plugin. + if ( + function_exists( 'use_block_editor_for_post_type' ) && + ! use_block_editor_for_post_type( $current_post->post_type ) + ) { + return true; + } + if ( empty( $content ) ) { return false; } diff --git a/lib/experimental/fonts-api/fonts-api.php b/lib/experimental/fonts-api/fonts-api.php index 841efeda47a953..8d07dc118f56e1 100644 --- a/lib/experimental/fonts-api/fonts-api.php +++ b/lib/experimental/fonts-api/fonts-api.php @@ -26,14 +26,7 @@ function wp_fonts() { // Initialize. $wp_fonts->register_provider( 'local', 'WP_Fonts_Provider_Local' ); add_action( 'wp_head', 'wp_print_fonts', 50 ); - - /* - * For themes without a theme.json, admin printing is initiated by the 'admin_print_styles' hook. - * For themes with theme.json, admin printing is initiated by _wp_get_iframed_editor_assets(). - */ - if ( ! wp_theme_has_theme_json() ) { - add_action( 'admin_print_styles', 'wp_print_fonts', 50 ); - } + add_action( 'admin_print_styles', 'wp_print_fonts', 50 ); } return $wp_fonts; @@ -250,3 +243,17 @@ static function( $mime_types ) { * during the build. See: tools/webpack/blocks.js. */ add_action( 'init', 'WP_Fonts_Resolver::register_fonts_from_theme_json', 21 ); + +add_filter( + 'block_editor_settings_all', + static function( $settings ) { + ob_start(); + wp_print_fonts( true ); + $styles = ob_get_clean(); + + // Add the font-face styles to iframed editor assets. + $settings['__unstableResolvedAssets']['styles'] .= $styles; + return $settings; + }, + 11 +); diff --git a/lib/experimental/fonts/bc-layer/class-gutenberg-fonts-api-bc-layer.php b/lib/experimental/fonts/bc-layer/class-gutenberg-fonts-api-bc-layer.php new file mode 100644 index 00000000000000..7cf041148eedd0 --- /dev/null +++ b/lib/experimental/fonts/bc-layer/class-gutenberg-fonts-api-bc-layer.php @@ -0,0 +1,49 @@ +` element for wrapping the `@font-face` CSS. + * + * @since X.X.X + * @deprecated 16.3.0 Get style element is not supported. + * + * @return string Empty string. + */ + protected function get_style_element() { + _deprecated_function( __METHOD__, 'Gutenberg 16.3.0' ); + return ''; + } +} diff --git a/lib/experimental/fonts/bc-layer/class-wp-fonts-resolver.php b/lib/experimental/fonts/bc-layer/class-wp-fonts-resolver.php new file mode 100644 index 00000000000000..c2702348b95232 --- /dev/null +++ b/lib/experimental/fonts/bc-layer/class-wp-fonts-resolver.php @@ -0,0 +1,64 @@ +get_registered_font_families(). + * @deprecated 16.3.0 Register is not supported. + * + * @return array Empty array. + */ + public function get_registered_font_families() { + _deprecated_function( __METHOD__, 'Gutenberg 15.8.0' ); + return array(); + } + + /** + * Gets the list of registered fonts. + * + * @since 6.0.0 + * @deprecated 14.9.1 Use wp_fonts()->get_registered(). + * @deprecated 16.3.0 Register is not supported. + * + * @return array Empty array. + */ + public function get_registered_webfonts() { + _deprecated_function( __METHOD__, 'Gutenberg 14.9.1' ); + + return array(); + } + + /** + * Gets the list of enqueued fonts. + * + * @since 6.0.0 + * @deprecated 14.9.1 Use wp_fonts()->get_enqueued(). + * @deprecated 16.3.0 Enqueue is not supported. + * + * @return array Empty array. + */ + public function get_enqueued_webfonts() { + _deprecated_function( __METHOD__, 'Gutenberg 14.9.1' ); + return array(); + } + + /** + * Gets the list of all fonts. + * + * @since X.X.X + * @deprecated GB 14.9.1 Use wp_fonts()->get_registered(). + * @deprecated 16.3.0 This method is not supported. + * + * @return array[] + */ + public function get_all_webfonts() { + _deprecated_function( __METHOD__, 'Gutenberg 14.9.1', 'wp_fonts()->get_registered()' ); + return array(); + } + + /** + * Registers a webfont. + * + * @since 6.0.0 + * @deprecated GB 14.9.1 Use wp_register_fonts(). + * @deprecated 16.3.0 Register is not supported. + * + * @return bool False. + */ + public function register_webfont() { + _deprecated_function( __METHOD__, 'GB 14.9.1', 'wp_register_fonts()' ); + return false; + } + + /** + * Enqueue a font-family that has been already registered. + * + * @since XX.X + * @deprecated 14.9.1 Use wp_enqueue_fonts(). + * @deprecated 16.3.0 Register is not supported. + * + * @return bool False. + */ + public function enqueue_webfont() { + _deprecated_function( __METHOD__, 'Gutenberg 14.9.1' ); + return false; + } +} diff --git a/lib/experimental/fonts/bc-layer/webfonts-deprecations.php b/lib/experimental/fonts/bc-layer/webfonts-deprecations.php new file mode 100644 index 00000000000000..4de2e6221d50bf --- /dev/null +++ b/lib/experimental/fonts/bc-layer/webfonts-deprecations.php @@ -0,0 +1,291 @@ +get_providers(). + * @deprecated 16.3.0 Providers are not supported. + * + * @return array Empty array. + */ + function wp_get_webfont_providers() { + _deprecated_function( __FUNCTION__, '14.9.1' ); + + return array(); + } +} + +if ( ! function_exists( 'wp_register_webfont_provider' ) ) { + /** + * Registers a custom font service provider. + * + * @since X.X.X + * @deprecated 15.1 Use wp_register_font_provider() instead. + * @deprecated 16.3.0 Providers are not supported. + * + * @return bool False. + */ + function wp_register_webfont_provider() { + _deprecated_function( __FUNCTION__, 'GB 15.1', 'wp_register_font_provider' ); + return false; + } +} + +if ( ! function_exists( 'wp_print_webfonts' ) ) { + /** + * Invokes each provider to process and print its styles. + * + * @since 14.9.1 + * @deprecated 15.1 Use wp_print_fonts() instead. + * @deprecated 16.3.0 Webfonts API is not supported. + * + * @return array Empty array. + */ + function wp_print_webfonts() { + _deprecated_function( __FUNCTION__, 'Gutenberg 15.1', 'wp_print_font_faces' ); + return array(); + } +} + +if ( ! function_exists( 'wp_fonts' ) ) { + /** + * Initialize $wp_fonts if it has not been set. + * + * @since X.X.X + * @deprecated 16.3.0 Use Fonts Library and Font Face. Fonts API is not supported. + * + * @global WP_Fonts $wp_fonts + * + * @return WP_Fonts WP_Fonts instance. + */ + function wp_fonts() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3' ); + + global $wp_fonts; + + if ( ! ( $wp_fonts instanceof WP_Fonts ) ) { + $wp_fonts = new WP_Fonts(); + } + + return $wp_fonts; + } +} + +if ( ! function_exists( 'wp_register_fonts' ) ) { + /** + * Registers one or more font-families and each of their variations. + * + * @since X.X.X + * @deprecated 16.3.0 Register is not supported. + * + * @return array Empty array. + */ + function wp_register_fonts() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3' ); + return array(); + } +} + +if ( ! function_exists( 'wp_enqueue_fonts' ) ) { + /** + * Enqueues one or more font family and all of its variations. + * + * @since X.X.X + * @deprecated 16.3.0 Enqueue is not supported. + */ + function wp_enqueue_fonts() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3' ); + } +} + +if ( ! function_exists( 'wp_enqueue_font_variations' ) ) { + /** + * Enqueues a specific set of font variations. + * + * @since X.X.X + * @deprecated 16.3.0 Enqueue is not supported. + */ + function wp_enqueue_font_variations() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3' ); + } +} + +if ( ! function_exists( 'wp_deregister_font_family' ) ) { + /** + * Deregisters a font family and all of its registered variations. + * + * @since X.X.X + * @deprecated 16.3.0 Deregister is not supported. + */ + function wp_deregister_font_family() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3' ); + } +} + +if ( ! function_exists( 'wp_deregister_font_variation' ) ) { + /** + * Deregisters a font variation. + * + * @since X.X.X + * @deprecated 16.3.0 Deregister is not supported. + */ + function wp_deregister_font_variation() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3' ); + } +} + +if ( ! function_exists( 'wp_register_font_provider' ) ) { + /** + * Registers a custom font service provider. + * + * @since X.X.X + * @deprecated 16.3.0 Providers are not supported. + * + * @return bool False. + */ + function wp_register_font_provider() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3' ); + return false; + } +} + +if ( ! function_exists( 'wp_print_fonts' ) ) { + /** + * Invokes each provider to process and print its styles. + * + * @since X.X.X + * @deprecated 16.3.0 For classic themes, use wp_print_font_faces(). For all other sites, + * Font Face will automatically print all fonts in theme.json merged data layer, + * including in theme and user activated fonts from the Fonts Library. + * + * @return array Empty array. + */ + function wp_print_fonts() { + _deprecated_function( __FUNCTION__, 'Gutenberg 16.3', 'wp_print_font_faces' ); + return array(); + } +} diff --git a/lib/experimental/fonts/class-wp-font-face-resolver.php b/lib/experimental/fonts/class-wp-font-face-resolver.php new file mode 100644 index 00000000000000..16e74d6051aa74 --- /dev/null +++ b/lib/experimental/fonts/class-wp-font-face-resolver.php @@ -0,0 +1,158 @@ +get_settings(); + + // Bail out early if there are no font settings. + if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { + return array(); + } + + return static::parse_settings( $settings ); + } + + /** + * Parse theme.json settings to extract font definitions with variations grouped by font-family. + * + * @since X.X.X + * + * @param array $settings Font settings to parse. + * @return array Returns an array of fonts, grouped by font-family. + */ + private static function parse_settings( array $settings ) { + $fonts = array(); + + foreach ( $settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $font_families as $definition ) { + + // Skip if font-family "name" is not defined. + if ( empty( $definition['name'] ) ) { + continue; + } + + // Skip if "fontFace" is not defined, meaning there are no variations. + if ( empty( $definition['fontFace'] ) ) { + continue; + } + + $font_family = $definition['name']; + + // Prepare the fonts array structure for this font-family. + if ( ! array_key_exists( $font_family, $fonts ) ) { + $fonts[ $font_family ] = array(); + } + + $fonts[ $font_family ] = static::convert_font_face_properties( $definition['fontFace'], $font_family ); + } + } + + return $fonts; + } + + /** + * Converts font-face properties from theme.json format. + * + * @since X.X.X + * + * @param array $font_face_definition The font-face definitions to convert. + * @param string $font_family_property The value to store in the font-face font-family property. + * @return array Converted font-face properties. + */ + private static function convert_font_face_properties( array $font_face_definition, $font_family_property ) { + $converted_font_faces = array(); + + foreach ( $font_face_definition as $font_face ) { + // Add the font-family property to the font-face. + $font_face['font-family'] = $font_family_property; + + // Converts the "file:./" src placeholder into a theme font file URI. + if ( ! empty( $font_face['src'] ) ) { + $font_face['src'] = static::to_theme_file_uri( (array) $font_face['src'] ); + } + + // Convert camelCase properties into kebab-case. + $font_face = static::to_kebab_case( $font_face ); + + $converted_font_faces[] = $font_face; + } + + return $converted_font_faces; + } + + /** + * Converts each 'file:./' placeholder into a URI to the font file in the theme. + * + * The 'file:./' is specified in the theme's `theme.json` as a placeholder to be + * replaced with the URI to the font file's location in the theme. When a "src" + * beings with this placeholder, it is replaced, converting the src into a URI. + * + * @since X.X.X + * + * @param array $src An array of font file sources to process. + * @return array An array of font file src URI(s). + */ + private static function to_theme_file_uri( array $src ) { + $placeholder = 'file:./'; + + foreach ( $src as $src_key => $src_url ) { + // Skip if the src doesn't start with the placeholder, as there's nothing to replace. + if ( ! str_starts_with( $src_url, $placeholder ) ) { + continue; + } + + $src_file = str_replace( $placeholder, '', $src_url ); + $src[ $src_key ] = get_theme_file_uri( $src_file ); + } + + return $src; + } + + /** + * Converts all first dimension keys into kebab-case. + * + * @since X.X.X + * + * @param array $data The array to process. + * @return array Data with first dimension keys converted into kebab-case. + */ + private static function to_kebab_case( array $data ) { + foreach ( $data as $key => $value ) { + $kebab_case = _wp_to_kebab_case( $key ); + $data[ $kebab_case ] = $value; + if ( $kebab_case !== $key ) { + unset( $data[ $key ] ); + } + } + + return $data; + } +} diff --git a/lib/experimental/fonts/class-wp-font-face.php b/lib/experimental/fonts/class-wp-font-face.php new file mode 100644 index 00000000000000..482bf4d42396d3 --- /dev/null +++ b/lib/experimental/fonts/class-wp-font-face.php @@ -0,0 +1,418 @@ + '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ); + + /** + * Valid font-face property names. + * + * @since X.X.X + * + * @var string[] + */ + private $valid_font_face_properties = array( + 'ascent-override', + 'descent-override', + 'font-display', + 'font-family', + 'font-stretch', + 'font-style', + 'font-weight', + 'font-variant', + 'font-feature-settings', + 'font-variation-settings', + 'line-gap-override', + 'size-adjust', + 'src', + 'unicode-range', + ); + + /** + * Valid font-display values. + * + * @since X.X.X + * + * @var string[] + */ + private $valid_font_display = array( 'auto', 'block', 'fallback', 'swap', 'optional' ); + + /** + * Array of font-face style tag's attribute(s) + * where the key is the attribute name and the + * value is its value. + * + * @since X.X.X + * + * @var string[] + */ + private $style_tag_attrs = array(); + + /** + * Creates and initializes an instance of WP_Font_Face. + * + * @since X.X.X + */ + public function __construct() { + /** + * Filters the font-face property defaults. + * + * @since X.X.X + * + * @param array $defaults { + * An array of required font-face properties and defaults. + * + * @type string $provider The provider ID. Default 'local'. + * @type string $font-family The font-family property. Default empty string. + * @type string $font-style The font-style property. Default 'normal'. + * @type string $font-weight The font-weight property. Default '400'. + * @type string $font-display The font-display property. Default 'fallback'. + * } + */ + $this->font_face_property_defaults = apply_filters( 'wp_font_face_property_defaults', $this->font_face_property_defaults ); + + if ( + function_exists( 'is_admin' ) && ! is_admin() + && + function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'style' ) + ) { + $this->style_tag_attrs = array( 'type' => 'text/css' ); + } + } + + /** + * Generates and prints the `@font-face` styles for the given fonts. + * + * @since X.X.X + * + * @param array $fonts The fonts to generate and print @font-face styles. + */ + public function generate_and_print( array $fonts ) { + $fonts = $this->validate_fonts( $fonts ); + + // Bail out if there are no fonts are given to process. + if ( empty( $fonts ) ) { + return; + } + + printf( + $this->get_style_element(), + $this->get_css( $fonts ) + ); + } + + /** + * Validates each of the font-face properties. + * + * @since X.X.X + * + * @param array $fonts The fonts to valid. + * @return array Prepared font-faces organized by provider and font-family. + */ + private function validate_fonts( array $fonts ) { + $validated_fonts = array(); + + foreach ( $fonts as $font_faces ) { + foreach ( $font_faces as $font_face ) { + $font_face = $this->validate_font_face_properties( $font_face ); + // Skip if failed validation. + if ( false === $font_face ) { + continue; + } + + $validated_fonts[] = $font_face; + } + } + + return $validated_fonts; + } + + /** + * Validates each font-face property. + * + * @since X.X.X + * + * @param array $font_face Font face properties to validate. + * @return false|array Validated font-face on success. Else, false. + */ + private function validate_font_face_properties( array $font_face ) { + $font_face = wp_parse_args( $font_face, $this->font_face_property_defaults ); + + // Check the font-family. + if ( empty( $font_face['font-family'] ) || ! is_string( $font_face['font-family'] ) ) { + trigger_error( 'Font font-family must be a non-empty string.' ); + return false; + } + + // Make sure that local fonts have 'src' defined. + if ( empty( $font_face['src'] ) || ( ! is_string( $font_face['src'] ) && ! is_array( $font_face['src'] ) ) ) { + trigger_error( 'Font src must be a non-empty string or an array of strings.' ); + return false; + } + + // Validate the 'src' property. + if ( ! empty( $font_face['src'] ) ) { + foreach ( (array) $font_face['src'] as $src ) { + if ( empty( $src ) || ! is_string( $src ) ) { + trigger_error( 'Each font src must be a non-empty string.' ); + return false; + } + } + } + + // Check the font-weight. + if ( ! is_string( $font_face['font-weight'] ) && ! is_int( $font_face['font-weight'] ) ) { + trigger_error( 'Font font-weight must be a properly formatted string or integer.' ); + return false; + } + + // Check the font-display. + if ( ! in_array( $font_face['font-display'], $this->valid_font_display, true ) ) { + $font_face['font-display'] = $this->font_face_property_defaults['font-display']; + } + + // Remove invalid properties. + foreach ( $font_face as $prop => $value ) { + if ( ! in_array( $prop, $this->valid_font_face_properties, true ) ) { + unset( $font_face[ $prop ] ); + } + } + + return $font_face; + } + + /** + * Gets the `\n"; + } + + /** + * Gets the defined ' + - ( assets?.styles ?? '' ); + const html = ` + + + + + ${ styles } + ${ scripts } + + + + +`; const [ src, cleanup ] = useMemo( () => { const _src = URL.createObjectURL( diff --git a/packages/block-editor/src/components/iframe/use-compatibility-styles.js b/packages/block-editor/src/components/iframe/use-compatibility-styles.js index 4b250175d73d3c..eb738c7ebefdfe 100644 --- a/packages/block-editor/src/components/iframe/use-compatibility-styles.js +++ b/packages/block-editor/src/components/iframe/use-compatibility-styles.js @@ -45,6 +45,11 @@ export function useCompatibilityStyles() { return accumulator; } + // Don't try to add styles without ID. Styles enqueued via the WP dependency system will always have IDs. + if ( ! ownerNode.id ) { + return accumulator; + } + function matchFromRules( _cssRules ) { return Array.from( _cssRules ).find( ( { diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index db93f112a366d3..5876eb4ec01e9e 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -165,8 +165,3 @@ export { default as __experimentalInspectorPopoverHeader } from './inspector-pop export { default as BlockEditorProvider } from './provider'; export { default as useSetting } from './use-setting'; - -/* - * The following rename hint component can be removed in 6.4. - */ -export { default as ReusableBlocksRenameHint } from './inserter/reusable-block-rename-hint'; diff --git a/packages/block-editor/src/components/inner-blocks/README.md b/packages/block-editor/src/components/inner-blocks/README.md index 5ecd9c90898210..0f5d303b8c7917 100644 --- a/packages/block-editor/src/components/inner-blocks/README.md +++ b/packages/block-editor/src/components/inner-blocks/README.md @@ -185,3 +185,13 @@ For example, a button block, deeply nested in several levels of block `X` that u - **Type:** `Array` - **Default:** - `undefined`. Determines which block types should be shown in the block inserter. For example, when inserting a block within the Navigation block we specify `core/navigation-link` and `core/navigation-link/page` as these are the most commonly used inner blocks. `prioritizedInserterBlocks` takes an array of the form {blockName}/{variationName}, where {variationName} is optional. + +### `defaultBlock` + +- **Type:** `Array` +- **Default:** - `undefined`. Determines which block type should be inserted by default and any attributes that should be set by default when the block is inserted. Takes an array in the form of `[ blockname, {blockAttributes} ]`. + +### `directInsert` + +- **Type:** `Boolean` +- **Default:** - `undefined`. Determines whether the default block should be inserted directly into the InnerBlocks area by the block appender. diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index e1513b9008374c..9e0e4f19cfc7ea 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -46,6 +46,8 @@ function UncontrolledInnerBlocks( props ) { clientId, allowedBlocks, prioritizedInserterBlocks, + defaultBlock, + directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, template, @@ -64,6 +66,8 @@ function UncontrolledInnerBlocks( props ) { clientId, allowedBlocks, prioritizedInserterBlocks, + defaultBlock, + directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, templateLock, diff --git a/packages/block-editor/src/components/inner-blocks/index.native.js b/packages/block-editor/src/components/inner-blocks/index.native.js index 8af05521e9f036..f07dcf4fc53053 100644 --- a/packages/block-editor/src/components/inner-blocks/index.native.js +++ b/packages/block-editor/src/components/inner-blocks/index.native.js @@ -73,6 +73,8 @@ function UncontrolledInnerBlocks( props ) { clientId, allowedBlocks, prioritizedInserterBlocks, + defaultBlock, + directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, template, @@ -103,6 +105,8 @@ function UncontrolledInnerBlocks( props ) { clientId, allowedBlocks, prioritizedInserterBlocks, + defaultBlock, + directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, templateLock, diff --git a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js index 633d6b2701ed65..44f99428a31bf8 100644 --- a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js +++ b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js @@ -3,6 +3,7 @@ */ import { useLayoutEffect, useMemo } from '@wordpress/element'; import { useSelect, useDispatch, useRegistry } from '@wordpress/data'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies @@ -25,9 +26,13 @@ const pendingSettingsUpdates = new WeakMap(); * @param {string[]} allowedBlocks An array of block names which are permitted * in inner blocks. * @param {string[]} prioritizedInserterBlocks Block names and/or block variations to be prioritized in the inserter, in the format {blockName}/{variationName}. - * @param {?WPDirectInsertBlock} __experimentalDefaultBlock The default block to insert: [ blockName, { blockAttributes } ]. - * @param {?Function|boolean} __experimentalDirectInsert If a default block should be inserted directly by the - * appender. + * @param {?WPDirectInsertBlock} defaultBlock The default block to insert: [ blockName, { blockAttributes } ]. + * @param {?Function|boolean} directInsert If a default block should be inserted directly by the appender. + * + * @param {?WPDirectInsertBlock} __experimentalDefaultBlock A deprecated prop for the default block to insert: [ blockName, { blockAttributes } ]. Use `defaultBlock` instead. + * + * @param {?Function|boolean} __experimentalDirectInsert A deprecated prop for whether a default block should be inserted directly by the appender. Use `directInsert` instead. + * * @param {string} [templateLock] The template lock specified for the inner * blocks component. (e.g. "all") * @param {boolean} captureToolbars Whether or children toolbars should be shown @@ -41,6 +46,8 @@ export default function useNestedSettingsUpdate( clientId, allowedBlocks, prioritizedInserterBlocks, + defaultBlock, + directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, templateLock, @@ -108,11 +115,29 @@ export default function useNestedSettingsUpdate( } if ( __experimentalDefaultBlock !== undefined ) { - newSettings.__experimentalDefaultBlock = __experimentalDefaultBlock; + deprecated( '__experimentalDefaultBlock', { + alternative: 'defaultBlock', + since: '6.3', + version: '6.4', + } ); + newSettings.defaultBlock = __experimentalDefaultBlock; + } + + if ( defaultBlock !== undefined ) { + newSettings.defaultBlock = defaultBlock; } if ( __experimentalDirectInsert !== undefined ) { - newSettings.__experimentalDirectInsert = __experimentalDirectInsert; + deprecated( '__experimentalDirectInsert', { + alternative: 'directInsert', + since: '6.3', + version: '6.4', + } ); + newSettings.directInsert = __experimentalDirectInsert; + } + + if ( directInsert !== undefined ) { + newSettings.directInsert = directInsert; } // Batch updates to block list settings to avoid triggering cascading renders @@ -144,6 +169,8 @@ export default function useNestedSettingsUpdate( _allowedBlocks, _prioritizedInserterBlocks, _templateLock, + defaultBlock, + directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, captureToolbars, diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index 9c24497e5a9078..8e2972fbe2bf5e 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -231,7 +231,7 @@ export const ComposedPrivateInserter = compose( [ getBlockRootClientId, hasInserterItems, getAllowedBlocks, - __experimentalGetDirectInsertBlock, + getDirectInsertBlock, getSettings, } = select( blockEditorStore ); @@ -243,8 +243,7 @@ export const ComposedPrivateInserter = compose( [ const allowedBlocks = getAllowedBlocks( rootClientId ); const directInsertBlock = - shouldDirectInsert && - __experimentalGetDirectInsertBlock( rootClientId ); + shouldDirectInsert && getDirectInsertBlock( rootClientId ); const settings = getSettings(); diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index a3e6981e6ecfc7..6edef19583b3fc 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { AccessibilityInfo, Platform, Text } from 'react-native'; +import { AccessibilityInfo, Platform } from 'react-native'; /** * WordPress dependencies @@ -35,33 +35,17 @@ const VOICE_OVER_ANNOUNCEMENT_DELAY = 1000; const defaultRenderToggle = ( { onToggle, disabled, - style, - containerStyle, + iconStyle, + buttonStyle, onLongPress, - useExpandedMode, } ) => { - // The "expanded mode" refers to the editor's appearance when no blocks - // are currently selected. The "add block" button has a separate style - // for the "expanded mode", which are added via the below "expandedModeViewProps" - // and "expandedModeViewText" variables. - const expandedModeViewProps = useExpandedMode && { - icon: , - customContainerStyles: containerStyle, - fixedRatio: false, - }; - const expandedModeViewText = ( - - { __( 'Add blocks' ) } - - ); - return ( } + icon={ } onClick={ onToggle } extraProps={ { hint: __( 'Double tap to add a block' ), @@ -69,12 +53,12 @@ const defaultRenderToggle = ( { // usually required for components. See: https://github.com/WordPress/gutenberg/pull/18832#issuecomment-561411389. testID: 'add-block-button', onLongPress, + hitSlop: { top: 10, bottom: 10, left: 10, right: 10 }, } } isDisabled={ disabled } - { ...expandedModeViewProps } - > - { useExpandedMode && expandedModeViewText } - + customContainerStyles={ buttonStyle } + fixedRatio={ false } + /> ); }; @@ -249,23 +233,21 @@ export class Inserter extends Component { renderToggle = defaultRenderToggle, getStylesFromColorScheme, showSeparator, - useExpandedMode, } = this.props; if ( showSeparator && isOpen ) { return ; } - const style = useExpandedMode - ? styles[ 'inserter-menu__add-block-button-icon--expanded' ] - : getStylesFromColorScheme( - styles[ 'inserter-menu__add-block-button-icon' ], - styles[ 'inserter-menu__add-block-button-icon--dark' ] - ); - - const containerStyle = getStylesFromColorScheme( + + const buttonStyle = getStylesFromColorScheme( styles[ 'inserter-menu__add-block-button' ], styles[ 'inserter-menu__add-block-button--dark' ] ); + const iconStyle = getStylesFromColorScheme( + styles[ 'inserter-menu__add-block-button-icon' ], + styles[ 'inserter-menu__add-block-button-icon--dark' ] + ); + const onPress = () => { this.setState( { @@ -301,10 +283,9 @@ export class Inserter extends Component { onToggle: onPress, isOpen, disabled, - style, - containerStyle, + iconStyle, + buttonStyle, onLongPress, - useExpandedMode, } ) } ( this.picker = instance ) } diff --git a/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js index 09861d9b97f1c9..6a3a3d1eec260b 100644 --- a/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js +++ b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js @@ -10,7 +10,24 @@ import { close } from '@wordpress/icons'; import { store as preferencesStore } from '@wordpress/preferences'; const PREFERENCE_NAME = 'isResuableBlocksrRenameHintVisible'; +/* + * This hook was added in 6.3 to help users with the transition from Reusable blocks to Patterns. + * It is only exported for use in the reusable-blocks package as well as block-editor. + * It will be removed in 6.4. and should not be used in any new code. + */ +export function useReusableBlocksRenameHint() { + return useSelect( + ( select ) => + select( preferencesStore ).get( 'core', PREFERENCE_NAME ) ?? true, + [] + ); +} +/* + * This component was added in 6.3 to help users with the transition from Reusable blocks to Patterns. + * It is only exported for use in the reusable-blocks package as well as block-editor. + * It will be removed in 6.4. and should not be used in any new code. + */ export default function ReusableBlocksRenameHint() { const isReusableBlocksRenameHint = useSelect( ( select ) => @@ -29,7 +46,7 @@ export default function ReusableBlocksRenameHint() {
{ __( - 'Reusable blocks are now called patterns. A synced pattern will behave in exactly the same way as a reusable block.' + 'Reusable blocks are now synced patterns. A synced pattern will behave in exactly the same way as a reusable block.' ) }
); }, + migrate: migrateTag, }; // Deprecation for blocks that renders fixed background as backgroud from the main block container. @@ -465,6 +466,7 @@ const v10 = { ); }, + migrate: migrateTag, }; // Deprecation for blocks with `minHeightUnit` set but no `minHeight`. diff --git a/packages/block-library/src/embed/embed-placeholder.native.js b/packages/block-library/src/embed/embed-placeholder.native.js index 966b96c939217c..35b71b3ab988e1 100644 --- a/packages/block-library/src/embed/embed-placeholder.native.js +++ b/packages/block-library/src/embed/embed-placeholder.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { View, Text, TouchableWithoutFeedback } from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; /** * WordPress dependencies @@ -18,6 +18,8 @@ import { useRef } from '@wordpress/element'; import styles from './styles.scss'; import { noticeOutline } from '../../../components/src/mobile/gridicons'; +const hitSlop = { top: 22, bottom: 22, left: 22, right: 22 }; + const EmbedPlaceholder = ( { icon, isSelected, @@ -28,10 +30,17 @@ const EmbedPlaceholder = ( { tryAgain, openEmbedLinkSettings, } ) => { - const containerStyle = usePreferredColorSchemeStyle( - styles.embed__container, - styles[ 'embed__container--dark' ] + const containerSelectedStyle = usePreferredColorSchemeStyle( + styles[ 'embed__container-selected' ], + styles[ 'embed__container-selected--dark' ] ); + const containerStyle = [ + usePreferredColorSchemeStyle( + styles.embed__container, + styles[ 'embed__container--dark' ] + ), + isSelected && containerSelectedStyle, + ]; const labelStyle = usePreferredColorSchemeStyle( styles.embed__label, styles[ 'embed__label--dark' ] @@ -44,6 +53,15 @@ const EmbedPlaceholder = ( { ); const embedIconErrorStyle = styles[ 'embed__icon--error' ]; + const buttonStyles = usePreferredColorSchemeStyle( + styles.embed__button, + styles[ 'embed__button--dark' ] + ); + const iconStyles = usePreferredColorSchemeStyle( + styles.embed__icon, + styles[ 'embed__icon--dark' ] + ); + const cannotEmbedMenuPickerRef = useRef(); const errorPickerOptions = { @@ -89,55 +107,70 @@ const EmbedPlaceholder = ( { return ( <> - - - { cannotEmbed ? ( - <> - - - { __( 'Unable to embed media' ) } - + + { cannotEmbed ? ( + <> + + + { __( 'Unable to embed media' ) } + + { __( 'More options' ) } - - - ) : ( - <> - + + + + ) : ( + <> + + { label } + + - { __( 'ADD LINK' ) } + { __( 'Add link' ) } - - ) } - - + + + ) } + ); }; diff --git a/packages/block-library/src/embed/styles.native.scss b/packages/block-library/src/embed/styles.native.scss index fade4204a975bf..d7f07bc259361f 100644 --- a/packages/block-library/src/embed/styles.native.scss +++ b/packages/block-library/src/embed/styles.native.scss @@ -4,19 +4,28 @@ flex-direction: column; align-items: center; justify-content: center; - background-color: $gray-lighten-30; + background-color: #e0e0e0; // $light-dim padding-left: 12; padding-right: 12; padding-top: 12; padding-bottom: 12; - border-top-left-radius: 4; - border-top-right-radius: 4; - border-bottom-left-radius: 4; - border-bottom-right-radius: 4; + border-top-left-radius: 2; + border-top-right-radius: 2; + border-bottom-left-radius: 2; + border-bottom-right-radius: 2; } .embed__container--dark { - background-color: $background-dark-secondary; + background-color: #1f1f1f; // $dark-dim +} + +.embed__container-selected { + border-width: 2px; + border-color: $blue-40; +} + +.embed__container-selected--dark { + border-color: $blue-50; } .embed__icon--error { @@ -24,23 +33,38 @@ fill: $alert-red; } +.embed__placeholder-header { + flex-direction: row; + align-items: center; + margin-top: 4; + margin-bottom: 16; +} + +.embed__icon { + fill: $light-secondary; +} + +.embed__icon--dark { + fill: $dark-tertiary; +} + .embed__label { text-align: center; - margin-top: 4; - margin-bottom: 4; - font-size: 14; - font-weight: 500; - color: $gray-90; + margin-left: $grid-unit; + font-size: 16; + font-weight: 400; + color: $light-secondary; } .embed__label--dark { - color: $gray-10; + color: $dark-tertiary; } .embed__description { font-size: $default-font-size; text-align: center; - margin-bottom: 4; + margin-top: 4; + margin-bottom: 16; color: $light-secondary; } @@ -53,12 +77,14 @@ } .embed__action { - width: 100%; text-align: center; - color: $blue-wordpress; - font-size: 14; - font-weight: 500; - margin-top: 4; + color: $white; + font-size: 16; + font-weight: 400; +} + +.embed__action--dark { + color: $black; } .embed-preview__loading { @@ -158,3 +184,13 @@ .embed-no-preview__sheet-button--dark { color: $blue-30; } + +.embed__button { + background-color: $light-primary; + border-radius: 3px; + padding: $grid-unit $grid-unit-20; +} + +.embed__button--dark { + background-color: $dark-primary; +} diff --git a/packages/block-library/src/embed/test/index.native.js b/packages/block-library/src/embed/test/index.native.js index 6abdd2509f863b..9a3bad5e6a7340 100644 --- a/packages/block-library/src/embed/test/index.native.js +++ b/packages/block-library/src/embed/test/index.native.js @@ -330,7 +330,7 @@ describe( 'Embed block', () => { const editor = await initializeWithEmbedBlock( EMPTY_EMBED_HTML ); // Edit URL. - fireEvent.press( await editor.findByText( 'ADD LINK' ) ); + fireEvent.press( await editor.findByText( 'Add link' ) ); // Wait for edit URL modal to be visible. const embedEditURLModal = editor.getByTestId( @@ -351,7 +351,7 @@ describe( 'Embed block', () => { const editor = await initializeWithEmbedBlock( EMPTY_EMBED_HTML ); // Edit URL. - fireEvent.press( editor.getByText( 'ADD LINK' ) ); + fireEvent.press( editor.getByText( 'Add link' ) ); // Wait for edit URL modal to be visible. const embedEditURLModal = editor.getByTestId( @@ -392,7 +392,7 @@ describe( 'Embed block', () => { const editor = await initializeWithEmbedBlock( EMPTY_EMBED_HTML ); // Edit URL. - fireEvent.press( editor.getByText( 'ADD LINK' ) ); + fireEvent.press( editor.getByText( 'Add link' ) ); // Wait for edit URL modal to be visible. const embedEditURLModal = editor.getByTestId( @@ -592,7 +592,7 @@ describe( 'Embed block', () => { fireEvent.press( editor.block ); // Edit URL. - fireEvent.press( editor.getByText( 'ADD LINK' ) ); + fireEvent.press( editor.getByText( 'Add link' ) ); // Wait for edit URL modal to be visible. await waitForModalVisible( embedEditURLModal ); @@ -602,7 +602,7 @@ describe( 'Embed block', () => { fireEvent( embedEditURLModal, MODAL_DISMISS_EVENT ); // Edit URL. - fireEvent.press( editor.getByText( 'ADD LINK' ) ); + fireEvent.press( editor.getByText( 'Add link' ) ); // Wait for edit URL modal to be visible. await waitForModalVisible( embedEditURLModal ); diff --git a/packages/block-library/src/file/block.json b/packages/block-library/src/file/block.json index 6096ba36d2a670..12edc20630d1ed 100644 --- a/packages/block-library/src/file/block.json +++ b/packages/block-library/src/file/block.json @@ -65,8 +65,7 @@ "background": true, "link": true } - }, - "interactivity": true + } }, "viewScript": "file:./view.min.js", "editorStyle": "wp-block-file-editor", diff --git a/packages/block-library/src/file/index.php b/packages/block-library/src/file/index.php index a7011cc9efa348..621f07b5b88eec 100644 --- a/packages/block-library/src/file/index.php +++ b/packages/block-library/src/file/index.php @@ -5,6 +5,24 @@ * @package WordPress */ +if ( gutenberg_should_block_use_interactivity_api( 'core/file' ) ) { + /** + * Replaces view script for the File block with version using Interactivity API. + * + * @param array $metadata Block metadata as read in via block.json. + * + * @return array Filtered block type metadata. + */ + function gutenberg_block_core_file_update_interactive_view_script( $metadata ) { + if ( 'core/file' === $metadata['name'] ) { + $metadata['viewScript'] = array( 'file:./view-interactivity.min.js' ); + $metadata['supports']['interactivity'] = true; + } + return $metadata; + } + add_filter( 'block_type_metadata', 'gutenberg_block_core_file_update_interactive_view_script', 10, 1 ); +} + /** * When the `core/file` block is rendering, check if we need to enqueue the `'wp-block-file-view` script. * @@ -54,7 +72,7 @@ static function ( $matches ) { ); // If it uses the Interactivity API, add the directives. - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN && $should_load_view_script ) { + if ( gutenberg_should_block_use_interactivity_api( 'core/file' ) && $should_load_view_script ) { $processor = new WP_HTML_Tag_Processor( $content ); $processor->next_tag(); $processor->set_attribute( 'data-wp-interactive', '' ); diff --git a/packages/block-library/src/file/inspector.js b/packages/block-library/src/file/inspector.js index adef947462057c..76ed28d124600e 100644 --- a/packages/block-library/src/file/inspector.js +++ b/packages/block-library/src/file/inspector.js @@ -58,6 +58,7 @@ export default function FileBlockInspector( { { displayPreview && ( + + + + + Path + + + + + File + + - - - Path - - + + Choose a file + - - File - - - CHOOSE A FILE - `; diff --git a/packages/block-library/src/file/view-interactivity.js b/packages/block-library/src/file/view-interactivity.js new file mode 100644 index 00000000000000..9d09ca2b7f4340 --- /dev/null +++ b/packages/block-library/src/file/view-interactivity.js @@ -0,0 +1,18 @@ +/** + * WordPress dependencies + */ +import { store } from '@wordpress/interactivity'; +/** + * Internal dependencies + */ +import { browserSupportsPdfs as hasPdfPreview } from './utils'; + +store( { + selectors: { + core: { + file: { + hasPdfPreview, + }, + }, + }, +} ); diff --git a/packages/block-library/src/file/view.js b/packages/block-library/src/file/view.js index 9d09ca2b7f4340..6d0b61fa51cb7c 100644 --- a/packages/block-library/src/file/view.js +++ b/packages/block-library/src/file/view.js @@ -1,18 +1,9 @@ -/** - * WordPress dependencies - */ -import { store } from '@wordpress/interactivity'; /** * Internal dependencies */ -import { browserSupportsPdfs as hasPdfPreview } from './utils'; +import { hidePdfEmbedsOnUnsupportedBrowsers } from './utils'; -store( { - selectors: { - core: { - file: { - hasPdfPreview, - }, - }, - }, -} ); +document.addEventListener( + 'DOMContentLoaded', + hidePdfEmbedsOnUnsupportedBrowsers +); diff --git a/packages/block-library/src/footnotes/block.json b/packages/block-library/src/footnotes/block.json index 0ab992009d123f..e021e9c5225dab 100644 --- a/packages/block-library/src/footnotes/block.json +++ b/packages/block-library/src/footnotes/block.json @@ -11,7 +11,6 @@ "supports": { "html": false, "multiple": false, - "inserter": false, "reusable": false }, "style": "wp-block-footnotes" diff --git a/packages/block-library/src/footnotes/edit.js b/packages/block-library/src/footnotes/edit.js index e90a7f82be94a9..fdfe7a94039af9 100644 --- a/packages/block-library/src/footnotes/edit.js +++ b/packages/block-library/src/footnotes/edit.js @@ -1,8 +1,11 @@ /** * WordPress dependencies */ -import { RichText, useBlockProps } from '@wordpress/block-editor'; +import { BlockIcon, RichText, useBlockProps } from '@wordpress/block-editor'; import { useEntityProp } from '@wordpress/core-data'; +import { __ } from '@wordpress/i18n'; +import { Placeholder } from '@wordpress/components'; +import { formatListNumbered as icon } from '@wordpress/icons'; export default function FootnotesEdit( { context: { postType, postId } } ) { const [ meta, updateMeta ] = useEntityProp( @@ -12,8 +15,24 @@ export default function FootnotesEdit( { context: { postType, postId } } ) { postId ); const footnotes = meta?.footnotes ? JSON.parse( meta.footnotes ) : []; + const blockProps = useBlockProps(); + + if ( ! footnotes.length ) { + return ( +
+ } + label={ __( 'Footnotes' ) } + instructions={ __( + 'Footnotes found in blocks within this document will be displayed here.' + ) } + /> +
+ ); + } + return ( -
    +
      { footnotes.map( ( { id, content } ) => (
    1. { - const id = createId(); - const newValue = insertObject( - value, - { - type: formatName, - attributes: { - href: '#' + id, - id: `${ id }-link`, - 'data-fn': id, + let id; + if ( isObjectActive ) { + const object = value.replacements[ value.start ]; + id = object?.attributes?.[ 'data-fn' ]; + } else { + id = createId(); + const newValue = insertObject( + value, + { + type: formatName, + attributes: { + 'data-fn': id, + }, + innerHTML: `*`, }, - innerHTML: '*', - }, - value.end, - value.end - ); - newValue.start = newValue.end - 1; - - onChange( newValue ); + value.end, + value.end + ); + newValue.start = newValue.end - 1; + onChange( newValue ); + } // BFS search to find the first footnote block. let fnBlock = null; diff --git a/packages/block-library/src/footnotes/style.scss b/packages/block-library/src/footnotes/style.scss index 4debba0560f173..aa7ab8b6951dd3 100644 --- a/packages/block-library/src/footnotes/style.scss +++ b/packages/block-library/src/footnotes/style.scss @@ -1,17 +1,20 @@ +// These styles are for backwards compatibility with the old footnotes anchors. +// Can be removed in the future. .editor-styles-wrapper, .entry-content { counter-reset: footnotes; } -[data-fn].fn { +a[data-fn].fn { vertical-align: super; font-size: smaller; counter-increment: footnotes; - display: inline-block; + display: inline-flex; + text-decoration: none; text-indent: -9999999px; } -[data-fn].fn::after { +a[data-fn].fn::after { content: "[" counter(footnotes) "]"; text-indent: 0; float: left; diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index acf8d54efb127c..5ae8cd2d6820d2 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -562,7 +562,7 @@ function GalleryEdit( props ) { max={ Math.min( MAX_COLUMNS, images.length ) } { ...MOBILE_CONTROL_PROPS_RANGE_CONTROL } required - size="__unstable-large" + __next40pxDefaultSize /> ) } { - const colorSchemeStyles = getStylesFromColorScheme( - styles.icon, - styles.iconDark - ); - return ; - } -); - -export const sharedIcon = ; diff --git a/packages/block-library/src/gallery/test/index.native.js b/packages/block-library/src/gallery/test/index.native.js index a64b8bf4032811..fad641d61b7a97 100644 --- a/packages/block-library/src/gallery/test/index.native.js +++ b/packages/block-library/src/gallery/test/index.native.js @@ -134,7 +134,7 @@ describe( 'Gallery block', () => { // Tap on Gallery block const block = await getBlock( screen, 'Gallery' ); fireEvent.press( block ); - fireEvent.press( within( block ).getByText( 'ADD MEDIA' ) ); + fireEvent.press( within( block ).getByText( 'Add media' ) ); // Observe that media options picker is displayed /* eslint-disable jest/no-conditional-expect */ @@ -161,7 +161,7 @@ describe( 'Gallery block', () => { // This case is disabled until the issue (https://github.com/WordPress/gutenberg/issues/38444) // is addressed. - it.skip( 'block remains selected after dimissing the media options picker', async () => { + it.skip( 'block remains selected after dismissing the media options picker', async () => { // Initialize with an empty gallery const { getByLabelText, getByText, getByTestId } = await initializeEditor( { @@ -169,13 +169,13 @@ describe( 'Gallery block', () => { } ); // Tap on Gallery block - fireEvent.press( getByText( 'ADD MEDIA' ) ); + fireEvent.press( getByText( 'Add media' ) ); // Observe that media options picker is displayed expect( getByText( 'Choose images' ) ).toBeVisible(); expect( getByText( 'WordPress Media Library' ) ).toBeVisible(); - // Dimiss the picker + // Dismiss the picker if ( Platform.isIOS ) { fireEvent.press( getByText( 'Cancel' ) ); } else { @@ -283,7 +283,7 @@ describe( 'Gallery block', () => { const { selectOption } = setupPicker( screen, MEDIA_OPTIONS ); // Upload images from device - fireEvent.press( getByText( 'ADD MEDIA' ) ); + fireEvent.press( getByText( 'Add media' ) ); selectOption( 'Choose from device' ); expectMediaPickerCall( 'DEVICE_MEDIA_LIBRARY', [ 'image' ], true ); @@ -321,7 +321,7 @@ describe( 'Gallery block', () => { const { galleryBlock, getByText } = await initializeWithGalleryBlock(); // Upload images from device - fireEvent.press( getByText( 'ADD MEDIA' ) ); + fireEvent.press( getByText( 'Add media' ) ); fireEvent.press( getByText( 'Choose from device' ) ); expectMediaPickerCall( 'DEVICE_MEDIA_LIBRARY', [ 'image' ], true ); @@ -375,7 +375,7 @@ describe( 'Gallery block', () => { const { galleryBlock, getByText } = await initializeWithGalleryBlock(); // Take a photo - fireEvent.press( getByText( 'ADD MEDIA' ) ); + fireEvent.press( getByText( 'Add media' ) ); fireEvent.press( getByText( 'Take a Photo' ) ); expectMediaPickerCall( 'DEVICE_CAMERA', [ 'image' ], true ); @@ -429,7 +429,7 @@ describe( 'Gallery block', () => { ); // Upload images from free photo library - fireEvent.press( getByText( 'ADD MEDIA' ) ); + fireEvent.press( getByText( 'Add media' ) ); fireEvent.press( getByText( 'Free Photo Library' ) ); expectMediaPickerCall( 'stock-photo-library', [ 'image' ], true ); @@ -469,7 +469,7 @@ describe( 'Gallery block', () => { const { galleryBlock, getByText } = await initializeWithGalleryBlock(); // Upload images from device - fireEvent.press( getByText( 'ADD MEDIA' ) ); + fireEvent.press( getByText( 'Add media' ) ); fireEvent.press( getByText( 'Choose from device' ) ); expectMediaPickerCall( 'DEVICE_MEDIA_LIBRARY', [ 'image' ], true ); @@ -511,10 +511,11 @@ describe( 'Gallery block', () => { // Reference: https://github.com/wordpress-mobile/test-cases/blob/trunk/test-cases/gutenberg/gallery.md#tc010 it( 'rearranges gallery items', async () => { // Initialize with a gallery that contains three items - const { galleryBlock } = await initializeWithGalleryBlock( { - numberOfItems: 3, - media, - } ); + const { getByLabelText, galleryBlock } = + await initializeWithGalleryBlock( { + numberOfItems: 3, + media, + } ); // Rearrange items (final disposition will be: Image 3 - Image 1 - Image 2) const galleryItem1 = getGalleryItem( galleryBlock, 1 ); @@ -523,7 +524,7 @@ describe( 'Gallery block', () => { fireEvent.press( galleryItem3 ); await act( () => fireEvent.press( - within( galleryItem3 ).getByLabelText( + getByLabelText( /Move block left from position 3 to position 2/ ) ) @@ -532,7 +533,7 @@ describe( 'Gallery block', () => { fireEvent.press( galleryItem1 ); await act( () => fireEvent.press( - within( galleryItem1 ).getByLabelText( + getByLabelText( /Move block right from position 1 to position 2/ ) ) @@ -568,7 +569,7 @@ describe( 'Gallery block', () => { ); // Upload images from other apps - fireEvent.press( getByText( 'ADD MEDIA' ) ); + fireEvent.press( getByText( 'Add media' ) ); fireEvent.press( getByText( 'Other Apps' ) ); expectMediaPickerCall( 'other-files', [ 'image' ], true ); diff --git a/packages/block-library/src/home-link/index.php b/packages/block-library/src/home-link/index.php index 459979b0d43893..8fb5ed109019d3 100644 --- a/packages/block-library/src/home-link/index.php +++ b/packages/block-library/src/home-link/index.php @@ -98,8 +98,15 @@ function block_core_home_link_build_li_wrapper_attributes( $context ) { $colors['css_classes'], $font_sizes['css_classes'] ); - $classes[] = 'wp-block-navigation-item'; $style_attribute = ( $colors['inline_styles'] . $font_sizes['inline_styles'] ); + $classes[] = 'wp-block-navigation-item'; + + if ( is_front_page() ) { + $classes[] = 'current-menu-item'; + } elseif ( is_home() && ( (int) get_option( 'page_for_posts' ) !== get_queried_object_id() ) ) { + // Edge case where the Reading settings has a posts page set but not a static homepage. + $classes[] = 'current-menu-item'; + } $wrapper_attributes = get_block_wrapper_attributes( array( @@ -124,8 +131,14 @@ function render_block_core_home_link( $attributes, $content, $block ) { if ( empty( $attributes['label'] ) ) { return ''; } + $aria_current = ''; - $aria_current = is_home() || ( is_front_page() && 'page' === get_option( 'show_on_front' ) ) ? ' aria-current="page"' : ''; + if ( is_front_page() ) { + $aria_current = ' aria-current="page"'; + } elseif ( is_home() && ( (int) get_option( 'page_for_posts' ) !== get_queried_object_id() ) ) { + // Edge case where the Reading settings has a posts page set but not a static homepage. + $aria_current = ' aria-current="page"'; + } return sprintf( '
    2. %4$s
    3. ', diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 1e437d860aa097..9c8f66b7ffe185 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -9,11 +9,12 @@ * Renders the `core/image` block on the server, * adding a data-id attribute to the element if core/gallery has added on pre-render. * - * @param array $attributes The block attributes. - * @param string $content The block content. + * @param array $attributes The block attributes. + * @param string $content The block content. + * @param WP_Block $block The block object. * @return string Returns the block content with the data-id attribute added. */ -function render_block_core_image( $attributes, $content ) { +function render_block_core_image( $attributes, $content, $block ) { $processor = new WP_HTML_Tag_Processor( $content ); $processor->next_tag( 'img' ); @@ -30,14 +31,52 @@ function render_block_core_image( $attributes, $content ) { $processor->set_attribute( 'data-id', $attributes['data-id'] ); } + $should_load_view_script = false; + $experiments = get_option( 'gutenberg-experiments' ); + $link_destination = isset( $attributes['linkDestination'] ) ? $attributes['linkDestination'] : 'none'; + // Get the lightbox setting from the block attributes. + if ( isset( $attributes['behaviors']['lightbox'] ) ) { + $lightbox_settings = $attributes['behaviors']['lightbox']; + // If the lightbox setting is not set in the block attributes, get it from the theme.json file. + } else { + $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_data(); + if ( isset( $theme_data['behaviors']['blocks'][ $block->name ]['lightbox'] ) ) { + $lightbox_settings = $theme_data['behaviors']['blocks'][ $block->name ]['lightbox']; + } else { + $lightbox_settings = null; + } + } + + // If the lightbox is enabled, the image is not linked, and the Interactivity API is enabled, load the view script. + if ( isset( $lightbox_settings['enabled'] ) && + true === $lightbox_settings['enabled'] && + 'none' === $link_destination && + ! empty( $experiments['gutenberg-interactivity-api-core-blocks'] ) + ) { + $should_load_view_script = true; + } + + $view_js_file = 'wp-block-image-view'; + if ( ! wp_script_is( $view_js_file ) ) { + $script_handles = $block->block_type->view_script_handles; + + // If the script is not needed, and it is still in the `view_script_handles`, remove it. + if ( ! $should_load_view_script && in_array( $view_js_file, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) ); + } + // If the script is needed, but it was previously removed, add it again. + if ( $should_load_view_script && ! in_array( $view_js_file, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) ); + } + } + return $processor->get_updated_html(); } -/** - * Registers the `core/image` block on server. - */ + /** + * Registers the `core/image` block on server. + */ function register_block_core_image() { - register_block_type_from_metadata( __DIR__ . '/image', array( @@ -45,4 +84,4 @@ function register_block_core_image() { ) ); } -add_action( 'init', 'register_block_core_image' ); + add_action( 'init', 'register_block_core_image' ); diff --git a/packages/block-library/src/image/test/edit.native.js b/packages/block-library/src/image/test/edit.native.js index 2e8bc4bf4a49bd..7d614e664c7fec 100644 --- a/packages/block-library/src/image/test/edit.native.js +++ b/packages/block-library/src/image/test/edit.native.js @@ -442,7 +442,7 @@ describe( 'Image Block', () => { `; const screen = await initializeEditor( { initialHtml } ); - fireEvent.press( screen.getByText( 'ADD IMAGE' ) ); + fireEvent.press( screen.getByText( 'Add image' ) ); fireEvent.press( screen.getByText( 'WordPress Media Library' ) ); const expectedHtml = ` diff --git a/packages/block-library/src/latest-comments/edit.js b/packages/block-library/src/latest-comments/edit.js index 59258f3c0b090c..85e66cf2e9dc60 100644 --- a/packages/block-library/src/latest-comments/edit.js +++ b/packages/block-library/src/latest-comments/edit.js @@ -68,6 +68,7 @@ export default function LatestComments( { attributes, setAttributes } ) { /> diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index 8fcc2cc5b1ddda..7aaf1b3ecf0eda 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -230,6 +230,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { displayPostContentRadio === 'excerpt' && ( @@ -359,6 +360,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { { postLayout === 'grid' && ( diff --git a/packages/block-library/src/list/edit.js b/packages/block-library/src/list/edit.js index 24d5ead74c47d4..7c8c15e05fe875 100644 --- a/packages/block-library/src/list/edit.js +++ b/packages/block-library/src/list/edit.js @@ -177,10 +177,12 @@ export default function Edit( { attributes, setAttributes, clientId, style } ) { { controls } { ordered && ( ) } diff --git a/packages/block-library/src/list/test/edit.native.js b/packages/block-library/src/list/test/edit.native.js index 6aefcf45b65529..84b549beff414e 100644 --- a/packages/block-library/src/list/test/edit.native.js +++ b/packages/block-library/src/list/test/edit.native.js @@ -19,7 +19,7 @@ import { */ import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks'; import { registerCoreBlocks } from '@wordpress/block-library'; -import { BACKSPACE } from '@wordpress/keycodes'; +import { BACKSPACE, ENTER } from '@wordpress/keycodes'; describe( 'List block', () => { beforeAll( () => { @@ -338,34 +338,125 @@ describe( 'List block', () => { expect( getEditorHtml() ).toMatchSnapshot(); } ); - it( 'merges with other lists', async () => { + it( 'splits empty list items into paragraphs', async () => { + // Arrange const initialHtml = `
        -
      • One
      - -
        +
      • One
      • +
      • Two
      `; + const screen = await initializeEditor( { initialHtml } ); - const screen = await initializeEditor( { - initialHtml, - } ); - - // Select List block - const [ listBlock ] = screen.getAllByLabelText( /List Block\. Row 2/ ); + // Act + const listBlock = screen.getByLabelText( /List Block\. Row 1/ ); fireEvent.press( listBlock ); await triggerBlockListLayout( listBlock ); + const listItemField = screen.getByLabelText( /Text input. .*One.*/ ); + selectRangeInRichText( listItemField, 3 ); + fireEvent( listItemField, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + const listItemField2 = screen.getByLabelText( /Text input. Empty/ ); + fireEvent( listItemField2, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); - // Select List Item block - const [ listItemBlock ] = within( listBlock ).getAllByLabelText( - /List item Block\. Row 1/ - ); - fireEvent.press( listItemBlock ); + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +
        +
      • One
      • +
      + - // With cursor positioned at the beginning of the first List Item, press - // backward delete - const listItemField = - within( listItemBlock ).getByLabelText( /Text input. .*Two.*/ ); + +

      + + + +
        +
      • Two
      • +
      + " + ` ); + } ); + + it( 'merges paragraphs into list items', async () => { + const initialHtml = ` +
        +
      • One
      • +
      + + + +

      Two

      + + + +
        +
      • Three
      • +
      + `; + const screen = await initializeEditor( { initialHtml } ); + + // Act + const paragraphField = screen.getByLabelText( /Text input. .*Two.*/ ); + selectRangeInRichText( paragraphField, 0 ); + fireEvent( paragraphField, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: BACKSPACE, + } ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +
        +
      • One
      • + + + +
      • Two
      • +
      + + + +
        +
      • Three
      • +
      + " + ` ); + } ); + + it( 'merges lists into lists', async () => { + // Arrange + const initialHtml = ` +
        +
      • One
      • + + + +
      • Two
      • +
      + + + +
        +
      • Three
      • +
      + `; + const screen = await initializeEditor( { initialHtml } ); + + // Act + const listBlock = screen.getByLabelText( /List Block\. Row 2/ ); + fireEvent.press( listBlock ); + await triggerBlockListLayout( listBlock ); + const listItemField = screen.getByLabelText( /Text input\..*Three/ ); selectRangeInRichText( listItemField, 0 ); fireEvent( listItemField, 'onKeyDown', { nativeEvent: {}, @@ -373,17 +464,22 @@ describe( 'List block', () => { keyCode: BACKSPACE, } ); + // Assert expect( getEditorHtml() ).toMatchInlineSnapshot( ` - " -
        -
      • One
      • - - - -
      • Two
      • -
      - " - ` ); + " +
        +
      • One
      • + + + +
      • Two
      • + + + +
      • Three
      • +
      + " + ` ); } ); it( 'unwraps first item when attempting to merge with non-list block', async () => { @@ -486,16 +582,16 @@ describe( 'List block', () => { "

      A quick brown fox.

      - +

      One

      - +
      • Two
      • - +
      • Three
      diff --git a/packages/block-library/src/media-text/media-container.native.js b/packages/block-library/src/media-text/media-container.native.js index 2057486b73ac9c..dbc30dcf23e7aa 100644 --- a/packages/block-library/src/media-text/media-container.native.js +++ b/packages/block-library/src/media-text/media-container.native.js @@ -316,6 +316,7 @@ class MediaContainer extends Component { onSelect={ this.onSelectMediaUploadOption } allowedTypes={ ALLOWED_MEDIA_TYPES } onFocus={ this.props.onFocus } + className={ 'no-block-outline' } /> ); } diff --git a/packages/block-library/src/missing/edit.native.js b/packages/block-library/src/missing/edit.native.js index 8c93e1f604620a..e8576914065723 100644 --- a/packages/block-library/src/missing/edit.native.js +++ b/packages/block-library/src/missing/edit.native.js @@ -5,7 +5,7 @@ import { View, Text, TouchableWithoutFeedback, - TouchableHighlight, + TouchableOpacity, } from 'react-native'; /** @@ -83,7 +83,7 @@ export class UnsupportedBlockEdit extends Component { ); return ( - - + ); } @@ -282,12 +282,14 @@ export class UnsupportedBlockEdit extends Component { ) } > { this.renderHelpIcon() } - - { title } + + + { title } + { subtitle } { this.renderSheet( title, originalName ) } diff --git a/packages/block-library/src/missing/style.native.scss b/packages/block-library/src/missing/style.native.scss index 6718713f320198..9a56f82f7e3f0d 100644 --- a/packages/block-library/src/missing/style.native.scss +++ b/packages/block-library/src/missing/style.native.scss @@ -31,11 +31,11 @@ height: 36; padding-top: 8; padding-bottom: 8; - color: $gray-darken-20; + color: $light-secondary; } .infoIconDark { - color: $gray-20; + color: $dark-tertiary; } .infoSheetIcon { @@ -82,7 +82,8 @@ } .unsupportedBlock { - background-color: $gray-lighten-30; + height: 142; + background-color: #e0e0e0; // $light-dim padding-top: 24; padding-bottom: 24; padding-left: 8; @@ -96,31 +97,37 @@ } .unsupportedBlockDark { - background-color: $background-dark-secondary; + background-color: #1f1f1f; // $dark-dim +} + +.unsupportedBlockHeader { + flex-direction: row; + align-items: center; + margin-top: 4; + margin-bottom: 8; } .unsupportedBlockIcon { - color: $gray-dark; + color: $light-secondary; } .unsupportedBlockIconDark { - color: $white; + color: $dark-tertiary; } .unsupportedBlockMessage { - margin-top: 4; text-align: center; - color: $gray-dark; - font-size: 14; - font-weight: 600; + color: $light-secondary; + font-size: 16; + font-weight: 400; + margin-left: 6; } .unsupportedBlockMessageDark { - color: $white; + color: $dark-tertiary; } .unsupportedBlockSubtitle { - margin-top: 2; text-align: center; color: $gray-darken-20; font-size: 12; diff --git a/packages/block-library/src/missing/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/missing/test/__snapshots__/edit.native.js.snap index f0f0db7010b54c..b287f58524c6f7 100644 --- a/packages/block-library/src/missing/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/missing/test/__snapshots__/edit.native.js.snap @@ -25,6 +25,7 @@ exports[`Missing block renders without crashing 1`] = ` accessibilityLabel="Help button" accessibilityRole="button" accessible={true} + collapsable={false} focusable={true} onClick={[Function]} onResponderGrant={[Function]} @@ -33,6 +34,11 @@ exports[`Missing block renders without crashing 1`] = ` onResponderTerminate={[Function]} onResponderTerminationRequest={[Function]} onStartShouldSetResponder={[Function]} + style={ + { + "opacity": 1, + } + } > - - Path - - - missing/block/title - + + + Path + + + missing/block/title + + Unsupported diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index b91015313f4039..d0d739575963a0 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -181,6 +181,10 @@ export default function NavigationLinkEdit( { const itemLabelPlaceholder = __( 'Add label…' ); const ref = useRef(); + // Change the label using inspector causes rich text to change focus on firefox. + // This is a workaround to keep the focus on the label field when label filed is focused we don't render the rich text. + const [ isLabelFieldFocused, setIsLabelFieldFocused ] = useState( false ); + const { innerBlocks, isAtMaxNesting, @@ -368,8 +372,8 @@ export default function NavigationLinkEdit( { }, { allowedBlocks: ALLOWED_BLOCKS, - __experimentalDefaultBlock: DEFAULT_BLOCK, - __experimentalDirectInsert: true, + defaultBlock: DEFAULT_BLOCK, + directInsert: true, renderAppender: false, } ); @@ -424,6 +428,8 @@ export default function NavigationLinkEdit( { } } label={ __( 'Label' ) } autoComplete="off" + onFocus={ () => setIsLabelFieldFocused( true ) } + onBlur={ () => setIsLabelFieldFocused( false ) } /> ) : ( <> - { ! isInvalid && ! isDraft && ( - <> - - setAttributes( { - label: labelValue, - } ) - } - onMerge={ mergeBlocks } - onReplace={ onReplace } - __unstableOnSplitAtEnd={ () => - insertBlocksAfter( - createBlock( - 'core/navigation-link' + { ! isInvalid && + ! isDraft && + ! isLabelFieldFocused && ( + <> + + setAttributes( { + label: labelValue, + } ) + } + onMerge={ mergeBlocks } + onReplace={ onReplace } + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( + createBlock( + 'core/navigation-link' + ) ) - ) - } - aria-label={ __( - 'Navigation link text' - ) } - placeholder={ itemLabelPlaceholder } - withoutInteractiveFormatting - allowedFormats={ [ - 'core/bold', - 'core/italic', - 'core/image', - 'core/strikethrough', - ] } - onClick={ () => { - if ( ! url ) { - setIsLinkOpen( true ); } - } } - /> - { description && ( - - { description } - - ) } - - ) } - { ( isInvalid || isDraft ) && ( + aria-label={ __( + 'Navigation link text' + ) } + placeholder={ itemLabelPlaceholder } + withoutInteractiveFormatting + allowedFormats={ [ + 'core/bold', + 'core/italic', + 'core/image', + 'core/strikethrough', + ] } + onClick={ () => { + if ( ! url ) { + setIsLinkOpen( true ); + } + } } + /> + { description && ( + + { description } + + ) } + + ) } + { ( isInvalid || + isDraft || + isLabelFieldFocused ) && (
      diff --git a/packages/block-library/src/navigation-submenu/edit.js b/packages/block-library/src/navigation-submenu/edit.js index 7707a6442111e5..ad907a0cb92cbb 100644 --- a/packages/block-library/src/navigation-submenu/edit.js +++ b/packages/block-library/src/navigation-submenu/edit.js @@ -320,8 +320,8 @@ export default function NavigationSubmenuEdit( { getNavigationChildBlockProps( innerBlocksColors ); const innerBlocksProps = useInnerBlocksProps( navigationChildBlockProps, { allowedBlocks, - __experimentalDefaultBlock: DEFAULT_BLOCK, - __experimentalDirectInsert: true, + defaultBlock: DEFAULT_BLOCK, + directInsert: true, // Ensure block toolbar is not too far removed from item // being edited. diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json index 7896ea147699f7..e45d0535367786 100644 --- a/packages/block-library/src/navigation/block.json +++ b/packages/block-library/src/navigation/block.json @@ -131,10 +131,9 @@ } } } - }, - "interactivity": true + } }, - "viewScript": "file:./view.min.js", + "viewScript": [ "file:./view.min.js", "file:./view-modal.min.js" ], "editorStyle": "wp-block-navigation-editor", "style": "wp-block-navigation" } diff --git a/packages/block-library/src/navigation/constants.js b/packages/block-library/src/navigation/constants.js index 5e6b26ae40b0f4..07d71d50dd98ce 100644 --- a/packages/block-library/src/navigation/constants.js +++ b/packages/block-library/src/navigation/constants.js @@ -20,13 +20,19 @@ export const PRIORITIZED_INSERTER_BLOCKS = [ 'core/navigation-link', ]; +// These parameters must be kept aligned with those in +// lib/compat/wordpress-6.3/navigation-block-preloading.php +// and +// edit-site/src/components/sidebar-navigation-screen-navigation-menus/constants.js +export const PRELOADED_NAVIGATION_MENUS_QUERY = { + per_page: 100, + status: [ 'publish', 'draft' ], + order: 'desc', + orderby: 'date', +}; + export const SELECT_NAVIGATION_MENUS_ARGS = [ 'postType', 'wp_navigation', - { - per_page: 100, - status: [ 'publish', 'draft' ], - order: 'desc', - orderby: 'date', - }, + PRELOADED_NAVIGATION_MENUS_QUERY, ]; diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index 7ec990d5b7389c..6f629c20c0cfd9 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -38,6 +38,7 @@ import { __experimentalToggleGroupControlOption as ToggleGroupControlOption, Button, Spinner, + Notice, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; @@ -196,7 +197,7 @@ function Navigation( { convert: convertClassicMenu, status: classicMenuConversionStatus, error: classicMenuConversionError, - } = useConvertClassicToBlockMenu( clientId ); + } = useConvertClassicToBlockMenu( createNavigationMenu ); const isConvertingClassicMenu = classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_PENDING; @@ -485,6 +486,21 @@ function Navigation( { { open: overlayMenuPreview } ); + const submenuAccessibilityNotice = + ! showSubmenuIcon && ! openSubmenusOnClick + ? __( + 'The current menu options offer reduced accessibility for users and are not recommended. Enabling either "Open on Click" or "Show arrow" offers enhanced accessibility by allowing keyboard users to browse submenus selectively.' + ) + : ''; + + const isFirstRender = useRef( true ); // Don't speak on first render. + useEffect( () => { + if ( ! isFirstRender.current && submenuAccessibilityNotice ) { + speak( submenuAccessibilityNotice ); + } + isFirstRender.current = false; + }, [ submenuAccessibilityNotice ] ); + const colorGradientSettings = useMultipleOriginColorsAndGradients(); const stylingInspectorControls = ( <> @@ -578,6 +594,18 @@ function Navigation( { disabled={ attributes.openSubmenusOnClick } label={ __( 'Show arrow' ) } /> + + { submenuAccessibilityNotice && ( +
      + + { submenuAccessibilityNotice } + +
      + ) } ) } diff --git a/packages/block-library/src/navigation/edit/inner-blocks.js b/packages/block-library/src/navigation/edit/inner-blocks.js index 669703f002dbb2..373acd1f52dfa8 100644 --- a/packages/block-library/src/navigation/edit/inner-blocks.js +++ b/packages/block-library/src/navigation/edit/inner-blocks.js @@ -98,8 +98,8 @@ export default function NavigationInnerBlocks( { onChange, allowedBlocks: ALLOWED_BLOCKS, prioritizedInserterBlocks: PRIORITIZED_INSERTER_BLOCKS, - __experimentalDefaultBlock: DEFAULT_BLOCK, - __experimentalDirectInsert: shouldDirectInsert, + defaultBlock: DEFAULT_BLOCK, + directInsert: shouldDirectInsert, orientation, templateLock, diff --git a/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js b/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js index 52f6d8134042de..e2ad08c2a99ab1 100644 --- a/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js +++ b/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js @@ -68,8 +68,8 @@ export default function UnsavedInnerBlocks( { { renderAppender: hasSelection ? undefined : false, allowedBlocks: ALLOWED_BLOCKS, - __experimentalDefaultBlock: DEFAULT_BLOCK, - __experimentalDirectInsert: shouldDirectInsert, + defaultBlock: DEFAULT_BLOCK, + directInsert: shouldDirectInsert, } ); diff --git a/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js b/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js index 70f9a6ff4bfeab..300672fa91e8ad 100644 --- a/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js +++ b/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js @@ -9,7 +9,6 @@ import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies */ -import useCreateNavigationMenu from './use-create-navigation-menu'; import menuItemsToBlocks from '../menu-items-to-blocks'; export const CLASSIC_MENU_CONVERSION_SUCCESS = 'success'; @@ -21,15 +20,10 @@ export const CLASSIC_MENU_CONVERSION_IDLE = 'idle'; // do not import the same classic menu twice. let classicMenuBeingConvertedId = null; -function useConvertClassicToBlockMenu( clientId ) { - /* - * The wp_navigation post is created as a draft so the changes on the frontend and - * the site editor are not permanent without a save interaction done by the user. - */ - const { create: createNavigationMenu } = useCreateNavigationMenu( - clientId, - 'draft' - ); +function useConvertClassicToBlockMenu( + createNavigationMenu, + { throwOnError = false } = {} +) { const registry = useRegistry(); const { editEntityRecord } = useDispatch( coreStore ); @@ -158,19 +152,21 @@ function useConvertClassicToBlockMenu( clientId ) { classicMenuBeingConvertedId = null; // Rethrow error for debugging. - throw new Error( - sprintf( - // translators: %s: the name of a menu (e.g. Header navigation). - __( `Unable to create Navigation Menu "%s".` ), - menuName - ), - { - cause: err, - } - ); + if ( throwOnError ) { + throw new Error( + sprintf( + // translators: %s: the name of a menu (e.g. Header navigation). + __( `Unable to create Navigation Menu "%s".` ), + menuName + ), + { + cause: err, + } + ); + } } ); }, - [ convertClassicMenuToBlockMenu ] + [ convertClassicMenuToBlockMenu, throwOnError ] ); return { diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index dbf6b8c0f5ed26..79988c3ee350bb 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -65,6 +65,9 @@ function block_core_navigation_sort_menu_items_by_parent_id( $menu_items ) { return $menu_items_by_parent_id; } +} + +if ( gutenberg_should_block_use_interactivity_api( 'core/navigation' ) ) { /** * Add Interactivity API directives to the navigation-submenu and page-list blocks markup using the Tag Processor @@ -138,8 +141,23 @@ function block_core_navigation_add_directives_to_submenu( $w, $block_attributes } return $w->get_updated_html(); }; -} + /** + * Replaces view script for the Navigation block with version using Interactivity API. + * + * @param array $metadata Block metadata as read in via block.json. + * + * @return array Filtered block type metadata. + */ + function gutenberg_block_core_navigation_update_interactive_view_script( $metadata ) { + if ( 'core/navigation' === $metadata['name'] ) { + $metadata['viewScript'] = array( 'file:./view-interactivity.min.js' ); + $metadata['supports']['interactivity'] = true; + } + return $metadata; + } + add_filter( 'block_type_metadata', 'gutenberg_block_core_navigation_update_interactive_view_script', 10, 1 ); +} /** @@ -670,7 +688,7 @@ function render_block_core_navigation( $attributes, $content, $block ) { } // Add directives to the submenu if needed. - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN && $has_submenus && $should_load_view_script ) { + if ( gutenberg_should_block_use_interactivity_api( 'core/navigation' ) && $has_submenus && $should_load_view_script ) { $w = new WP_HTML_Tag_Processor( $inner_blocks_html ); $inner_blocks_html = block_core_navigation_add_directives_to_submenu( $w, $attributes ); } @@ -718,7 +736,7 @@ function render_block_core_navigation( $attributes, $content, $block ) { $responsive_container_directives = ''; $responsive_dialog_directives = ''; $close_button_directives = ''; - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN && $should_load_view_script ) { + if ( gutenberg_should_block_use_interactivity_api( 'core/navigation' ) && $should_load_view_script ) { $nav_element_directives = ' data-wp-interactive data-wp-context=\'{ "core": { "navigation": { "overlayOpenedBy": {}, "type": "overlay", "roleAttribute": "" } } }\' @@ -746,11 +764,11 @@ function render_block_core_navigation( $attributes, $content, $block ) { } $responsive_container_markup = sprintf( - ' + '
      -
      +
      - +
      %2$s
      diff --git a/packages/block-library/src/navigation/style.scss b/packages/block-library/src/navigation/style.scss index b0c8748075bdd7..180b40b43daca1 100644 --- a/packages/block-library/src/navigation/style.scss +++ b/packages/block-library/src/navigation/style.scss @@ -31,6 +31,7 @@ $navigation-icon-size: 24px; // Menu item container. .wp-block-navigation-item { + background-color: inherit; display: flex; align-items: center; position: relative; @@ -398,15 +399,26 @@ button.wp-block-navigation-item__content { } // Default background and font color. -.wp-block-navigation:not(.has-background) { +.wp-block-navigation:not(.has-background) .wp-block-navigation__submenu-container { + // Set a background color for submenus so that they're not transparent. + // NOTE TO DEVS - if refactoring this code, please double-check that + // submenus have a default background color, this feature has regressed + // several times, so care needs to be taken. + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.15); +} + +// If we do have a background color selected, inherit it from the navigation block +.wp-block-navigation.has-background { .wp-block-navigation__submenu-container { - // Set a background color for submenus so that they're not transparent. - // NOTE TO DEVS - if refactoring this code, please double-check that - // submenus have a default background color, this feature has regressed - // several times, so care needs to be taken. - background-color: #fff; + background-color: inherit; + } +} + +.wp-block-navigation:not(.has-text-color) { + .wp-block-navigation__submenu-container { + // Set a default color for submenu text if none is selected color: #000; - border: 1px solid rgba(0, 0, 0, 0.15); } } @@ -458,7 +470,8 @@ button.wp-block-navigation-item__content { right: 0; bottom: 0; - .wp-block-navigation-link a { + // Low specificity so that themes can override. + & :where(.wp-block-navigation-item a) { color: inherit; } @@ -579,6 +592,7 @@ button.wp-block-navigation-item__content { // Remove background colors for items inside the overlay menu. // Has to be !important to override global styles. .wp-block-navigation-item .wp-block-navigation__submenu-container, + .wp-block-navigation__container, .wp-block-navigation-item, .wp-block-page-list { color: inherit !important; @@ -620,9 +634,14 @@ button.wp-block-navigation-item__content { .wp-block-navigation:not(.has-background) .wp-block-navigation__responsive-container.is-menu-open { background-color: #fff; +} + +.wp-block-navigation:not(.has-text-color) +.wp-block-navigation__responsive-container.is-menu-open { color: #000; } + // Overlay menu toggle button label .wp-block-navigation__toggle_button_label { font-size: 1rem; diff --git a/packages/block-library/src/navigation/use-navigation-menu.js b/packages/block-library/src/navigation/use-navigation-menu.js index 30e5cdd99c0924..02df0ba2831dcc 100644 --- a/packages/block-library/src/navigation/use-navigation-menu.js +++ b/packages/block-library/src/navigation/use-navigation-menu.js @@ -4,85 +4,61 @@ import { store as coreStore, useResourcePermissions, + useEntityRecords, } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import { SELECT_NAVIGATION_MENUS_ARGS } from './constants'; +import { PRELOADED_NAVIGATION_MENUS_QUERY } from './constants'; export default function useNavigationMenu( ref ) { const permissions = useResourcePermissions( 'navigation', ref ); - return useSelect( + const { + navigationMenu, + isNavigationMenuResolved, + isNavigationMenuMissing, + } = useSelect( ( select ) => { - const { - canCreate, - canUpdate, - canDelete, - isResolving, - hasResolved, - } = permissions; - - const { - navigationMenus, - isResolvingNavigationMenus, - hasResolvedNavigationMenus, - } = selectNavigationMenus( select ); - - const { - navigationMenu, - isNavigationMenuResolved, - isNavigationMenuMissing, - } = selectExistingMenu( select, ref ); - - return { - navigationMenus, - isResolvingNavigationMenus, - hasResolvedNavigationMenus, - - navigationMenu, - isNavigationMenuResolved, - isNavigationMenuMissing, - - canSwitchNavigationMenu: ref - ? navigationMenus?.length > 1 - : navigationMenus?.length > 0, - - canUserCreateNavigationMenu: canCreate, - isResolvingCanUserCreateNavigationMenu: isResolving, - hasResolvedCanUserCreateNavigationMenu: hasResolved, - - canUserUpdateNavigationMenu: canUpdate, - hasResolvedCanUserUpdateNavigationMenu: ref - ? hasResolved - : undefined, - - canUserDeleteNavigationMenu: canDelete, - hasResolvedCanUserDeleteNavigationMenu: ref - ? hasResolved - : undefined, - }; + return selectExistingMenu( select, ref ); }, - [ ref, permissions ] + [ ref ] ); -} -function selectNavigationMenus( select ) { - const { getEntityRecords, hasFinishedResolution, isResolving } = - select( coreStore ); + const { canCreate, canUpdate, canDelete, isResolving, hasResolved } = + permissions; + + const { + records: navigationMenus, + isResolving: isResolvingNavigationMenus, + hasResolved: hasResolvedNavigationMenus, + } = useEntityRecords( + 'postType', + `wp_navigation`, + PRELOADED_NAVIGATION_MENUS_QUERY + ); + + const canSwitchNavigationMenu = ref + ? navigationMenus?.length > 1 + : navigationMenus?.length > 0; return { - navigationMenus: getEntityRecords( ...SELECT_NAVIGATION_MENUS_ARGS ), - isResolvingNavigationMenus: isResolving( - 'getEntityRecords', - SELECT_NAVIGATION_MENUS_ARGS - ), - hasResolvedNavigationMenus: hasFinishedResolution( - 'getEntityRecords', - SELECT_NAVIGATION_MENUS_ARGS - ), + navigationMenu, + isNavigationMenuResolved, + isNavigationMenuMissing, + navigationMenus, + isResolvingNavigationMenus, + hasResolvedNavigationMenus, + canSwitchNavigationMenu, + canUserCreateNavigationMenu: canCreate, + isResolvingCanUserCreateNavigationMenu: isResolving, + hasResolvedCanUserCreateNavigationMenu: hasResolved, + canUserUpdateNavigationMenu: canUpdate, + hasResolvedCanUserUpdateNavigationMenu: ref ? hasResolved : undefined, + canUserDeleteNavigationMenu: canDelete, + hasResolvedCanUserDeleteNavigationMenu: ref ? hasResolved : undefined, }; } diff --git a/packages/block-library/src/navigation/view-interactivity.js b/packages/block-library/src/navigation/view-interactivity.js new file mode 100644 index 00000000000000..b0d39ef3ca4d57 --- /dev/null +++ b/packages/block-library/src/navigation/view-interactivity.js @@ -0,0 +1,196 @@ +/** + * WordPress dependencies + */ +import { store as wpStore } from '@wordpress/interactivity'; + +const focusableSelectors = [ + 'a[href]', + 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', + 'select:not([disabled]):not([aria-hidden])', + 'textarea:not([disabled]):not([aria-hidden])', + 'button:not([disabled]):not([aria-hidden])', + '[contenteditable]', + '[tabindex]:not([tabindex^="-"])', +]; + +const openMenu = ( store, menuOpenedOn ) => { + const { context, ref, selectors } = store; + selectors.core.navigation.menuOpenedBy( store )[ menuOpenedOn ] = true; + context.core.navigation.previousFocus = ref; + if ( context.core.navigation.type === 'overlay' ) { + // Add a `has-modal-open` class to the root. + document.documentElement.classList.add( 'has-modal-open' ); + } +}; + +const closeMenu = ( store, menuClosedOn ) => { + const { context, selectors } = store; + selectors.core.navigation.menuOpenedBy( store )[ menuClosedOn ] = false; + // Check if the menu is still open or not. + if ( ! selectors.core.navigation.isMenuOpen( store ) ) { + if ( + context.core.navigation.modal?.contains( + window.document.activeElement + ) + ) { + context.core.navigation.previousFocus.focus(); + } + context.core.navigation.modal = null; + context.core.navigation.previousFocus = null; + if ( context.core.navigation.type === 'overlay' ) { + document.documentElement.classList.remove( 'has-modal-open' ); + } + } +}; + +wpStore( { + effects: { + core: { + navigation: { + initMenu: ( store ) => { + const { context, selectors, ref } = store; + if ( selectors.core.navigation.isMenuOpen( store ) ) { + const focusableElements = + ref.querySelectorAll( focusableSelectors ); + context.core.navigation.modal = ref; + context.core.navigation.firstFocusableElement = + focusableElements[ 0 ]; + context.core.navigation.lastFocusableElement = + focusableElements[ focusableElements.length - 1 ]; + } + }, + focusFirstElement: ( store ) => { + const { selectors, ref } = store; + if ( selectors.core.navigation.isMenuOpen( store ) ) { + ref.querySelector( + '.wp-block-navigation-item > *:first-child' + ).focus(); + } + }, + }, + }, + }, + selectors: { + core: { + navigation: { + roleAttribute: ( store ) => { + const { context, selectors } = store; + return context.core.navigation.type === 'overlay' && + selectors.core.navigation.isMenuOpen( store ) + ? 'dialog' + : ''; + }, + isMenuOpen: ( { context } ) => + // The menu is opened if either `click`, `hover` or `focus` is true. + Object.values( + context.core.navigation[ + context.core.navigation.type === 'overlay' + ? 'overlayOpenedBy' + : 'submenuOpenedBy' + ] + ).filter( Boolean ).length > 0, + menuOpenedBy: ( { context } ) => + context.core.navigation[ + context.core.navigation.type === 'overlay' + ? 'overlayOpenedBy' + : 'submenuOpenedBy' + ], + }, + }, + }, + actions: { + core: { + navigation: { + openMenuOnHover( store ) { + const { navigation } = store.context.core; + if ( + navigation.type === 'submenu' && + // Only open on hover if the overlay is closed. + Object.values( + navigation.overlayOpenedBy || {} + ).filter( Boolean ).length === 0 + ) + openMenu( store, 'hover' ); + }, + closeMenuOnHover( store ) { + closeMenu( store, 'hover' ); + }, + openMenuOnClick( store ) { + openMenu( store, 'click' ); + }, + closeMenuOnClick( store ) { + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); + }, + openMenuOnFocus( store ) { + openMenu( store, 'focus' ); + }, + toggleMenuOnClick: ( store ) => { + const { selectors } = store; + const menuOpenedBy = + selectors.core.navigation.menuOpenedBy( store ); + if ( menuOpenedBy.click || menuOpenedBy.focus ) { + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); + } else { + openMenu( store, 'click' ); + } + }, + handleMenuKeydown: ( store ) => { + const { context, selectors, event } = store; + if ( + selectors.core.navigation.menuOpenedBy( store ).click + ) { + // If Escape close the menu. + if ( event?.key === 'Escape' ) { + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); + return; + } + + // Trap focus if it is an overlay (main menu). + if ( + context.core.navigation.type === 'overlay' && + event.key === 'Tab' + ) { + // If shift + tab it change the direction. + if ( + event.shiftKey && + window.document.activeElement === + context.core.navigation + .firstFocusableElement + ) { + event.preventDefault(); + context.core.navigation.lastFocusableElement.focus(); + } else if ( + ! event.shiftKey && + window.document.activeElement === + context.core.navigation.lastFocusableElement + ) { + event.preventDefault(); + context.core.navigation.firstFocusableElement.focus(); + } + } + } + }, + handleMenuFocusout: ( store ) => { + const { context, event } = store; + // If focus is outside modal, and in the document, close menu + // event.target === The element losing focus + // event.relatedTarget === The element receiving focus (if any) + // When focusout is outsite the document, + // `window.document.activeElement` doesn't change. + if ( + ! context.core.navigation.modal?.contains( + event.relatedTarget + ) && + event.target !== window.document.activeElement + ) { + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); + } + }, + }, + }, + }, +} ); diff --git a/packages/block-library/src/navigation/view-modal.js b/packages/block-library/src/navigation/view-modal.js new file mode 100644 index 00000000000000..9477d262816d93 --- /dev/null +++ b/packages/block-library/src/navigation/view-modal.js @@ -0,0 +1,78 @@ +/** + * External dependencies + */ +import MicroModal from 'micromodal'; + +// Responsive navigation toggle. +function navigationToggleModal( modal ) { + const dialogContainer = modal.querySelector( + `.wp-block-navigation__responsive-dialog` + ); + + const isHidden = 'true' === modal.getAttribute( 'aria-hidden' ); + + modal.classList.toggle( 'has-modal-open', ! isHidden ); + dialogContainer.toggleAttribute( 'aria-modal', ! isHidden ); + + if ( isHidden ) { + dialogContainer.removeAttribute( 'role' ); + dialogContainer.removeAttribute( 'aria-modal' ); + } else { + dialogContainer.setAttribute( 'role', 'dialog' ); + dialogContainer.setAttribute( 'aria-modal', 'true' ); + } + + // Add a class to indicate the modal is open. + const htmlElement = document.documentElement; + htmlElement.classList.toggle( 'has-modal-open' ); +} + +function isLinkToAnchorOnCurrentPage( node ) { + return ( + node.hash && + node.protocol === window.location.protocol && + node.host === window.location.host && + node.pathname === window.location.pathname && + node.search === window.location.search + ); +} + +window.addEventListener( 'load', () => { + MicroModal.init( { + onShow: navigationToggleModal, + onClose: navigationToggleModal, + openClass: 'is-menu-open', + } ); + + // Close modal automatically on clicking anchor links inside modal. + const navigationLinks = document.querySelectorAll( + '.wp-block-navigation-item__content' + ); + + navigationLinks.forEach( function ( link ) { + // Ignore non-anchor links and anchor links which open on a new tab. + if ( + ! isLinkToAnchorOnCurrentPage( link ) || + link.attributes?.target === '_blank' + ) { + return; + } + + // Find the specific parent modal for this link + // since .close() won't work without an ID if there are + // multiple navigation menus in a post/page. + const modal = link.closest( + '.wp-block-navigation__responsive-container' + ); + const modalId = modal?.getAttribute( 'id' ); + + link.addEventListener( 'click', () => { + // check if modal exists and is open before trying to close it + // otherwise Micromodal will toggle the `has-modal-open` class + // on the html tag which prevents scrolling + if ( modalId && modal.classList.contains( 'has-modal-open' ) ) { + MicroModal.close( modalId ); + } + } ); + } ); +} ); diff --git a/packages/block-library/src/navigation/view.js b/packages/block-library/src/navigation/view.js index b0d39ef3ca4d57..19805a44ae4ae2 100644 --- a/packages/block-library/src/navigation/view.js +++ b/packages/block-library/src/navigation/view.js @@ -1,196 +1,74 @@ -/** - * WordPress dependencies - */ -import { store as wpStore } from '@wordpress/interactivity'; +// Open on click functionality. +function closeSubmenus( element ) { + element + .querySelectorAll( '[aria-expanded="true"]' ) + .forEach( function ( toggle ) { + toggle.setAttribute( 'aria-expanded', 'false' ); + } ); +} -const focusableSelectors = [ - 'a[href]', - 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', - 'select:not([disabled]):not([aria-hidden])', - 'textarea:not([disabled]):not([aria-hidden])', - 'button:not([disabled]):not([aria-hidden])', - '[contenteditable]', - '[tabindex]:not([tabindex^="-"])', -]; +function toggleSubmenuOnClick( event ) { + const buttonToggle = event.target.closest( '[aria-expanded]' ); + const isSubmenuOpen = buttonToggle.getAttribute( 'aria-expanded' ); -const openMenu = ( store, menuOpenedOn ) => { - const { context, ref, selectors } = store; - selectors.core.navigation.menuOpenedBy( store )[ menuOpenedOn ] = true; - context.core.navigation.previousFocus = ref; - if ( context.core.navigation.type === 'overlay' ) { - // Add a `has-modal-open` class to the root. - document.documentElement.classList.add( 'has-modal-open' ); + if ( isSubmenuOpen === 'true' ) { + closeSubmenus( buttonToggle.closest( '.wp-block-navigation-item' ) ); + } else { + // Close all sibling submenus. + const parentElement = buttonToggle.closest( + '.wp-block-navigation-item' + ); + const navigationParent = buttonToggle.closest( + '.wp-block-navigation__submenu-container, .wp-block-navigation__container, .wp-block-page-list' + ); + navigationParent + .querySelectorAll( '.wp-block-navigation-item' ) + .forEach( function ( child ) { + if ( child !== parentElement ) { + closeSubmenus( child ); + } + } ); + // Open submenu. + buttonToggle.setAttribute( 'aria-expanded', 'true' ); } -}; +} -const closeMenu = ( store, menuClosedOn ) => { - const { context, selectors } = store; - selectors.core.navigation.menuOpenedBy( store )[ menuClosedOn ] = false; - // Check if the menu is still open or not. - if ( ! selectors.core.navigation.isMenuOpen( store ) ) { - if ( - context.core.navigation.modal?.contains( - window.document.activeElement - ) - ) { - context.core.navigation.previousFocus.focus(); - } - context.core.navigation.modal = null; - context.core.navigation.previousFocus = null; - if ( context.core.navigation.type === 'overlay' ) { - document.documentElement.classList.remove( 'has-modal-open' ); - } - } -}; +// Necessary for some themes such as TT1 Blocks, where +// scripts could be loaded before the body. +window.addEventListener( 'load', () => { + const submenuButtons = document.querySelectorAll( + '.wp-block-navigation-submenu__toggle' + ); -wpStore( { - effects: { - core: { - navigation: { - initMenu: ( store ) => { - const { context, selectors, ref } = store; - if ( selectors.core.navigation.isMenuOpen( store ) ) { - const focusableElements = - ref.querySelectorAll( focusableSelectors ); - context.core.navigation.modal = ref; - context.core.navigation.firstFocusableElement = - focusableElements[ 0 ]; - context.core.navigation.lastFocusableElement = - focusableElements[ focusableElements.length - 1 ]; - } - }, - focusFirstElement: ( store ) => { - const { selectors, ref } = store; - if ( selectors.core.navigation.isMenuOpen( store ) ) { - ref.querySelector( - '.wp-block-navigation-item > *:first-child' - ).focus(); - } - }, - }, - }, - }, - selectors: { - core: { - navigation: { - roleAttribute: ( store ) => { - const { context, selectors } = store; - return context.core.navigation.type === 'overlay' && - selectors.core.navigation.isMenuOpen( store ) - ? 'dialog' - : ''; - }, - isMenuOpen: ( { context } ) => - // The menu is opened if either `click`, `hover` or `focus` is true. - Object.values( - context.core.navigation[ - context.core.navigation.type === 'overlay' - ? 'overlayOpenedBy' - : 'submenuOpenedBy' - ] - ).filter( Boolean ).length > 0, - menuOpenedBy: ( { context } ) => - context.core.navigation[ - context.core.navigation.type === 'overlay' - ? 'overlayOpenedBy' - : 'submenuOpenedBy' - ], - }, - }, - }, - actions: { - core: { - navigation: { - openMenuOnHover( store ) { - const { navigation } = store.context.core; - if ( - navigation.type === 'submenu' && - // Only open on hover if the overlay is closed. - Object.values( - navigation.overlayOpenedBy || {} - ).filter( Boolean ).length === 0 - ) - openMenu( store, 'hover' ); - }, - closeMenuOnHover( store ) { - closeMenu( store, 'hover' ); - }, - openMenuOnClick( store ) { - openMenu( store, 'click' ); - }, - closeMenuOnClick( store ) { - closeMenu( store, 'click' ); - closeMenu( store, 'focus' ); - }, - openMenuOnFocus( store ) { - openMenu( store, 'focus' ); - }, - toggleMenuOnClick: ( store ) => { - const { selectors } = store; - const menuOpenedBy = - selectors.core.navigation.menuOpenedBy( store ); - if ( menuOpenedBy.click || menuOpenedBy.focus ) { - closeMenu( store, 'click' ); - closeMenu( store, 'focus' ); - } else { - openMenu( store, 'click' ); - } - }, - handleMenuKeydown: ( store ) => { - const { context, selectors, event } = store; - if ( - selectors.core.navigation.menuOpenedBy( store ).click - ) { - // If Escape close the menu. - if ( event?.key === 'Escape' ) { - closeMenu( store, 'click' ); - closeMenu( store, 'focus' ); - return; - } + submenuButtons.forEach( function ( button ) { + button.addEventListener( 'click', toggleSubmenuOnClick ); + } ); - // Trap focus if it is an overlay (main menu). - if ( - context.core.navigation.type === 'overlay' && - event.key === 'Tab' - ) { - // If shift + tab it change the direction. - if ( - event.shiftKey && - window.document.activeElement === - context.core.navigation - .firstFocusableElement - ) { - event.preventDefault(); - context.core.navigation.lastFocusableElement.focus(); - } else if ( - ! event.shiftKey && - window.document.activeElement === - context.core.navigation.lastFocusableElement - ) { - event.preventDefault(); - context.core.navigation.firstFocusableElement.focus(); - } - } - } - }, - handleMenuFocusout: ( store ) => { - const { context, event } = store; - // If focus is outside modal, and in the document, close menu - // event.target === The element losing focus - // event.relatedTarget === The element receiving focus (if any) - // When focusout is outsite the document, - // `window.document.activeElement` doesn't change. - if ( - ! context.core.navigation.modal?.contains( - event.relatedTarget - ) && - event.target !== window.document.activeElement - ) { - closeMenu( store, 'click' ); - closeMenu( store, 'focus' ); - } - }, - }, - }, - }, + // Close on click outside. + document.addEventListener( 'click', function ( event ) { + const navigationBlocks = document.querySelectorAll( + '.wp-block-navigation' + ); + navigationBlocks.forEach( function ( block ) { + if ( ! block.contains( event.target ) ) { + closeSubmenus( block ); + } + } ); + } ); + // Close on focus outside or escape key. + document.addEventListener( 'keyup', function ( event ) { + const submenuBlocks = document.querySelectorAll( + '.wp-block-navigation-item.has-child' + ); + submenuBlocks.forEach( function ( block ) { + if ( ! block.contains( event.target ) ) { + closeSubmenus( block ); + } else if ( event.key === 'Escape' ) { + const toggle = block.querySelector( '[aria-expanded="true"]' ); + closeSubmenus( block ); + // Focus the submenu trigger so focus does not get trapped in the closed submenu. + toggle?.focus(); + } + } ); + } ); } ); diff --git a/packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap index c1bcb2e853c8cd..adc6ab4210efa5 100644 --- a/packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap @@ -32,6 +32,7 @@ exports[`Paragraph block should render without crashing and match snapshot 1`] = onSelectionChange={[Function]} placeholder="Start writing…" placeholderTextColor="gray" + selectionColor="black" triggerKeyCodes={[]} value="" /> diff --git a/packages/block-library/src/paragraph/test/edit.native.js b/packages/block-library/src/paragraph/test/edit.native.js index 945b88ae997f58..8e439811cfadc2 100644 --- a/packages/block-library/src/paragraph/test/edit.native.js +++ b/packages/block-library/src/paragraph/test/edit.native.js @@ -639,4 +639,30 @@ describe( 'Paragraph block', () => { ); expect( contrastCheckElement ).toBeDefined(); } ); + + it( 'should highlight text with selection', async () => { + // Arrange + const screen = await initializeEditor( { withGlobalStyles: true } ); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + const paragraphTextInput = + within( paragraphBlock ).getByPlaceholderText( 'Start writing…' ); + typeInRichText( + paragraphTextInput, + 'A quick brown fox jumps over the lazy dog.', + { finalSelectionStart: 2, finalSelectionEnd: 7 } + ); + fireEvent.press( screen.getByLabelText( 'Text color' ) ); + fireEvent.press( await screen.findByLabelText( 'Tertiary' ) ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +

      A quick brown fox jumps over the lazy dog.

      + " + ` ); + } ); } ); diff --git a/packages/block-library/src/post-template/index.php b/packages/block-library/src/post-template/index.php index 3c023c80ed263d..b1499d845f39a6 100644 --- a/packages/block-library/src/post-template/index.php +++ b/packages/block-library/src/post-template/index.php @@ -97,11 +97,13 @@ function render_block_core_post_template( $attributes, $content, $block ) { $context['postId'] = $post_id; return $context; }; - add_filter( 'render_block_context', $filter_block_context ); + + // Use an early priority to so that other 'render_block_context' filters have access to the values. + add_filter( 'render_block_context', $filter_block_context, 1 ); // Render the inner blocks of the Post Template block with `dynamic` set to `false` to prevent calling // `render_callback` and ensure that no wrapper markup is included. $block_content = ( new WP_Block( $block_instance ) )->render( array( 'dynamic' => false ) ); - remove_filter( 'render_block_context', $filter_block_context ); + remove_filter( 'render_block_context', $filter_block_context, 1 ); // Wrap the render inner blocks in a `li` element with the appropriate post classes. $post_classes = implode( ' ', get_post_class( 'wp-block-post' ) ); diff --git a/packages/block-library/src/post-title/index.php b/packages/block-library/src/post-title/index.php index e1d4b255c57733..1769b199cebf17 100644 --- a/packages/block-library/src/post-title/index.php +++ b/packages/block-library/src/post-title/index.php @@ -19,8 +19,11 @@ function render_block_core_post_title( $attributes, $content, $block ) { return ''; } - $post = get_post( $block->context['postId'] ); - $title = get_the_title( $post ); + /** + * The `$post` argument is intentionally omitted so that changes are reflected when previewing a post. + * See: https://github.com/WordPress/gutenberg/pull/37622#issuecomment-1000932816. + */ + $title = get_the_title(); if ( ! $title ) { return ''; @@ -33,7 +36,7 @@ function render_block_core_post_title( $attributes, $content, $block ) { if ( isset( $attributes['isLink'] ) && $attributes['isLink'] ) { $rel = ! empty( $attributes['rel'] ) ? 'rel="' . esc_attr( $attributes['rel'] ) . '"' : ''; - $title = sprintf( '%4$s', get_the_permalink( $post ), esc_attr( $attributes['linkTarget'] ), $rel, $title ); + $title = sprintf( '%4$s', get_the_permalink( $block->context['postId'] ), esc_attr( $attributes['linkTarget'] ), $rel, $title ); } $classes = array(); diff --git a/packages/block-library/src/preformatted/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/preformatted/test/__snapshots__/edit.native.js.snap index 7c0a7aa1dcf48c..db1c80c514206b 100644 --- a/packages/block-library/src/preformatted/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/preformatted/test/__snapshots__/edit.native.js.snap @@ -39,6 +39,7 @@ exports[`Preformatted should match snapshot when content is empty 1`] = ` onSelectionChange={[Function]} placeholder="Write preformatted text…" placeholderTextColor="gray" + selectionColor="black" triggerKeyCodes={[]} value="" /> @@ -85,6 +86,7 @@ exports[`Preformatted should match snapshot when content is not empty 1`] = ` onSelectionChange={[Function]} placeholder="Write preformatted text…" placeholderTextColor="gray" + selectionColor="black" triggerKeyCodes={[]} value="
      Hello World!
      " /> diff --git a/packages/block-library/src/query-pagination/edit.js b/packages/block-library/src/query-pagination/edit.js index 8ab1f63377949b..7598eba5c1cacf 100644 --- a/packages/block-library/src/query-pagination/edit.js +++ b/packages/block-library/src/query-pagination/edit.js @@ -34,20 +34,23 @@ export default function QueryPaginationEdit( { setAttributes, clientId, } ) { - const hasNextPreviousBlocks = useSelect( ( select ) => { - const { getBlocks } = select( blockEditorStore ); - const innerBlocks = getBlocks( clientId ); - /** - * Show the `paginationArrow` and `showLabel` controls only if a - * `QueryPaginationNext/Previous` block exists. - */ - return innerBlocks?.find( ( innerBlock ) => { - return [ - 'core/query-pagination-next', - 'core/query-pagination-previous', - ].includes( innerBlock.name ); - } ); - }, [] ); + const hasNextPreviousBlocks = useSelect( + ( select ) => { + const { getBlocks } = select( blockEditorStore ); + const innerBlocks = getBlocks( clientId ); + /** + * Show the `paginationArrow` and `showLabel` controls only if a + * `QueryPaginationNext/Previous` block exists. + */ + return innerBlocks?.find( ( innerBlock ) => { + return [ + 'core/query-pagination-next', + 'core/query-pagination-previous', + ].includes( innerBlock.name ); + } ); + }, + [ clientId ] + ); const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps( blockProps, { template: TEMPLATE, diff --git a/packages/block-library/src/query-title/edit.js b/packages/block-library/src/query-title/edit.js index e5fa6f6c396774..b74e03e7583b2d 100644 --- a/packages/block-library/src/query-title/edit.js +++ b/packages/block-library/src/query-title/edit.js @@ -13,9 +13,11 @@ import { useBlockProps, Warning, HeadingLevelDropdown, + store as blockEditorStore, } from '@wordpress/block-editor'; import { ToggleControl, PanelBody } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; const SUPPORTED_TYPES = [ 'archive', 'search' ]; @@ -23,6 +25,18 @@ export default function QueryTitleEdit( { attributes: { type, level, textAlign, showPrefix, showSearchTerm }, setAttributes, } ) { + const { archiveTypeTitle, archiveNameLabel } = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + const { + __experimentalArchiveTitleNameLabel, + __experimentalArchiveTitleTypeLabel, + } = getSettings(); + return { + archiveTypeTitle: __experimentalArchiveTitleTypeLabel, + archiveNameLabel: __experimentalArchiveTitleNameLabel, + }; + } ); + const TagName = `h${ level }`; const blockProps = useBlockProps( { className: classnames( 'wp-block-query-title__placeholder', { @@ -40,6 +54,38 @@ export default function QueryTitleEdit( { let titleElement; if ( type === 'archive' ) { + let title; + if ( archiveTypeTitle ) { + if ( showPrefix ) { + if ( archiveNameLabel ) { + title = sprintf( + /* translators: 1: Archive type title e.g: "Category", 2: Label of the archive e.g: "Shoes" */ + __( '%1$s: %2$s' ), + archiveTypeTitle, + archiveNameLabel + ); + } else { + title = sprintf( + /* translators: %s: Archive type title e.g: "Category", "Tag"... */ + __( '%s: Name' ), + archiveTypeTitle + ); + } + } else if ( archiveNameLabel ) { + title = archiveNameLabel; + } else { + title = sprintf( + /* translators: %s: Archive type title e.g: "Category", "Tag"... */ + __( '%s name' ), + archiveTypeTitle + ); + } + } else { + title = showPrefix + ? __( 'Archive type: Name' ) + : __( 'Archive title' ); + } + titleElement = ( <> @@ -54,11 +100,7 @@ export default function QueryTitleEdit( { /> - - { showPrefix - ? __( 'Archive type: Name' ) - : __( 'Archive title' ) } - + { title } ); } diff --git a/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap index 5b5df918f2beeb..65d87d5b0d7bd4 100644 --- a/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap +++ b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap @@ -22,6 +22,16 @@ exports[`Quote block transforms to Group block 1`] = ` " `; +exports[`Quote block transforms to Paragraph block 1`] = ` +" +

      "This will make running your own blog a viable alternative again."

      + + + +

      Adrian Zumbrunnen

      +" +`; + exports[`Quote block transforms to Pullquote block 1`] = ` "

      "This will make running your own blog a viable alternative again."

      Adrian Zumbrunnen
      diff --git a/packages/block-library/src/quote/test/transforms.native.js b/packages/block-library/src/quote/test/transforms.native.js index 46c4eb2b6f9727..25030e0a018d41 100644 --- a/packages/block-library/src/quote/test/transforms.native.js +++ b/packages/block-library/src/quote/test/transforms.native.js @@ -21,7 +21,11 @@ const initialHtml = ` `; const transformsWithInnerBlocks = [ 'Columns', 'Group' ]; -const blockTransforms = [ 'Pullquote', ...transformsWithInnerBlocks ]; +const blockTransforms = [ + 'Pullquote', + 'Paragraph', + ...transformsWithInnerBlocks, +]; setupCoreBlocks(); diff --git a/packages/block-library/src/quote/transforms.js b/packages/block-library/src/quote/transforms.js index d4cd77177bf030..4e153a6399029f 100644 --- a/packages/block-library/src/quote/transforms.js +++ b/packages/block-library/src/quote/transforms.js @@ -109,6 +109,19 @@ const transforms = { } ); }, }, + { + type: 'block', + blocks: [ 'core/paragraph' ], + transform: ( { citation }, innerBlocks ) => + citation + ? [ + ...innerBlocks, + createBlock( 'core/paragraph', { + content: citation, + } ), + ] + : innerBlocks, + }, { type: 'block', blocks: [ 'core/group' ], diff --git a/packages/block-library/src/rss/edit.js b/packages/block-library/src/rss/edit.js index 2fd94cdd2a021a..d24de5c291d511 100644 --- a/packages/block-library/src/rss/edit.js +++ b/packages/block-library/src/rss/edit.js @@ -116,6 +116,7 @@ export default function RSSEdit( { attributes, setAttributes } ) { @@ -146,6 +147,7 @@ export default function RSSEdit( { attributes, setAttributes } ) { { displayExcerpt && ( @@ -159,6 +161,7 @@ export default function RSSEdit( { attributes, setAttributes } ) { { blockLayout === 'grid' && ( diff --git a/packages/block-library/src/search/block.json b/packages/block-library/src/search/block.json index 1a23590699af4e..b2873bfa8e5729 100644 --- a/packages/block-library/src/search/block.json +++ b/packages/block-library/src/search/block.json @@ -90,6 +90,7 @@ }, "html": false }, + "viewScript": "file:./view.min.js", "editorStyle": "wp-block-search-editor", "style": "wp-block-search" } diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index 7cfa293076b3d1..80238ab1741f7e 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -97,8 +97,8 @@ export default function SearchEdit( { ); const { __unstableMarkNextChangeAsNotPersistent } = useDispatch( blockEditorStore ); - useEffect( () => { - if ( ! insertedInNavigationBlock ) return; + + if ( insertedInNavigationBlock ) { // This side-effect should not create an undo level. __unstableMarkNextChangeAsNotPersistent(); setAttributes( { @@ -106,7 +106,8 @@ export default function SearchEdit( { buttonUseIcon: true, buttonPosition: 'button-inside', } ); - }, [ insertedInNavigationBlock ] ); + } + const borderRadius = style?.border?.radius; const borderProps = useBorderProps( attributes ); diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php index ce76587dbbb441..700a299a92457a 100644 --- a/packages/block-library/src/search/index.php +++ b/packages/block-library/src/search/index.php @@ -8,11 +8,13 @@ /** * Dynamically renders the `core/search` block. * - * @param array $attributes The block attributes. + * @param array $attributes The block attributes. + * @param string $content The saved content. + * @param WP_Block $block The parsed block. * * @return string The search block markup. */ -function render_block_core_search( $attributes ) { +function render_block_core_search( $attributes, $content, $block ) { // Older versions of the Search block defaulted the label and buttonText // attributes to `__( 'Search' )` meaning that many posts contain ``. Support these by defaulting an undefined label and @@ -70,10 +72,26 @@ function render_block_core_search( $attributes ) { $input->set_attribute( 'id', $input_id ); $input->set_attribute( 'value', get_search_query() ); $input->set_attribute( 'placeholder', $attributes['placeholder'] ); - if ( 'button-only' === $button_position && 'expand-searchfield' === $button_behavior ) { + + $is_expandable_searchfield = 'button-only' === $button_position && 'expand-searchfield' === $button_behavior; + if ( $is_expandable_searchfield ) { $input->set_attribute( 'aria-hidden', 'true' ); $input->set_attribute( 'tabindex', '-1' ); - wp_enqueue_script( 'wp-block--search-view', plugins_url( 'search/view.min.js', __FILE__ ) ); + } + + // If the script already exists, there is no point in removing it from viewScript. + $view_js_file = 'wp-block-search-view'; + if ( ! wp_script_is( $view_js_file ) ) { + $script_handles = $block->block_type->view_script_handles; + + // If the script is not needed, and it is still in the `view_script_handles`, remove it. + if ( ! $is_expandable_searchfield && in_array( $view_js_file, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) ); + } + // If the script is needed, but it was previously removed, add it again. + if ( $is_expandable_searchfield && ! in_array( $view_js_file, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) ); + } } } diff --git a/packages/block-library/src/search/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/search/test/__snapshots__/edit.native.js.snap index 47652cd448c328..c63e66508f324f 100644 --- a/packages/block-library/src/search/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/search/test/__snapshots__/edit.native.js.snap @@ -52,6 +52,7 @@ exports[`Search Block renders block with button inside option 1`] = ` onStartShouldSetResponder={[Function]} placeholder="Add label…" placeholderTextColor="gray" + selectionColor="black" style={ { "backgroundColor": undefined, @@ -168,6 +169,7 @@ exports[`Search Block renders block with button inside option 1`] = ` onStartShouldSetResponder={[Function]} placeholder="" placeholderTextColor="gray" + selectionColor="black" style={ { "backgroundColor": undefined, @@ -246,6 +248,7 @@ exports[`Search Block renders block with icon button option matches snapshot 1`] onStartShouldSetResponder={[Function]} placeholder="Add label…" placeholderTextColor="gray" + selectionColor="black" style={ { "backgroundColor": undefined, @@ -432,6 +435,7 @@ exports[`Search Block renders block with label hidden matches snapshot 1`] = ` onStartShouldSetResponder={[Function]} placeholder="" placeholderTextColor="gray" + selectionColor="black" style={ { "backgroundColor": undefined, @@ -510,6 +514,7 @@ exports[`Search Block renders with default configuration matches snapshot 1`] = onStartShouldSetResponder={[Function]} placeholder="Add label…" placeholderTextColor="gray" + selectionColor="black" style={ { "backgroundColor": undefined, @@ -626,6 +631,7 @@ exports[`Search Block renders with default configuration matches snapshot 1`] = onStartShouldSetResponder={[Function]} placeholder="" placeholderTextColor="gray" + selectionColor="black" style={ { "backgroundColor": undefined, @@ -704,6 +710,7 @@ exports[`Search Block renders with no-button option matches snapshot 1`] = ` onStartShouldSetResponder={[Function]} placeholder="Add label…" placeholderTextColor="gray" + selectionColor="black" style={ { "backgroundColor": undefined, diff --git a/packages/block-library/src/site-logo/edit.js b/packages/block-library/src/site-logo/edit.js index 8fe86d6d4a6dec..cddfb5dfdc4fc4 100644 --- a/packages/block-library/src/site-logo/edit.js +++ b/packages/block-library/src/site-logo/edit.js @@ -293,6 +293,7 @@ const SiteLogo = ( { setAttributes( { width: newWidth } ) diff --git a/packages/block-library/src/tag-cloud/edit.js b/packages/block-library/src/tag-cloud/edit.js index 714de24a38a149..4dbec86fff520c 100644 --- a/packages/block-library/src/tag-cloud/edit.js +++ b/packages/block-library/src/tag-cloud/edit.js @@ -122,6 +122,7 @@ function TagCloudEdit( { attributes, setAttributes, taxonomies } ) { /> diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index b7ae4f6043afb1..a4d2e5f2a017e2 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -77,7 +77,7 @@ export default function TemplatePartEdit( { area: _area, }; }, - [ templatePartId, clientId ] + [ templatePartId, attributes.area, clientId ] ); const { templateParts } = useAlternativeTemplateParts( area, diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index 84e3a985ec4410..1066aa01419159 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -18,12 +18,11 @@ function render_block_core_template_part( $attributes ) { $template_part_id = null; $content = null; $area = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; - $stylesheet = get_stylesheet(); if ( isset( $attributes['slug'] ) && isset( $attributes['theme'] ) && - $stylesheet === $attributes['theme'] + get_stylesheet() === $attributes['theme'] ) { $template_part_id = $attributes['theme'] . '//' . $attributes['slug']; $template_part_query = new WP_Query( @@ -64,22 +63,17 @@ function render_block_core_template_part( $attributes ) { */ do_action( 'render_block_core_template_part_post', $template_part_id, $attributes, $template_part_post, $content ); } else { + $template_part_file_path = ''; // Else, if the template part was provided by the active theme, // render the corresponding file content. if ( 0 === validate_file( $attributes['slug'] ) ) { - $themes = array( $stylesheet ); - $template = get_template(); - if ( $stylesheet !== $template ) { - $themes[] = $template; - } - - foreach ( $themes as $theme ) { - $theme_folders = get_block_theme_folders( $theme ); - $template_part_file_path = get_theme_file_path( '/' . $theme_folders['wp_template_part'] . '/' . $attributes['slug'] . '.html' ); - if ( file_exists( $template_part_file_path ) ) { - $content = (string) file_get_contents( $template_part_file_path ); - $content = '' !== $content ? _inject_theme_attribute_in_block_template_content( $content ) : ''; - break; + $block_template_file = _get_block_template_file( 'wp_template_part', $attributes['slug'] ); + if ( $block_template_file ) { + $template_part_file_path = $block_template_file['path']; + $content = (string) file_get_contents( $template_part_file_path ); + $content = '' !== $content ? _inject_theme_attribute_in_block_template_content( $content ) : ''; + if ( isset( $block_template_file['area'] ) ) { + $area = $block_template_file['area']; } } } diff --git a/packages/block-library/src/text-columns/edit.js b/packages/block-library/src/text-columns/edit.js index 7e7946927b1d29..761d047e90b1c9 100644 --- a/packages/block-library/src/text-columns/edit.js +++ b/packages/block-library/src/text-columns/edit.js @@ -35,6 +35,7 @@ export default function TextColumnsEdit( { attributes, setAttributes } ) { diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 03f0ad1563448f..f4661358d27a97 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,18 @@ ## Unreleased +### Enhancements + +- `TextControl`: Add `id` prop to allow for custom IDs in `TextControl`s ([#52028](https://github.com/WordPress/gutenberg/pull/52028)). +- `Navigator`: Add `replace` option to `navigator.goTo()` and `navigator.goToParent()` ([#52456](https://github.com/WordPress/gutenberg/pull/52456)). + +### Bug Fix + +- `Popover`: Pin `react-dropdown-menu` version to avoid breaking changes in dependency updates. ([#52356](https://github.com/WordPress/gutenberg/pull/52356)). +- `Item`: Unify focus style and add default font styles. ([#52495](https://github.com/WordPress/gutenberg/pull/52495)). +- `Toolbar`: Fix toolbar items not being tabbable on the first render. ([#52613](https://github.com/WordPress/gutenberg/pull/52613)) +- `FormTokenField`: Fix token overflow when moving cursor left or right. ([#52662](https://github.com/WordPress/gutenberg/pull/52662)) + ## 25.3.0 (2023-07-05) ### Enhancements @@ -28,6 +40,7 @@ - `UnitControl`: Revamp support for changing unit by typing ([#39303](https://github.com/WordPress/gutenberg/pull/39303)). - `Modal`: Update corner radius to be between buttons and the site view frame, in a 2-4-8 system. ([#51254](https://github.com/WordPress/gutenberg/pull/51254)). - `ItemGroup`: Update button focus state styles to be inline with other button focus states in the editor. ([#51576](https://github.com/WordPress/gutenberg/pull/51576)). +- `ItemGroup`: Update button focus state styles to target `:focus-visible` rather than `:focus`. ([#51787](https://github.com/WordPress/gutenberg/pull/51787)). ### Bug Fix diff --git a/packages/components/package.json b/packages/components/package.json index 2b6595215011e5..455d3daa3c089d 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -30,7 +30,7 @@ ], "types": "build-types", "dependencies": { - "@ariakit/react": "^0.2.10", + "@ariakit/react": "^0.2.12", "@babel/runtime": "^7.16.0", "@emotion/cache": "^11.7.1", "@emotion/css": "^11.7.1", @@ -39,7 +39,7 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "1.0.0", - "@radix-ui/react-dropdown-menu": "^2.0.4", + "@radix-ui/react-dropdown-menu": "2.0.4", "@use-gesture/react": "^10.2.24", "@wordpress/a11y": "file:../a11y", "@wordpress/compose": "file:../compose", diff --git a/packages/components/src/button/index.native.js b/packages/components/src/button/index.native.js index 02820e1731f71c..95ca9d36b3791b 100644 --- a/packages/components/src/button/index.native.js +++ b/packages/components/src/button/index.native.js @@ -24,6 +24,7 @@ import { */ import Tooltip from '../tooltip'; import Icon from '../icon'; +import style from './style.scss'; const isAndroid = Platform.OS === 'android'; const marginBottom = isAndroid ? -0.5 : 0; @@ -51,8 +52,6 @@ const styles = StyleSheet.create( { justifyContent: 'center', alignItems: 'center', borderRadius: 6, - borderColor: '#2e4453', - backgroundColor: '#2e4453', }, subscriptInactive: { color: '#7b9ab1', // $toolbar-button. @@ -95,6 +94,7 @@ export function Button( props ) { tooltipPosition, isActiveStyle, customContainerStyles, + hitSlop, } = props; const preferredColorScheme = usePreferredColorScheme(); @@ -105,10 +105,16 @@ export function Button( props ) { customContainerStyles && { ...customContainerStyles }, ]; + const buttonActiveColorStyles = usePreferredColorSchemeStyle( + style[ 'components-button-light--active' ], + style[ 'components-button-dark--active' ] + ); + const buttonViewStyle = { opacity: isDisabled ? 0.3 : 1, ...( fixedRatio && styles.fixedRatio ), ...( isPressed ? styles.buttonActive : styles.buttonInactive ), + ...( isPressed ? buttonActiveColorStyles : {} ), ...( isPressed && isActiveStyle?.borderRadius && { borderRadius: isActiveStyle.borderRadius, @@ -158,7 +164,6 @@ export function Button( props ) { const newIcon = icon ? cloneElement( , { - colorScheme: preferredColorScheme, isPressed, } ) : null; @@ -184,6 +189,7 @@ export function Button( props ) { style={ containerStyle } disabled={ isDisabled } testID={ testID } + hitSlop={ hitSlop } > ) { const classes = useMemo( () => cx( - as === 'button' && styles.unstyledButton, + ( as === 'button' || as === 'a' ) && + styles.unstyledButton( as ), styles.itemSizes[ size ] || styles.itemSizes.medium, styles.item, spacedAround && styles.spacedAround, diff --git a/packages/components/src/item-group/stories/index.tsx b/packages/components/src/item-group/stories/index.tsx index 3fa35c67cfb533..e520ff10c160f5 100644 --- a/packages/components/src/item-group/stories/index.tsx +++ b/packages/components/src/item-group/stories/index.tsx @@ -39,20 +39,25 @@ Default.args = { children: ( [ { - children: 'First item', + children: 'First button item', // eslint-disable-next-line no-alert onClick: () => alert( 'First item clicked' ), }, { - children: 'Second item', + children: 'Second button item', // eslint-disable-next-line no-alert onClick: () => alert( 'Second item clicked' ), }, { - children: 'Third item', + children: 'Third button item', // eslint-disable-next-line no-alert onClick: () => alert( 'Third item clicked' ), }, + { + children: 'Anchor item', + as: 'a', + href: 'https://wordpress.org', + }, ] as ItemProps[] ).map( mapPropsToItem ), }; diff --git a/packages/components/src/item-group/styles.ts b/packages/components/src/item-group/styles.ts index 670fdf987dd932..95076f0bb4081d 100644 --- a/packages/components/src/item-group/styles.ts +++ b/packages/components/src/item-group/styles.ts @@ -6,34 +6,45 @@ import { css } from '@emotion/react'; /** * Internal dependencies */ -import { CONFIG, COLORS } from '../utils'; - -export const unstyledButton = css` - appearance: none; - border: 1px solid transparent; - cursor: pointer; - background: none; - text-align: start; - - svg, - path { - fill: currentColor; - } - - &:hover { - color: ${ COLORS.ui.theme }; - } - - &:focus-visible { - box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) - var( - --wp-components-color-accent, - var( --wp-admin-theme-color, ${ COLORS.ui.theme } ) - ); - // Windows high contrast mode. - outline: 2px solid transparent; - } -`; +import { CONFIG, COLORS, font } from '../utils'; + +export const unstyledButton = ( as: 'a' | 'button' ) => { + return css` + font-size: ${ font( 'default.fontSize' ) }; + font-family: inherit; + appearance: none; + border: 1px solid transparent; + cursor: pointer; + background: none; + text-align: start; + text-decoration: ${ as === 'a' ? 'none' : undefined }; + + svg, + path { + fill: currentColor; + } + + &:hover { + color: ${ COLORS.ui.theme }; + } + + &:focus { + box-shadow: none; + outline: none; + } + + &:focus-visible { + box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) + var( + --wp-components-color-accent, + var( --wp-admin-theme-color, ${ COLORS.ui.theme } ) + ); + // Windows high contrast mode. + outline: 2px solid transparent; + outline-offset: 0; + } + `; +}; export const itemWrapper = css` width: 100%; diff --git a/packages/components/src/mobile/image/index.native.js b/packages/components/src/mobile/image/index.native.js index 7f69f0bed3afff..064150ec5f728d 100644 --- a/packages/components/src/mobile/image/index.native.js +++ b/packages/components/src/mobile/image/index.native.js @@ -185,6 +185,13 @@ const ImageComponent = ( { imageHeight && { height: imageHeight }, shapeStyle, ]; + const imageSelectedStyles = [ + usePreferredColorSchemeStyle( + styles.imageBorder, + styles.imageBorderDark + ), + { height: containerSize?.height }, + ]; return ( + ) } { ! imageData ? ( diff --git a/packages/components/src/mobile/image/style.native.scss b/packages/components/src/mobile/image/style.native.scss index c5ccd195993519..f6deb3655f3699 100644 --- a/packages/components/src/mobile/image/style.native.scss +++ b/packages/components/src/mobile/image/style.native.scss @@ -7,7 +7,7 @@ } .imageBorder { - border-color: $blue-medium; + border-color: $blue-40; border-width: 2px; border-style: solid; position: absolute; @@ -16,6 +16,10 @@ height: 100%; } +.imageBorderDark { + border-color: $blue-50; +} + .retryIcon { width: 80px; height: 80px; diff --git a/packages/components/src/navigator/navigator-provider/component.tsx b/packages/components/src/navigator/navigator-provider/component.tsx index 4c98db5b1a4d18..9bcffad6bc54bc 100644 --- a/packages/components/src/navigator/navigator-provider/component.tsx +++ b/packages/components/src/navigator/navigator-provider/component.tsx @@ -148,6 +148,7 @@ function UnconnectedNavigatorProvider( focusTargetSelector, isBack = false, skipFocus = false, + replace = false, ...restOptions } = options; @@ -172,34 +173,38 @@ function UnconnectedNavigatorProvider( skipFocus, }; - if ( prevLocationHistory.length < 1 ) { - return [ newLocation ]; + if ( prevLocationHistory.length === 0 ) { + return replace ? [] : [ newLocation ]; } - return [ - ...prevLocationHistory.slice( - prevLocationHistory.length > MAX_HISTORY_LENGTH - 1 - ? 1 - : 0, - -1 - ), - // Assign `focusTargetSelector` to the previous location in history - // (the one we just navigated from). - { - ...prevLocationHistory[ - prevLocationHistory.length - 1 - ], - focusTargetSelector, - }, - newLocation, - ]; + const newLocationHistory = prevLocationHistory.slice( + prevLocationHistory.length > MAX_HISTORY_LENGTH - 1 ? 1 : 0, + -1 + ); + + if ( ! replace ) { + newLocationHistory.push( + // Assign `focusTargetSelector` to the previous location in history + // (the one we just navigated from). + { + ...prevLocationHistory[ + prevLocationHistory.length - 1 + ], + focusTargetSelector, + } + ); + } + + newLocationHistory.push( newLocation ); + + return newLocationHistory; } ); }, [ goBack ] ); - const goToParent: NavigatorContextType[ 'goToParent' ] = - useCallback( () => { + const goToParent: NavigatorContextType[ 'goToParent' ] = useCallback( + ( options = {} ) => { const currentPath = currentLocationHistory.current[ currentLocationHistory.current.length - 1 @@ -214,8 +219,10 @@ function UnconnectedNavigatorProvider( if ( parentPath === undefined ) { return; } - goTo( parentPath, { isBack: true } ); - }, [ goTo ] ); + goTo( parentPath, { ...options, isBack: true } ); + }, + [ goTo ] + ); const navigatorContextValue: NavigatorContextType = useMemo( () => ( { diff --git a/packages/components/src/navigator/types.ts b/packages/components/src/navigator/types.ts index e638084e8376d5..557f8074fd42e2 100644 --- a/packages/components/src/navigator/types.ts +++ b/packages/components/src/navigator/types.ts @@ -14,8 +14,11 @@ export type NavigateOptions = { focusTargetSelector?: string; isBack?: boolean; skipFocus?: boolean; + replace?: boolean; }; +export type NavigateToParentOptions = Omit< NavigateOptions, 'isBack' >; + export type NavigatorLocation = NavigateOptions & { isInitial?: boolean; path?: string; @@ -28,7 +31,7 @@ export type Navigator = { params: MatchParams; goTo: ( path: string, options?: NavigateOptions ) => void; goBack: () => void; - goToParent: () => void; + goToParent: ( options?: NavigateToParentOptions ) => void; }; export type NavigatorContext = Navigator & { diff --git a/packages/components/src/placeholder/style.scss b/packages/components/src/placeholder/style.scss index 7fd9977e4645dc..df06969852fdae 100644 --- a/packages/components/src/placeholder/style.scss +++ b/packages/components/src/placeholder/style.scss @@ -161,6 +161,11 @@ padding: 0 $grid-unit-10 2px; } } + .components-placeholder__learn-more { + .components-external-link { + color: var(--wp-admin-theme-color); + } + } } diff --git a/packages/components/src/query-controls/index.native.js b/packages/components/src/query-controls/index.native.js index 6ba18f646146e6..51ba7cba690be5 100644 --- a/packages/components/src/query-controls/index.native.js +++ b/packages/components/src/query-controls/index.native.js @@ -84,6 +84,7 @@ const QueryControls = memo( ) } { onNumberOfItemsChange && ( ) => onChange( event.target.value ); diff --git a/packages/components/src/text-control/test/text-control.tsx b/packages/components/src/text-control/test/text-control.tsx new file mode 100644 index 00000000000000..fc048b93992f08 --- /dev/null +++ b/packages/components/src/text-control/test/text-control.tsx @@ -0,0 +1,61 @@ +/** + * External dependencies + */ +import { render, screen } from '@testing-library/react'; + +/** + * Internal dependencies + */ +import TextControl from '..'; + +const noop = () => {}; + +describe( 'TextControl', () => { + describe( 'When no ID prop is provided', () => { + it( 'should generate an ID', () => { + render( ); + + expect( screen.getByRole( 'textbox' ) ).toHaveAttribute( + 'id', + expect.stringMatching( /^inspector-text-control-/ ) + ); + } ); + + it( 'should be labelled correctly', () => { + const labelValue = 'Test Label'; + render( + + ); + + expect( + screen.getByRole( 'textbox', { name: labelValue } ) + ).toBeVisible(); + } ); + } ); + + describe( 'When an ID prop is provided', () => { + const id = 'test-id'; + + it( 'should use the passed ID prop if provided', () => { + render( ); + + expect( screen.getByRole( 'textbox' ) ).toHaveAttribute( 'id', id ); + } ); + + it( 'should be labelled correctly', () => { + const labelValue = 'Test Label'; + render( + + ); + + expect( + screen.getByRole( 'textbox', { name: labelValue } ) + ).toBeVisible(); + } ); + } ); +} ); diff --git a/packages/components/src/toolbar/toolbar-group/style.native.scss b/packages/components/src/toolbar/toolbar-group/style.native.scss index e218aa37363e37..f45797de3a625a 100644 --- a/packages/components/src/toolbar/toolbar-group/style.native.scss +++ b/packages/components/src/toolbar/toolbar-group/style.native.scss @@ -1,11 +1,10 @@ .container { flex-direction: row; - border-left-width: 1px; - border-color: #e9eff3; + border-color: $light-quaternary; padding-left: 5px; padding-right: 5px; } .containerDark { - border-color: #525354; + border-color: $dark-quaternary; } diff --git a/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js b/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js index 6eaf0dc0c3c74a..53c2c59bda9548 100644 --- a/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js +++ b/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js @@ -1,31 +1,26 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; /** * WordPress dependencies */ -import { withPreferredColorScheme } from '@wordpress/compose'; +import { usePreferredColorSchemeStyle } from '@wordpress/compose'; /** * Internal dependencies */ import styles from './style.scss'; -const ToolbarGroupContainer = ( { - getStylesFromColorScheme, - passedStyle, - children, -} ) => ( - - { children } - -); +const ToolbarGroupContainer = ( { passedStyle, children } ) => { + const groupStyles = [ + usePreferredColorSchemeStyle( styles.container, styles.containerDark ), + { borderLeftWidth: StyleSheet.hairlineWidth }, + passedStyle, + ]; -export default withPreferredColorScheme( ToolbarGroupContainer ); + return { children }; +}; + +export default ToolbarGroupContainer; diff --git a/packages/components/src/tooltip/README.md b/packages/components/src/tooltip/README.md index e4d030681240ca..101d642f39eb2e 100644 --- a/packages/components/src/tooltip/README.md +++ b/packages/components/src/tooltip/README.md @@ -28,7 +28,7 @@ The direction in which the tooltip should open relative to its parent node. Spec - Type: `String` - Required: No -- Default: `"top center"` +- Default: `"bottom"` ### children diff --git a/packages/core-commands/src/site-editor-navigation-commands.js b/packages/core-commands/src/site-editor-navigation-commands.js index 8f2003d0e08a27..3d551619d63b04 100644 --- a/packages/core-commands/src/site-editor-navigation-commands.js +++ b/packages/core-commands/src/site-editor-navigation-commands.js @@ -13,6 +13,7 @@ import { symbolFilled, styles, navigation, + symbol, } from '@wordpress/icons'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { getQueryArg, addQueryArgs, getPath } from '@wordpress/url'; @@ -199,8 +200,8 @@ function useSiteEditorBasicNavigationCommands() { result.push( { name: 'core/edit-site/open-template-parts', - label: __( 'Open library' ), - icon: symbolFilled, + label: __( 'Open patterns' ), + icon: symbol, callback: ( { close } ) => { const args = { path: '/patterns', diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index 2170e3ffcb4aea..824e1159286ede 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -18,7 +18,6 @@ import { receiveItems, removeItems, receiveQueriedItems } from './queried-data'; import { getOrLoadEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities'; import { createBatch } from './batch'; import { STORE_NAME } from './name'; -import { getUndoEdits, getRedoEdits } from './private-selectors'; /** * Returns an action object used in signalling that authors have been received. @@ -407,8 +406,7 @@ export const editEntityRecord = export const undo = () => ( { select, dispatch } ) => { - // Todo: we shouldn't have to pass "root" here. - const undoEdit = select( ( state ) => getUndoEdits( state.root ) ); + const undoEdit = select.getUndoEdits(); if ( ! undoEdit ) { return; } @@ -425,8 +423,7 @@ export const undo = export const redo = () => ( { select, dispatch } ) => { - // Todo: we shouldn't have to pass "root" here. - const redoEdit = select( ( state ) => getRedoEdits( state.root ) ); + const redoEdit = select.getRedoEdits(); if ( ! redoEdit ) { return; } @@ -789,6 +786,22 @@ export const __experimentalSaveSpecifiedEntityEdits = editsToSave[ edit ] = edits[ edit ]; } } + + const configs = await dispatch( getOrLoadEntitiesConfig( kind ) ); + const entityConfig = configs.find( + ( config ) => config.kind === kind && config.name === name + ); + + const entityIdKey = entityConfig?.key || DEFAULT_ENTITY_KEY; + + // If a record key is provided then update the existing record. + // This necessitates providing `recordKey` to saveEntityRecord as part of the + // `record` argument (here called `editsToSave`) to stop that action creating + // a new record and instead cause it to update the existing record. + if ( recordId ) { + editsToSave[ entityIdKey ] = recordId; + } + return await dispatch.saveEntityRecord( kind, name, diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js index da048944f14984..fad9fe693a575b 100644 --- a/packages/core-data/src/entity-provider.js +++ b/packages/core-data/src/entity-provider.js @@ -5,7 +5,7 @@ import { createContext, useContext, useCallback, - useEffect, + useMemo, } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { parse, __unstableSerializeAndClean } from '@wordpress/blocks'; @@ -129,7 +129,7 @@ export function useEntityProp( kind, name, prop, _id ) { [ prop ]: newValue, } ); }, - [ kind, name, id, prop ] + [ editEntityRecord, kind, name, id, prop ] ); return [ value, setValue, fullValue ]; @@ -156,12 +156,12 @@ export function useEntityProp( kind, name, prop, _id ) { export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { const providerId = useEntityId( kind, name ); const id = _id ?? providerId; - const { content, blocks, meta } = useSelect( + const { content, editedBlocks, meta } = useSelect( ( select ) => { const { getEditedEntityRecord } = select( STORE_NAME ); const editedRecord = getEditedEntityRecord( kind, name, id ); return { - blocks: editedRecord.blocks, + editedBlocks: editedRecord.blocks, content: editedRecord.content, meta: editedRecord.meta, }; @@ -171,29 +171,22 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { const { __unstableCreateUndoLevel, editEntityRecord } = useDispatch( STORE_NAME ); - useEffect( () => { - // Load the blocks from the content if not already in state - // Guard against other instances that might have - // set content to a function already or the blocks are already in state. - if ( content && typeof content !== 'function' && ! blocks ) { - const parsedContent = parse( content ); - editEntityRecord( - kind, - name, - id, - { - blocks: parsedContent, - }, - { undoIgnore: true } - ); + const blocks = useMemo( () => { + if ( editedBlocks ) { + return editedBlocks; } - }, [ content ] ); + + return content && typeof content !== 'function' + ? parse( content ) + : EMPTY_ARRAY; + }, [ editedBlocks, content ] ); const updateFootnotes = useCallback( ( _blocks ) => { - if ( ! meta ) return; + const output = { blocks: _blocks }; + if ( ! meta ) return output; // If meta.footnotes is empty, it means the meta is not registered. - if ( meta.footnotes === undefined ) return {}; + if ( meta.footnotes === undefined ) return output; const { getRichTextValues } = unlock( blockEditorPrivateApis ); const _content = getRichTextValues( _blocks ).join( '' ) || ''; @@ -215,7 +208,8 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { : []; const currentOrder = footnotes.map( ( fn ) => fn.id ); - if ( currentOrder.join( '' ) === newOrder.join( '' ) ) return; + if ( currentOrder.join( '' ) === newOrder.join( '' ) ) + return output; const newFootnotes = newOrder.map( ( fnId ) => @@ -226,6 +220,71 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { } ); + function updateAttributes( attributes ) { + attributes = { ...attributes }; + + for ( const key in attributes ) { + const value = attributes[ key ]; + + if ( Array.isArray( value ) ) { + attributes[ key ] = value.map( updateAttributes ); + continue; + } + + if ( typeof value !== 'string' ) { + continue; + } + + if ( value.indexOf( 'data-fn' ) === -1 ) { + continue; + } + + // When we store rich text values, this would no longer + // require a regex. + const regex = + /(]+data-fn="([^"]+)"[^>]*>]*>)[\d*]*<\/a><\/sup>/g; + + attributes[ key ] = value.replace( + regex, + ( match, opening, fnId ) => { + const index = newOrder.indexOf( fnId ); + return `${ opening }${ index + 1 }`; + } + ); + + const compatRegex = + /]+data-fn="([^"]+)"[^>]*>\*<\/a>/g; + + attributes[ key ] = attributes[ key ].replace( + compatRegex, + ( match, fnId ) => { + const index = newOrder.indexOf( fnId ); + return `${ + index + 1 + }`; + } + ); + } + + return attributes; + } + + function updateBlocksAttributes( __blocks ) { + return __blocks.map( ( block ) => { + return { + ...block, + attributes: updateAttributes( block.attributes ), + innerBlocks: updateBlocksAttributes( + block.innerBlocks + ), + }; + } ); + } + + // We need to go through all block attributs deeply and update the + // footnote anchor numbering (textContent) to match the new order. + const newBlocks = updateBlocksAttributes( _blocks ); + oldFootnotes = { ...oldFootnotes, ...footnotes.reduce( ( acc, fn ) => { @@ -241,6 +300,7 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { ...meta, footnotes: JSON.stringify( newFootnotes ), }, + blocks: newBlocks, }; }, [ meta ] @@ -258,7 +318,6 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { // to make sure the edit makes the post dirty and creates // a new undo level. const edits = { - blocks: newBlocks, selection, content: ( { blocks: blocksForSerialization = [] } ) => __unstableSerializeAndClean( blocksForSerialization ), @@ -282,12 +341,12 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { ( newBlocks, options ) => { const { selection } = options; const footnotesChanges = updateFootnotes( newBlocks ); - const edits = { blocks: newBlocks, selection, ...footnotesChanges }; + const edits = { selection, ...footnotesChanges }; editEntityRecord( kind, name, id, edits, { isCached: true } ); }, [ kind, name, id, updateFootnotes, editEntityRecord ] ); - return [ blocks ?? EMPTY_ARRAY, onInput, onChange ]; + return [ blocks, onInput, onChange ]; } diff --git a/packages/core-data/src/index.js b/packages/core-data/src/index.js index 98509d9f0383ba..5b42b6731716ff 100644 --- a/packages/core-data/src/index.js +++ b/packages/core-data/src/index.js @@ -8,13 +8,13 @@ import { createReduxStore, register } from '@wordpress/data'; */ import reducer from './reducer'; import * as selectors from './selectors'; +import * as privateSelectors from './private-selectors'; import * as actions from './actions'; import * as resolvers from './resolvers'; import createLocksActions from './locks/actions'; import { rootEntitiesConfig, getMethodName } from './entities'; import { STORE_NAME } from './name'; import { unlock } from './private-apis'; -import { getNavigationFallbackId } from './private-selectors'; // The entity selectors/resolvers and actions are shortcuts to their generic equivalents // (getEntityRecord, getEntityRecords, updateEntityRecord, updateEntityRecords) @@ -64,9 +64,7 @@ const storeConfig = () => ( { * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore */ export const store = createReduxStore( STORE_NAME, storeConfig() ); -unlock( store ).registerPrivateSelectors( { - getNavigationFallbackId, -} ); +unlock( store ).registerPrivateSelectors( privateSelectors ); register( store ); // Register store after unlocking private selectors to allow resolvers to use them. export { default as EntityProvider } from './entity-provider'; diff --git a/packages/core-data/src/utils/set-nested-value.js b/packages/core-data/src/utils/set-nested-value.js index cb7db8a04b4b07..e90bf23e4dad8e 100644 --- a/packages/core-data/src/utils/set-nested-value.js +++ b/packages/core-data/src/utils/set-nested-value.js @@ -10,6 +10,8 @@ * * @see https://lodash.com/docs/4.17.15#set * + * @todo Needs to be deduplicated with its copy in `@wordpress/edit-site`. + * * @param {Object} object Object to modify * @param {Array} path Path of the property to set. * @param {*} value Value to set. diff --git a/packages/create-block-interactive-template/.npmrc b/packages/create-block-interactive-template/.npmrc new file mode 100644 index 00000000000000..43c97e719a5a82 --- /dev/null +++ b/packages/create-block-interactive-template/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md new file mode 100644 index 00000000000000..a79948001de14a --- /dev/null +++ b/packages/create-block-interactive-template/CHANGELOG.md @@ -0,0 +1 @@ + diff --git a/packages/create-block-interactive-template/README.md b/packages/create-block-interactive-template/README.md new file mode 100644 index 00000000000000..cc0530c0630549 --- /dev/null +++ b/packages/create-block-interactive-template/README.md @@ -0,0 +1,19 @@ +# Create Block Interactive Template + +This is a template for [`@wordpress/create-block`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/create-block/README.md) to create interactive blocks + +## Usage + +This block template can be used by running the following command: + +```bash +npx @wordpress/create-block --template @wordpress/create-block-interactive-template +``` + +## Contributing to this package + +This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. + +To find out more about contributing to this package or Gutenberg as a whole, please read the project's main [contributor guide](https://github.com/WordPress/gutenberg/tree/HEAD/CONTRIBUTING.md). + +

      Code is Poetry.

      diff --git a/packages/create-block-interactive-template/block-templates/README.md.mustache b/packages/create-block-interactive-template/block-templates/README.md.mustache new file mode 100644 index 00000000000000..728f37b8c0e39f --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/README.md.mustache @@ -0,0 +1,16 @@ +# Interactive Block + +> **Warning** +> **This block requires Gutenberg 16.2 or superior to work**. The Interactivity API is, at the moment, not part of WordPress Core as it is still very experimental, and very likely to change. + +> **Note** +> This block uses the API shared at [Proposal: The Interactivity API – A better developer experience in building interactive blocks](https://make.wordpress.org/core/2023/03/30/proposal-the-interactivity-api-a-better-developer-experience-in-building-interactive-blocks/). + +{{#isBasicVariant}} +This block has been created with the `create-block-interactive-template` and shows a basic structure of an interactive block that uses the Interactivity API. +{{/isBasicVariant}} + +Check the following resources for more info about the Interactivity API: +- [`@wordpress/interactivity` package](https://github.com/WordPress/gutenberg/blob/trunk/packages/interactivity/README.md) +- [Proposal: The Interactivity API – A better developer experience in building interactive blocks](https://make.wordpress.org/core/2023/03/30/proposal-the-interactivity-api-a-better-developer-experience-in-building-interactive-blocks/) +- [“Interactivity API” category](https://github.com/WordPress/gutenberg/discussions/categories/interactivity-api) in Gutenberg repo discussions \ No newline at end of file diff --git a/packages/create-block-interactive-template/block-templates/edit.js.mustache b/packages/create-block-interactive-template/block-templates/edit.js.mustache new file mode 100644 index 00000000000000..1a0aeeac8d6979 --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/edit.js.mustache @@ -0,0 +1,36 @@ +/** + * Retrieves the translation of text. + * + * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/ + */ +import { __ } from '@wordpress/i18n'; + +/** + * React hook that is used to mark the block wrapper element. + * It provides all the necessary props like the class name. + * + * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops + */ +import { useBlockProps } from '@wordpress/block-editor'; + +/** + * The edit function describes the structure of your block in the context of the + * editor. This represents what the editor will render when the block is used. + * + * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit + * + * @param {Object} props Properties passed to the function. + * @param {Object} props.attributes Available block attributes. + * @param {Function} props.setAttributes Function that updates individual attributes. + * + * @return {WPElement} Element to render. + */ +export default function Edit( { attributes, setAttributes } ) { + const blockProps = useBlockProps(); + + return ( +

      + { __( '{{title}} – hello from the editor!', '{{textdomain}}' ) } +

      + ); +} diff --git a/packages/create-block-interactive-template/block-templates/editor.scss.mustache b/packages/create-block-interactive-template/block-templates/editor.scss.mustache new file mode 100644 index 00000000000000..bdb19abec84212 --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/editor.scss.mustache @@ -0,0 +1,12 @@ +/** + * The following styles get applied inside the editor only. + * + * Replace them with your own styles or remove the file completely. + */ + +.wp-block-{{namespace}}-{{slug}} input[type="text"] { + font-size: 1em; + color: inherit; + background: inherit; + border: 0; +} diff --git a/packages/create-block-interactive-template/block-templates/index.js.mustache b/packages/create-block-interactive-template/block-templates/index.js.mustache new file mode 100644 index 00000000000000..cf40358217c76d --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/index.js.mustache @@ -0,0 +1,43 @@ +/** + * Registers a new block provided a unique name and an object defining its behavior. + * + * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files. + * All files containing `style` keyword are bundled together. The code used + * gets applied both to the front of your site and to the editor. All other files + * get applied to the editor only. + * + * @see https://www.npmjs.com/package/@wordpress/scripts#using-css + */ +import './style.scss'; +import './editor.scss'; + +/** + * Internal dependencies + */ +import Edit from './edit'; +import metadata from './block.json'; + +/** + * Every block starts by registering a new block type definition. + * + * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block + */ +registerBlockType( metadata.name, { + /** + * Used to construct a preview for the block to be shown in the block inserter. + */ + example: { + attributes: { + message: '{{title}}', + }, + }, + /** + * @see ./edit.js + */ + edit: Edit, +} ); diff --git a/packages/create-block-interactive-template/block-templates/render.php.mustache b/packages/create-block-interactive-template/block-templates/render.php.mustache new file mode 100644 index 00000000000000..ba791783e6edef --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/render.php.mustache @@ -0,0 +1,33 @@ +{{#isBasicVariant}} + + +
      + data-wp-interactive + data-wp-context='{ "{{namespace}}": { "isOpen": false } }' + data-wp-effect="effects.{{namespace}}.logIsOpen" +> + + +

      + +

      +
      +{{/isBasicVariant}} \ No newline at end of file diff --git a/packages/create-block-interactive-template/block-templates/style.scss.mustache b/packages/create-block-interactive-template/block-templates/style.scss.mustache new file mode 100644 index 00000000000000..1c73fa1c38ff94 --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/style.scss.mustache @@ -0,0 +1,12 @@ +/** + * The following styles get applied both on the front of your site + * and in the editor. + * + * Replace them with your own styles or remove the file completely. + */ + +.wp-block-{{namespace}}-{{slug}} { + font-size: 1em; + background: #ffff001a; + padding: 1em; +} diff --git a/packages/create-block-interactive-template/block-templates/view.js.mustache b/packages/create-block-interactive-template/block-templates/view.js.mustache new file mode 100644 index 00000000000000..85d74fec190ba5 --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/view.js.mustache @@ -0,0 +1,26 @@ +{{#isBasicVariant}} + +/** + * WordPress dependencies + */ +import { store } from "@wordpress/interactivity"; + +store( { + actions: { + '{{namespace}}': { + toggle: ( { context } ) => { + context[ '{{namespace}}' ].isOpen = !context[ '{{namespace}}' ].isOpen; + }, + }, + }, + effects: { + '{{namespace}}': { + logIsOpen: ( { context } ) => { + // Log the value of `isOpen` each time it changes. + console.log( `Is open: ${ context[ '{{namespace}}' ].isOpen }` ); + }, + }, + }, +} ); + +{{/isBasicVariant}} \ No newline at end of file diff --git a/packages/create-block-interactive-template/index.js b/packages/create-block-interactive-template/index.js new file mode 100644 index 00000000000000..a52475ed7a79e5 --- /dev/null +++ b/packages/create-block-interactive-template/index.js @@ -0,0 +1,24 @@ +/** + * External dependencies + */ +const { join } = require( 'path' ); + +module.exports = { + defaultValues: { + slug: 'example-interactive', + title: 'Example Interactive', + description: 'An interactive block with the Interactivity API', + dashicon: 'media-interactive', + npmDependencies: [ '@wordpress/interactivity' ], + supports: { + interactivity: true, + }, + render: 'file:./render.php', + viewScript: 'file:./view.js', + }, + variants: { + basic: {}, + }, + pluginTemplatesPath: join( __dirname, 'plugin-templates' ), + blockTemplatesPath: join( __dirname, 'block-templates' ), +}; diff --git a/packages/create-block-interactive-template/package.json b/packages/create-block-interactive-template/package.json new file mode 100644 index 00000000000000..1638b33e648478 --- /dev/null +++ b/packages/create-block-interactive-template/package.json @@ -0,0 +1,25 @@ +{ + "name": "@wordpress/create-block-interactive-template", + "version": "1.0.0", + "description": "Template for @wordpress/create-block to create interactive blocks with the Interactivity API.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "create block", + "block template", + "Interactivity API" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/docs/getting-started/create-block", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/create-block-interactive-template" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache b/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache new file mode 100644 index 00000000000000..2322881ab0d710 --- /dev/null +++ b/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache @@ -0,0 +1,43 @@ + !! value ) ), diff --git a/packages/create-block/lib/scaffold.js b/packages/create-block/lib/scaffold.js index cdeaf85a97bb4a..a8d9c3859e20d3 100644 --- a/packages/create-block/lib/scaffold.js +++ b/packages/create-block/lib/scaffold.js @@ -44,6 +44,7 @@ module.exports = async ( editorStyle, style, render, + viewScript, variantVars, customPackageJSON, customBlockJSON, @@ -103,6 +104,7 @@ module.exports = async ( editorStyle, style, render, + viewScript, customPackageJSON, customBlockJSON, ...variantVars, diff --git a/packages/create-block/lib/templates.js b/packages/create-block/lib/templates.js index 018385c9fde82c..4ce898617adde3 100644 --- a/packages/create-block/lib/templates.js +++ b/packages/create-block/lib/templates.js @@ -33,6 +33,7 @@ const predefinedPluginTemplates = { editorScript: null, editorStyle: null, style: null, + viewScript: 'file:./view.js', }, templatesPath: join( __dirname, 'templates', 'es5' ), variants: { @@ -53,6 +54,7 @@ const predefinedPluginTemplates = { supports: { html: false, }, + viewScript: 'file:./view.js', }, variants: { static: {}, diff --git a/packages/create-block/lib/templates/block/view.js.mustache b/packages/create-block/lib/templates/block/view.js.mustache new file mode 100644 index 00000000000000..baff12a165cd9a --- /dev/null +++ b/packages/create-block/lib/templates/block/view.js.mustache @@ -0,0 +1,3 @@ +/* eslint-disable no-console */ +console.log("Hello World! (from {{namespace}}-{{slug}} block)"); +/* eslint-enable no-console */ diff --git a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap index 85683906793548..0e4ff9e63a10a6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -32,7 +32,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`combine-assets\` should pro `; exports[`DependencyExtractionWebpackPlugin Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array('wp-blob'), 'version' => 'c8be4fceac30d1d00ca7'); +" array('wp-blob'), 'version' => 'b0e5d8b4c38533765be8'); " `; @@ -50,7 +50,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`dynamic-import\` should pro `; exports[`DependencyExtractionWebpackPlugin Webpack \`function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; @@ -96,7 +96,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`has-extension-suffix\` shou `; exports[`DependencyExtractionWebpackPlugin Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(), 'version' => 'f7e2cb527e601f74f8bd'); +" array(), 'version' => '43880e6c42e7c39fcdf1'); " `; @@ -110,7 +110,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`no-deps\` should produce ex exports[`DependencyExtractionWebpackPlugin Webpack \`no-deps\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin Webpack \`option-function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; @@ -133,7 +133,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`option-function-output-file `; exports[`DependencyExtractionWebpackPlugin Webpack \`option-output-filename\` should produce expected output: Asset file 'main-foo.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; @@ -155,7 +155,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`option-output-filename\` sh ] `; -exports[`DependencyExtractionWebpackPlugin Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"4c42b9646049ad2e9438"}"`; +exports[`DependencyExtractionWebpackPlugin Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"7bd48470d799a795bf9a"}"`; exports[`DependencyExtractionWebpackPlugin Webpack \`output-format-json\` should produce expected output: External modules should match snapshot 1`] = ` [ @@ -207,17 +207,17 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`overrides\` should produce `; exports[`DependencyExtractionWebpackPlugin Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` -" array('wp-blob'), 'version' => '09a0c551770a351c5ca7'); +" array('wp-blob'), 'version' => 'd3cda564b538b44d38ef'); " `; exports[`DependencyExtractionWebpackPlugin Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => 'c9f00d690a9f72438910'); +" array('lodash', 'wp-blob'), 'version' => '420d636da562e71648f7'); " `; exports[`DependencyExtractionWebpackPlugin Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'runtime.asset.php' should match snapshot 1`] = ` -" array(), 'version' => '46ea0ff11ac53fa5e88b'); +" array(), 'version' => '66079b05b32ae1e16886'); " `; @@ -240,7 +240,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`runtime-chunk-single\` shou `; exports[`DependencyExtractionWebpackPlugin Webpack \`style-imports\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => 'd8c0ee89d933a3809c0e'); +" array('lodash', 'wp-blob'), 'version' => '4c661914a4e4d80b8a0b'); " `; @@ -263,7 +263,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`style-imports\` should prod `; exports[`DependencyExtractionWebpackPlugin Webpack \`wordpress\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js index 917b4cd7e204bf..4545bcd0c19bc6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js index 612e420c2a6c4d..f9eb6f42f0a2dc 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js @@ -1,6 +1,7 @@ /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.map( [], _.identity ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js index 917b4cd7e204bf..4545bcd0c19bc6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js index 917b4cd7e204bf..4545bcd0c19bc6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js index 612e420c2a6c4d..f9eb6f42f0a2dc 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js @@ -1,6 +1,7 @@ /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.map( [], _.identity ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js index 917b4cd7e204bf..4545bcd0c19bc6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js index df02e0b35e6f8e..b4e8cb76d1fa56 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; /** diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js index 917b4cd7e204bf..4545bcd0c19bc6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/e2e-test-utils-playwright/src/admin/visit-admin-page.ts b/packages/e2e-test-utils-playwright/src/admin/visit-admin-page.ts index 60aec50837694f..1ffb20c6cc3f86 100644 --- a/packages/e2e-test-utils-playwright/src/admin/visit-admin-page.ts +++ b/packages/e2e-test-utils-playwright/src/admin/visit-admin-page.ts @@ -18,7 +18,7 @@ import type { Admin } from './'; export async function visitAdminPage( this: Admin, adminPath: string, - query: string + query?: string ) { await this.page.goto( join( 'wp-admin', adminPath ) + ( query ? `?${ query }` : '' ) diff --git a/packages/e2e-test-utils-playwright/src/test.ts b/packages/e2e-test-utils-playwright/src/test.ts index 894abf93dcd02e..eec8e4e279c0ff 100644 --- a/packages/e2e-test-utils-playwright/src/test.ts +++ b/packages/e2e-test-utils-playwright/src/test.ts @@ -136,18 +136,6 @@ const test = base.extend< storageStatePath: STORAGE_STATE_PATH, } ); - await Promise.all( [ - requestUtils.activateTheme( 'twentytwentyone' ), - // Disable this test plugin as it's conflicting with some of the tests. - // We already have reduced motion enabled and Playwright will wait for most of the animations anyway. - requestUtils.deactivatePlugin( - 'gutenberg-test-plugin-disables-the-css-animations' - ), - requestUtils.deleteAllPosts(), - requestUtils.deleteAllBlocks(), - requestUtils.resetPreferences(), - ] ); - await use( requestUtils ); }, { scope: 'worker', auto: true }, diff --git a/packages/e2e-test-utils/src/create-reusable-block.js b/packages/e2e-test-utils/src/create-reusable-block.js index 4559d3b435fd15..860b5ac5660eb4 100644 --- a/packages/e2e-test-utils/src/create-reusable-block.js +++ b/packages/e2e-test-utils/src/create-reusable-block.js @@ -15,8 +15,6 @@ import { canvas } from './canvas'; export const createReusableBlock = async ( content, title ) => { const reusableBlockNameInputSelector = '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; - const syncToggleSelector = - '.reusable-blocks-menu-items__convert-modal .components-form-toggle__input'; const syncToggleSelectorChecked = '.reusable-blocks-menu-items__convert-modal .components-form-toggle.is-checked'; // Insert a paragraph block @@ -31,14 +29,12 @@ export const createReusableBlock = async ( content, title ) => { await nameInput.click(); await page.keyboard.type( title ); - const syncToggle = await page.waitForSelector( syncToggleSelector ); - syncToggle.click(); await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); // Wait for creation to finish await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Synced Pattern created."]' + '//*[contains(@class, "components-snackbar")]/*[contains(text(),"Pattern created:")]' ); // Check that we have a reusable block on the page diff --git a/packages/e2e-test-utils/src/install-plugin.js b/packages/e2e-test-utils/src/install-plugin.js index a2f8e493294196..5edfbb54f6642a 100644 --- a/packages/e2e-test-utils/src/install-plugin.js +++ b/packages/e2e-test-utils/src/install-plugin.js @@ -20,6 +20,8 @@ export async function installPlugin( slug, searchTerm ) { '&tab=search&type=term' ); await page.click( `.install-now[data-slug="${ slug }"]` ); - await page.waitForSelector( `.activate-now[data-slug="${ slug }"]` ); + await page.waitForSelector( `.activate-now[data-slug="${ slug }"]`, { + timeout: 60000, + } ); await switchUserToTest(); } diff --git a/packages/e2e-test-utils/src/install-theme.js b/packages/e2e-test-utils/src/install-theme.js index d030165c7cb97c..7d001d395bda7f 100644 --- a/packages/e2e-test-utils/src/install-theme.js +++ b/packages/e2e-test-utils/src/install-theme.js @@ -37,6 +37,8 @@ export async function installTheme( slug, { searchTerm } = {} ) { await page.waitForSelector( `.theme-install[data-slug="${ slug }"]` ); await page.click( `.theme-install[data-slug="${ slug }"]` ); - await page.waitForSelector( `.theme[data-slug="${ slug }"] .activate` ); + await page.waitForSelector( `.theme[data-slug="${ slug }"] .activate`, { + timeout: 60000, + } ); await switchUserToTest(); } diff --git a/packages/e2e-test-utils/src/login-user.js b/packages/e2e-test-utils/src/login-user.js index aeecfd9d5cc612..976153bce8f616 100644 --- a/packages/e2e-test-utils/src/login-user.js +++ b/packages/e2e-test-utils/src/login-user.js @@ -17,7 +17,9 @@ export async function loginUser( password = WP_PASSWORD ) { if ( ! isCurrentURL( 'wp-login.php' ) ) { + const waitForLoginPageNavigation = page.waitForNavigation(); await page.goto( createURL( 'wp-login.php' ) ); + await waitForLoginPageNavigation; } await page.focus( '#user_login' ); @@ -27,8 +29,6 @@ export async function loginUser( await pressKeyWithModifier( 'primary', 'a' ); await page.type( '#user_pass', password ); - await Promise.all( [ - page.waitForNavigation(), - page.click( '#wp-submit' ), - ] ); + const waitForLoginNavigation = page.waitForNavigation(); + await Promise.all( [ waitForLoginNavigation, page.click( '#wp-submit' ) ] ); } diff --git a/packages/e2e-tests/plugins/block-context.php b/packages/e2e-tests/plugins/block-context.php index dc92132152e1f0..d245efce2a6156 100644 --- a/packages/e2e-tests/plugins/block-context.php +++ b/packages/e2e-tests/plugins/block-context.php @@ -8,10 +8,10 @@ */ /** - * Enqueues a custom script for the plugin. + * Registers plugin test context blocks. */ -function gutenberg_test_enqueue_block_context_script() { - wp_enqueue_script( +function gutenberg_test_register_context_blocks() { + wp_register_script( 'gutenberg-test-block-context', plugins_url( 'block-context/index.js', __FILE__ ), array( @@ -22,37 +22,32 @@ function gutenberg_test_enqueue_block_context_script() { filemtime( plugin_dir_path( __FILE__ ) . 'block-context/index.js' ), true ); -} -add_action( 'init', 'gutenberg_test_enqueue_block_context_script' ); -/** - * Registers plugin test context blocks. - */ -function gutenberg_test_register_context_blocks() { register_block_type( 'gutenberg/test-context-provider', array( - 'attributes' => array( + 'attributes' => array( 'recordId' => array( 'type' => 'number', 'default' => 0, ), ), - 'provides_context' => array( + 'provides_context' => array( 'gutenberg/recordId' => 'recordId', ), + 'editor_script_handles' => array( 'gutenberg-test-block-context' ), ) ); register_block_type( 'gutenberg/test-context-consumer', array( - 'uses_context' => array( + 'uses_context' => array( 'gutenberg/recordId', 'postId', 'postType', ), - 'render_callback' => static function( $attributes, $content, $block ) { + 'render_callback' => static function( $attributes, $content, $block ) { $ordered_context = array( $block->context['gutenberg/recordId'], $block->context['postId'], @@ -61,6 +56,7 @@ function gutenberg_test_register_context_blocks() { return implode( ',', $ordered_context ); }, + 'editor_script_handles' => array( 'gutenberg-test-block-context' ), ) ); } diff --git a/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php b/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php index ad98354dd45dc7..3f24a6e25cfcb5 100644 --- a/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php +++ b/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php @@ -17,5 +17,18 @@ static function() { filemtime( plugin_dir_path( __FILE__ ) . 'iframed-enqueue-block-assets/style.css' ) ); wp_add_inline_style( 'iframed-enqueue-block-assets', 'body{padding:20px!important}' ); + wp_enqueue_script( + 'iframed-enqueue-block-assets-script', + plugin_dir_url( __FILE__ ) . 'iframed-enqueue-block-assets/script.js', + array(), + filemtime( plugin_dir_path( __FILE__ ) . 'iframed-enqueue-block-assets/script.js' ) + ); + wp_localize_script( + 'iframed-enqueue-block-assets-script', + 'iframedEnqueueBlockAssetsL10n', + array( + 'test' => 'Iframed Enqueue Block Assets!', + ) + ); } ); diff --git a/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js b/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js new file mode 100644 index 00000000000000..f0eddd65c70ebe --- /dev/null +++ b/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js @@ -0,0 +1,3 @@ +window.addEventListener( 'load', () => { + document.body.dataset.iframedEnqueueBlockAssetsL10n = window.iframedEnqueueBlockAssetsL10n.test; +} ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json new file mode 100644 index 00000000000000..6cbfa57b0784f1 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-style", + "title": "E2E Interactivity tests - directive style", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-style-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php new file mode 100644 index 00000000000000..4272333c3ec277 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php @@ -0,0 +1,93 @@ + + +
      + + + + +
      Don't change style if callback returns same value on hydration
      + +
      Remove style if callback returns falsy value on hydration
      + +
      Change style if callback returns a new value on hydration
      + +
      Handles multiple styles and callbacks on hydration
      + +
      Can add style when style attribute is missing on hydration
      + +
      Can toggle style
      + +
      Can remove style
      + +
      Can toggle style in the middle
      + +
      Handles styles names with hyphens
      + +
      +
      + +
      +
      diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js new file mode 100644 index 00000000000000..04fbf1aab11403 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js @@ -0,0 +1,22 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( { + state: { + falseValue: false, + color: "red", + border: "2px solid yellow" + }, + actions: { + toggleColor: ( { state } ) => { + state.color = state.color === "red" ? "blue" : "red"; + }, + switchColorToFalse: ({ state }) => { + state.color = false; + }, + toggleContext: ( { context } ) => { + context.color = context.color === "red" ? "blue" : "red"; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap index fb3f84595e5e2b..cbcbf0402f8c96 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap @@ -22,10 +22,11 @@ exports[`InnerBlocks Template Sync Ensures blocks without locking are kept intac

      Content…

      - -

      added paragraph

      - -" + +

      added paragraph

      + + +" `; exports[`InnerBlocks Template Sync Removes blocks that are not expected by the template if a lock all exists 1`] = ` diff --git a/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js b/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js index c1bd26fe1c7610..c29af593abb124 100644 --- a/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js +++ b/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js @@ -32,6 +32,7 @@ describe( 'iframed inline styles', () => { } ); it( 'should load styles added through enqueue_block_assets', async () => { + await page.waitForSelector( 'iframe[name="editor-canvas"]' ); // Check stylesheet. expect( await getComputedStyle( canvas(), 'body', 'background-color' ) @@ -40,5 +41,11 @@ describe( 'iframed inline styles', () => { expect( await getComputedStyle( canvas(), 'body', 'padding' ) ).toBe( '20px' ); + + expect( + await canvas().evaluate( () => ( { ...document.body.dataset } ) ) + ).toEqual( { + iframedEnqueueBlockAssetsL10n: 'Iframed Enqueue Block Assets!', + } ); } ); } ); diff --git a/packages/e2e-tests/specs/editor/various/adding-inline-tokens.test.js b/packages/e2e-tests/specs/editor/various/adding-inline-tokens.test.js deleted file mode 100644 index 7c04bae1f95ddb..00000000000000 --- a/packages/e2e-tests/specs/editor/various/adding-inline-tokens.test.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * External dependencies - */ -import path from 'path'; -import fs from 'fs'; -import os from 'os'; -import { v4 as uuid } from 'uuid'; - -/** - * WordPress dependencies - */ -import { - clickBlockAppender, - getEditedPostContent, - createNewPost, - clickBlockToolbarButton, - clickButton, - pressKeyWithModifier, -} from '@wordpress/e2e-test-utils'; - -describe( 'adding inline tokens', () => { - beforeEach( async () => { - await createNewPost(); - } ); - - it( 'should insert inline image', async () => { - // Create a paragraph. - await clickBlockAppender(); - await page.keyboard.type( 'a ' ); - - await clickBlockToolbarButton( 'More' ); - await clickButton( 'Inline image' ); - - // Wait for media modal to appear and upload image. - // Wait for media modal to appear and upload image. - const inputElement = await page.waitForSelector( - '.media-modal .moxie-shim input[type=file]' - ); - const testImagePath = path.join( - __dirname, - '..', - '..', - '..', - 'assets', - '10x10_e2e_test_image_z9T8jK.png' - ); - const filename = uuid(); - const tmpFileName = path.join( os.tmpdir(), filename + '.png' ); - fs.copyFileSync( testImagePath, tmpFileName ); - await inputElement.uploadFile( tmpFileName ); - - // Wait for upload. - await page.waitForSelector( - `.media-modal li[aria-label="${ filename }"]` - ); - - // Insert the uploaded image. - await page.click( '.media-modal button.media-button-select' ); - - // Check the content. - const regex = new RegExp( - `\\s*

      a <\\/p>\\s*` - ); - expect( await getEditedPostContent() ).toMatch( regex ); - - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await page.waitForSelector( - '.block-editor-format-toolbar__image-popover' - ); - await page.keyboard.press( 'Tab' ); - await page.keyboard.type( '20' ); - await page.keyboard.press( 'Enter' ); - - // Check the content. - const regex2 = new RegExp( - `\\s*

      a <\\/p>\\s*` - ); - expect( await getEditedPostContent() ).toMatch( regex2 ); - } ); -} ); diff --git a/packages/e2e-tests/specs/editor/various/publish-button.test.js b/packages/e2e-tests/specs/editor/various/publish-button.test.js index b6461ef11bc5b4..90ef0950e535bb 100644 --- a/packages/e2e-tests/specs/editor/various/publish-button.test.js +++ b/packages/e2e-tests/specs/editor/various/publish-button.test.js @@ -43,19 +43,4 @@ describe( 'PostPublishButton', () => { await page.$( '.editor-post-publish-button[aria-disabled="true"]' ) ).not.toBeNull(); } ); - - it( 'should be disabled when metabox is being saved', async () => { - await canvas().type( '.editor-post-title__input', 'E2E Test Post' ); // Make it saveable. - expect( - await page.$( '.editor-post-publish-button[aria-disabled="true"]' ) - ).toBeNull(); - - await page.evaluate( () => { - window.wp.data.dispatch( 'core/edit-post' ).requestMetaBoxUpdates(); - return true; - } ); - expect( - await page.$( '.editor-post-publish-button[aria-disabled="true"]' ) - ).not.toBeNull(); - } ); } ); diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js index ec2fc8e535550c..1fc9217b4a77c0 100644 --- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js @@ -23,8 +23,6 @@ const reusableBlockNameInputSelector = '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; const reusableBlockInspectorNameInputSelector = '.block-editor-block-inspector .components-text-control__input'; -const syncToggleSelector = - '.reusable-blocks-menu-items__convert-modal .components-form-toggle__input'; const syncToggleSelectorChecked = '.reusable-blocks-menu-items__convert-modal .components-form-toggle.is-checked'; @@ -205,14 +203,12 @@ describe( 'Reusable blocks', () => { ); await nameInput.click(); await page.keyboard.type( 'Multi-selection reusable block' ); - const syncToggle = await page.waitForSelector( syncToggleSelector ); - syncToggle.click(); await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); // Wait for creation to finish. await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Synced Pattern created."]' + '//*[contains(@class, "components-snackbar")]/*[contains(text(),"Pattern created:")]' ); await clearAllBlocks(); @@ -352,7 +348,7 @@ describe( 'Reusable blocks', () => { expect( reusableBlockWithParagraph ).toBeTruthy(); // Convert back to regular blocks. - await clickBlockToolbarButton( 'Select Pattern' ); + await clickBlockToolbarButton( 'Select Edited block' ); await clickBlockToolbarButton( 'Detach pattern' ); await page.waitForXPath( selector, { hidden: true, @@ -389,8 +385,6 @@ describe( 'Reusable blocks', () => { ); await nameInput.click(); await page.keyboard.type( 'Block with styles' ); - const syncToggle = await page.waitForSelector( syncToggleSelector ); - syncToggle.click(); await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); const reusableBlock = await canvas().waitForSelector( diff --git a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js b/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js index d54f9c78dd8b89..c2bd3cb6d75076 100644 --- a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js +++ b/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js @@ -12,8 +12,6 @@ import { activateTheme, clickButton, createReusableBlock, - visitSiteEditor, - enterEditMode, deleteAllTemplates, canvas, } from '@wordpress/e2e-test-utils'; @@ -239,104 +237,4 @@ describe( 'Multi-entity save flow', () => { expect( checkboxInputs ).toHaveLength( 1 ); } ); } ); - - describe( 'Site Editor', () => { - // Selectors - Site editor specific. - const saveSiteSelector = '.edit-site-save-button__button'; - const activeSaveSiteSelector = `${ saveSiteSelector }[aria-disabled=false]`; - const disabledSaveSiteSelector = `${ saveSiteSelector }[aria-disabled=true]`; - const saveA11ySelector = '.edit-site-editor__toggle-save-panel-button'; - - const saveAllChanges = async () => { - // Clicking button should open panel with boxes checked. - await page.click( activeSaveSiteSelector ); - await page.waitForSelector( savePanelSelector ); - await assertAllBoxesChecked(); - - // Save a11y button should not be present with save panel open. - await assertExistence( saveA11ySelector, false ); - - // Saving should result in items being saved. - await page.click( entitiesSaveSelector ); - }; - - it( 'Save flow should work as expected', async () => { - // Navigate to site editor. - await visitSiteEditor( { - postId: 'emptytheme//index', - postType: 'wp_template', - } ); - - await enterEditMode(); - - // Select the header template part via list view. - await page.click( '.edit-site-header-edit-mode__list-view-toggle' ); - const headerTemplatePartListViewButton = await page.waitForXPath( - '//a[contains(@class, "block-editor-list-view-block-select-button")][contains(., "header")]' - ); - headerTemplatePartListViewButton.click(); - await page.click( 'button[aria-label="Close"]' ); - - // Insert something to dirty the editor. - await insertBlock( 'Paragraph' ); - - const enabledButton = await page.waitForSelector( - activeSaveSiteSelector - ); - - // Should be enabled after edits. - expect( enabledButton ).not.toBeNull(); - - // Save a11y button should be present. - await assertExistence( saveA11ySelector, true ); - - // Save all changes. - await saveAllChanges(); - - const disabledButton = await page.waitForSelector( - disabledSaveSiteSelector - ); - expect( disabledButton ).not.toBeNull(); - } ); - - it( 'Save flow should allow re-saving after changing the same block attribute', async () => { - // Navigate to site editor. - await visitSiteEditor( { - postId: 'emptytheme//index', - postType: 'wp_template', - } ); - - await enterEditMode(); - - // Insert a paragraph at the bottom. - await insertBlock( 'Paragraph' ); - - // Open the block settings. - await page.click( 'button[aria-label="Settings"]' ); - - // Wait for the font size picker controls. - await page.waitForSelector( - '.components-font-size-picker__controls' - ); - - // Change the font size. - await page.click( - '.components-font-size-picker__controls button[aria-label="Small"]' - ); - - // Save all changes. - await saveAllChanges(); - - // Change the font size. - await page.click( - '.components-font-size-picker__controls button[aria-label="Medium"]' - ); - - // Assert that the save button has been re-enabled. - const saveButton = await page.waitForSelector( - activeSaveSiteSelector - ); - expect( saveButton ).not.toBeNull(); - } ); - } ); } ); diff --git a/packages/edit-post/src/components/device-preview/index.js b/packages/edit-post/src/components/device-preview/index.js index 5c061c92fde8e0..ab38523969ace1 100644 --- a/packages/edit-post/src/components/device-preview/index.js +++ b/packages/edit-post/src/components/device-preview/index.js @@ -15,26 +15,22 @@ import { store as coreStore } from '@wordpress/core-data'; import { store as editPostStore } from '../../store'; export default function DevicePreview() { - const { - hasActiveMetaboxes, - isPostSaveable, - isSaving, - isViewable, - deviceType, - } = useSelect( ( select ) => { - const { getEditedPostAttribute } = select( editorStore ); - const { getPostType } = select( coreStore ); - const postType = getPostType( getEditedPostAttribute( 'type' ) ); + const { hasActiveMetaboxes, isPostSaveable, isViewable, deviceType } = + useSelect( ( select ) => { + const { getEditedPostAttribute } = select( editorStore ); + const { getPostType } = select( coreStore ); + const postType = getPostType( getEditedPostAttribute( 'type' ) ); - return { - hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - isSaving: select( editPostStore ).isSavingMetaBoxes(), - isPostSaveable: select( editorStore ).isEditedPostSaveable(), - isViewable: postType?.viewable ?? false, - deviceType: - select( editPostStore ).__experimentalGetPreviewDeviceType(), - }; - }, [] ); + return { + hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), + isPostSaveable: select( editorStore ).isEditedPostSaveable(), + isViewable: postType?.viewable ?? false, + deviceType: + select( + editPostStore + ).__experimentalGetPreviewDeviceType(), + }; + }, [] ); const { __experimentalSetPreviewDeviceType: setPreviewDeviceType } = useDispatch( editPostStore ); @@ -46,26 +42,26 @@ export default function DevicePreview() { setDeviceType={ setPreviewDeviceType } label={ __( 'Preview' ) } > - { isViewable && ( - -

      - - { __( 'Preview in new tab' ) } - - - } - /> -
      - - ) } + { ( { onClose } ) => + isViewable && ( + +
      + + { __( 'Preview in new tab' ) } + + + } + onPreview={ onClose } + /> +
      +
      + ) + } ); } diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index 8f9e413707d503..bcb81804272916 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -37,8 +37,6 @@ function HeaderToolbar() { const inserterButton = useRef(); const { setIsInserterOpened, setIsListViewOpened } = useDispatch( editPostStore ); - const { get: getPreference } = useSelect( preferencesStore ); - const hasFixedToolbar = getPreference( 'core/edit-post', 'fixedToolbar' ); const { isInserterEnabled, isInserterOpened, @@ -46,6 +44,7 @@ function HeaderToolbar() { showIconLabels, isListViewOpen, listViewShortcut, + hasFixedToolbar, } = useSelect( ( select ) => { const { hasInserterItems, getBlockRootClientId, getBlockSelectionEnd } = select( blockEditorStore ); @@ -53,6 +52,7 @@ function HeaderToolbar() { const { getEditorMode, isFeatureActive, isListViewOpened } = select( editPostStore ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); + const { get: getPreference } = select( preferencesStore ); return { // This setting (richEditingEnabled) should not live in the block editor's setting. @@ -69,6 +69,7 @@ function HeaderToolbar() { listViewShortcut: getShortcutRepresentation( 'core/edit-post/toggle-list-view' ), + hasFixedToolbar: getPreference( 'core/edit-post', 'fixedToolbar' ), }; }, [] ); diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index b9b91c8d4f5585..692136b2c3c204 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -1,13 +1,13 @@ /** * External dependencies */ -import { Platform, ScrollView, View } from 'react-native'; +import { ScrollView, StyleSheet, View } from 'react-native'; /** * WordPress dependencies */ -import { useCallback, useRef, useState } from '@wordpress/element'; -import { compose, withPreferredColorScheme } from '@wordpress/compose'; +import { useCallback, useRef, useEffect, Platform } from '@wordpress/element'; +import { compose, usePreferredColorSchemeStyle } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; import { withViewportMatch } from '@wordpress/viewport'; import { __ } from '@wordpress/i18n'; @@ -19,10 +19,19 @@ import { import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; import { keyboardClose, - undo as undoIcon, - redo as redoIcon, + audio as audioIcon, + media as imageIcon, + video as videoIcon, + gallery as galleryIcon, } from '@wordpress/icons'; import { store as editorStore } from '@wordpress/editor'; +import { createBlock } from '@wordpress/blocks'; +import { + toggleUndoButton, + toggleRedoButton, + subscribeOnUndoPressed, + subscribeOnRedoPressed, +} from '@wordpress/react-native-bridge'; /** * Internal dependencies @@ -30,6 +39,13 @@ import { store as editorStore } from '@wordpress/editor'; import styles from './style.scss'; import { store as editPostStore } from '../../../store'; +const shadowStyle = { + shadowOffset: { width: 2, height: 2 }, + shadowOpacity: 1, + shadowRadius: 6, + elevation: 18, +}; + function HeaderToolbar( { hasRedo, hasUndo, @@ -37,83 +53,129 @@ function HeaderToolbar( { undo, showInserter, showKeyboardHideButton, - getStylesFromColorScheme, + insertBlock, onHideKeyboard, isRTL, noContentSelected, } ) { - const wasNoContentSelected = useRef( noContentSelected ); - const [ isInserterOpen, setIsInserterOpen ] = useState( false ); + const anchorNodeRef = useRef(); + + const containerStyle = [ + usePreferredColorSchemeStyle( + styles[ 'header-toolbar__container' ], + styles[ 'header-toolbar__container--dark' ] + ), + { borderTopWidth: StyleSheet.hairlineWidth }, + ]; + + useEffect( () => { + const onUndoSubscription = subscribeOnUndoPressed( undo ); + const onRedoSubscription = subscribeOnRedoPressed( redo ); + + return () => { + onUndoSubscription?.remove(); + onRedoSubscription?.remove(); + }; + }, [ undo, redo ] ); + + useEffect( () => { + toggleUndoButton( ! hasUndo ); + }, [ hasUndo ] ); + + useEffect( () => { + toggleRedoButton( ! hasRedo ); + }, [ hasRedo ] ); const scrollViewRef = useRef( null ); const scrollToStart = () => { // scrollview doesn't seem to automatically adjust to RTL on Android so, scroll to end when Android - const isAndroid = Platform.OS === 'android'; - if ( isAndroid && isRTL ) { + if ( Platform.isAndroid && isRTL ) { scrollViewRef.current.scrollToEnd(); } else { scrollViewRef.current.scrollTo( { x: 0 } ); } }; - const renderHistoryButtons = () => { - const buttons = [ - /* TODO: replace with EditorHistoryRedo and EditorHistoryUndo. */ + + const onInsertBlock = useCallback( + ( blockType ) => () => { + insertBlock( createBlock( blockType ), undefined, undefined, true, { + source: 'inserter_menu', + } ); + }, + [ insertBlock ] + ); + + const renderMediaButtons = ( + , + /> , - ]; - - return isRTL ? buttons.reverse() : buttons; - }; - - const onToggleInserter = useCallback( - ( isOpen ) => { - if ( isOpen ) { - wasNoContentSelected.current = noContentSelected; - } - setIsInserterOpen( isOpen ); - }, - [ noContentSelected ] + /> + + + ); - // Expanded mode should be preserved while the inserter is open. - // This way we prevent style updates during the opening transition. - const useExpandedMode = isInserterOpen - ? wasNoContentSelected.current - : noContentSelected; - /* translators: accessibility text for the editor toolbar */ const toolbarAriaLabel = __( 'Document tools' ); + const shadowColor = usePreferredColorSchemeStyle( + styles[ 'header-toolbar__keyboard-hide-shadow--light' ], + styles[ 'header-toolbar__keyboard-hide-shadow--dark' ] + ); + const showKeyboardButtonStyles = [ + usePreferredColorSchemeStyle( + styles[ 'header-toolbar__keyboard-hide-container' ], + styles[ 'header-toolbar__keyboard-hide-container--dark' ] + ), + shadowStyle, + { + shadowColor: Platform.isAndroid + ? styles[ 'header-toolbar__keyboard-hide-shadow--solid' ].color + : shadowColor.color, + }, + ]; + return ( - - { renderHistoryButtons() } - + + + { noContentSelected && renderMediaButtons } + { showKeyboardHideButton && ( - + { - const { clearSelectedBlock } = dispatch( blockEditorStore ); + const { clearSelectedBlock, insertBlock } = + dispatch( blockEditorStore ); const { togglePostTitleSelection } = dispatch( editorStore ); return { @@ -191,8 +247,8 @@ export default compose( [ clearSelectedBlock(); togglePostTitleSelection( false ); }, + insertBlock, }; } ), withViewportMatch( { isLargeViewport: 'medium' } ), - withPreferredColorScheme, ] )( HeaderToolbar ); diff --git a/packages/edit-post/src/components/header/header-toolbar/style.native.scss b/packages/edit-post/src/components/header/header-toolbar/style.native.scss index 031d082706656a..751f83d3dc63e6 100644 --- a/packages/edit-post/src/components/header/header-toolbar/style.native.scss +++ b/packages/edit-post/src/components/header/header-toolbar/style.native.scss @@ -3,13 +3,13 @@ height: $mobile-header-toolbar-height; flex-direction: row; background-color: $app-background; - border-top-color: #e9eff3; - border-top-width: 1px; + border-top-color: $light-quaternary; + overflow: hidden; } .header-toolbar__container--dark { - background-color: $app-background-dark-alt; - border-top-color: $background-dark-elevated; + background-color: $app-safe-area-background-dark; + border-top-color: $dark-quaternary; } .header-toolbar__container--expanded { @@ -18,6 +18,7 @@ .header-toolbar__scrollable-content { flex-grow: 1; // Fixes RTL issue on Android. + padding-right: 8px; } .header-toolbar__keyboard-hide-container { @@ -27,4 +28,22 @@ width: 44px; justify-content: center; align-items: center; + border-color: transparent; + background-color: $app-background; +} + +.header-toolbar__keyboard-hide-container--dark { + background-color: $app-background-dark-alt; +} + +.header-toolbar__keyboard-hide-shadow--solid { + color: $black; +} + +.header-toolbar__keyboard-hide-shadow--light { + color: $light-quaternary; +} + +.header-toolbar__keyboard-hide-shadow--dark { + color: $light-primary; } diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index 3f42d4736f57bb..9c9462a641dd33 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -32,22 +32,17 @@ const slideX = { function Header( { setEntitiesSavedStatesCallback } ) { const isLargeViewport = useViewportMatch( 'large' ); - const { - hasActiveMetaboxes, - isPublishSidebarOpened, - isSaving, - showIconLabels, - } = useSelect( - ( select ) => ( { - hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - isPublishSidebarOpened: - select( editPostStore ).isPublishSidebarOpened(), - isSaving: select( editPostStore ).isSavingMetaBoxes(), - showIconLabels: - select( editPostStore ).isFeatureActive( 'showIconLabels' ), - } ), - [] - ); + const { hasActiveMetaboxes, isPublishSidebarOpened, showIconLabels } = + useSelect( + ( select ) => ( { + hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), + isPublishSidebarOpened: + select( editPostStore ).isPublishSidebarOpened(), + showIconLabels: + select( editPostStore ).isFeatureActive( 'showIconLabels' ), + } ), + [] + ); return (
      @@ -82,19 +77,14 @@ function Header( { setEntitiesSavedStatesCallback } ) { // when the publish sidebar has been closed. ) } - + { - return { + } = useSelect( + ( select ) => ( { publishSidebarOpened: select( editPostStore ).isPublishSidebarOpened(), hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - isSavingMetaBoxes: select( editPostStore ).isSavingMetaBoxes(), hasNonPostEntityChanges: select( editorStore ).hasNonPostEntityChanges(), - }; - }, [] ); + } ), + [] + ); const openEntitiesSavedStates = useCallback( () => setEntitiesSavedStatesCallback( true ), @@ -57,7 +56,6 @@ export default function ActionsPanel( { diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 0c18521b1215f2..87fc0aa408516d 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -12,14 +12,13 @@ import { UnsavedChangesWarning, EditorNotices, EditorKeyboardShortcutsRegister, + EditorKeyboardShortcuts, EditorSnackbars, + PostSyncStatusModal, store as editorStore, } from '@wordpress/editor'; import { useSelect, useDispatch } from '@wordpress/data'; -import { - BlockBreadcrumb, - privateApis as blockEditorPrivateApis, -} from '@wordpress/block-editor'; +import { BlockBreadcrumb } from '@wordpress/block-editor'; import { Button, ScrollLock, Popover } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; import { PluginArea } from '@wordpress/plugins'; @@ -52,9 +51,6 @@ import WelcomeGuide from '../welcome-guide'; import ActionsPanel from './actions-panel'; import StartPageOptions from '../start-page-options'; import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -const { BlockRemovalWarningModal } = unlock( blockEditorPrivateApis ); const interfaceLabels = { /* translators: accessibility text for the editor top bar landmark region. */ @@ -69,12 +65,6 @@ const interfaceLabels = { footer: __( 'Editor footer' ), }; -const blockRemovalRules = { - 'core/footnotes': __( - 'The Footnotes block displays all footnotes found in the content. Note that any footnotes in the content will persist after removing this block.' - ), -}; - function Layout( { styles } ) { const isMobileViewport = useViewportMatch( 'medium', '<' ); const isHugeViewport = useViewportMatch( 'huge', '>=' ); @@ -214,7 +204,7 @@ function Layout( { styles } ) { - + + diff --git a/packages/edit-post/src/components/layout/style.native.scss b/packages/edit-post/src/components/layout/style.native.scss index faee2799e54eb8..765314bf15955c 100644 --- a/packages/edit-post/src/components/layout/style.native.scss +++ b/packages/edit-post/src/components/layout/style.native.scss @@ -6,7 +6,7 @@ } .containerDark { - background-color: $app-background-dark-alt; + background-color: $app-safe-area-background-dark; } .background { diff --git a/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap index 182531ee01c02f..32254bb91c6955 100644 --- a/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap +++ b/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap @@ -590,6 +590,8 @@ exports[`EditPostPreferencesModal should match snapshot when the modal is active } .emotion-13 { + font-size: 13px; + font-family: inherit; -webkit-appearance: none; -moz-appearance: none; -ms-appearance: none; @@ -616,12 +618,18 @@ exports[`EditPostPreferencesModal should match snapshot when the modal is active color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9)); } +.emotion-13:focus { + box-shadow: none; + outline: none; +} + .emotion-13:focus-visible { box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) var( - --wp-components-color-accent, - var( --wp-admin-theme-color, var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9)) ) - ); + --wp-components-color-accent, + var( --wp-admin-theme-color, var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9)) ) + ); outline: 2px solid transparent; + outline-offset: 0; } .emotion-15 { diff --git a/packages/edit-post/src/components/text-editor/index.js b/packages/edit-post/src/components/text-editor/index.js index 9c9b3c29e99481..b4b2ad64133a82 100644 --- a/packages/edit-post/src/components/text-editor/index.js +++ b/packages/edit-post/src/components/text-editor/index.js @@ -4,7 +4,6 @@ import { PostTextEditor, PostTitle, - TextEditorGlobalKeyboardShortcuts, store as editorStore, } from '@wordpress/editor'; import { Button } from '@wordpress/components'; @@ -25,7 +24,6 @@ export default function TextEditor() { return (
      - { isRichEditingEnabled && (

      { __( 'Editing code' ) }

      diff --git a/packages/edit-post/src/components/visual-editor/header.native.js b/packages/edit-post/src/components/visual-editor/header.native.js index 20ed9ae5d8164b..f7679e531ec9ea 100644 --- a/packages/edit-post/src/components/visual-editor/header.native.js +++ b/packages/edit-post/src/components/visual-editor/header.native.js @@ -16,23 +16,9 @@ import { useEditorWrapperStyles, } from '@wordpress/block-editor'; -/** - * Internal dependencies - */ -import styles from './style.scss'; - const Header = memo( - function EditorHeader( { - editTitle, - setTitleRef, - title, - getStylesFromColorScheme, - } ) { + function EditorHeader( { editTitle, setTitleRef, title } ) { const [ wrapperStyles ] = useEditorWrapperStyles(); - const blockHolderFocusedStyle = getStylesFromColorScheme( - styles.blockHolderFocused, - styles.blockHolderFocusedDark - ); return ( diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 29e6e15da4579f..fe14ef05561134 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -6,11 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { - VisualEditorGlobalKeyboardShortcuts, - PostTitle, - store as editorStore, -} from '@wordpress/editor'; +import { PostTitle, store as editorStore } from '@wordpress/editor'; import { WritingFlow, BlockList, @@ -345,7 +341,6 @@ export default function VisualEditor( { styles } ) { 'is-template-mode': isTemplateMode, } ) } > - +

      First example paragraph.

      + + + +

      Second example paragraph.

      + + + + +" +`; diff --git a/packages/edit-post/src/components/visual-editor/test/index.native.js b/packages/edit-post/src/components/visual-editor/test/index.native.js index af07e4309ab691..8c6e041880a830 100644 --- a/packages/edit-post/src/components/visual-editor/test/index.native.js +++ b/packages/edit-post/src/components/visual-editor/test/index.native.js @@ -1,11 +1,12 @@ /** * External dependencies */ -import { initializeEditor, fireEvent } from 'test/helpers'; +import { initializeEditor, getEditorHtml, fireEvent } from 'test/helpers'; /** * WordPress dependencies */ +import { Platform } from '@wordpress/element'; import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks'; import { registerCoreBlocks } from '@wordpress/block-library'; @@ -21,6 +22,12 @@ afterAll( () => { } ); } ); +const MEDIA_OPTIONS = [ + 'Choose from device', + 'Take a Photo', + 'WordPress Media Library', +]; + const initialHtml = `

      First example paragraph.

      @@ -63,6 +70,38 @@ describe( 'when title is focused', () => { screen.getAllByLabelText( /Paragraph Block. Row 3/ )[ 0 ] ).toBeDefined(); } ); + + it( 'media blocks should be displayed', async () => { + const screen = await initializeEditor( { + initialHtml, + } ); + + // Focus first block + fireEvent.press( + screen.getAllByLabelText( /Paragraph Block. Row 1/ )[ 0 ] + ); + + // Focus title + fireEvent( + screen.getAllByLabelText( 'Post title. test' )[ 0 ], + 'select' + ); + + // Assert that the media buttons are visible + const imageButton = await screen.findByTestId( 'insert-image-button' ); + expect( imageButton ).toBeVisible(); + + const videoButton = await screen.findByTestId( 'insert-video-button' ); + expect( videoButton ).toBeVisible(); + + const galleryButton = await screen.findByTestId( + 'insert-gallery-button' + ); + expect( galleryButton ).toBeVisible(); + + const audioButton = await screen.findByTestId( 'insert-audio-button' ); + expect( audioButton ).toBeVisible(); + } ); } ); describe( 'when title is no longer focused', () => { @@ -101,4 +140,82 @@ describe( 'when title is no longer focused', () => { screen.getAllByLabelText( /Heading Block. Row 3/ )[ 0 ] ).toBeDefined(); } ); + + it( 'media blocks should not be displayed', async () => { + const screen = await initializeEditor( { + initialHtml, + } ); + + // Focus first block + fireEvent.press( + screen.getAllByLabelText( /Paragraph Block. Row 1/ )[ 0 ] + ); + + // Focus title + fireEvent( + screen.getAllByLabelText( 'Post title. test' )[ 0 ], + 'select' + ); + + // Focus last block + fireEvent.press( + screen.getAllByLabelText( /Paragraph Block. Row 2/ )[ 0 ] + ); + + // Assert that the media buttons are not visible + const imageButton = screen.queryByTestId( 'insert-image-button' ); + expect( imageButton ).toBeNull(); + + const videoButton = screen.queryByTestId( 'insert-video-button' ); + expect( videoButton ).toBeNull(); + + const galleryButton = screen.queryByTestId( 'insert-gallery-button' ); + expect( galleryButton ).toBeNull(); + + const audioButton = screen.queryByTestId( 'insert-audio-button' ); + expect( audioButton ).toBeNull(); + } ); +} ); + +describe( 'when nothing is selected', () => { + it( 'media buttons and picker display correctly', async () => { + const screen = await initializeEditor( { + initialHtml, + } ); + + const { getByText, getByTestId } = screen; + + // Check that the gallery button is visible within the toolbar + const galleryButton = await screen.queryByTestId( + 'insert-gallery-button' + ); + expect( galleryButton ).toBeVisible(); + + // Press the toolbar Gallery button + fireEvent.press( galleryButton ); + + // Expect the block to be created + expect( + screen.getAllByLabelText( /Gallery Block. Row 3/ )[ 0 ] + ).toBeDefined(); + + expect( getByText( 'Choose images' ) ).toBeVisible(); + MEDIA_OPTIONS.forEach( ( option ) => + expect( getByText( option ) ).toBeVisible() + ); + + // Dismiss the picker + if ( Platform.isIOS ) { + fireEvent.press( getByText( 'Cancel' ) ); + } else { + fireEvent( getByTestId( 'media-options-picker' ), 'backdropPress' ); + } + + // Expect the Gallery block to remain + expect( + screen.getAllByLabelText( /Gallery Block. Row 3/ )[ 0 ] + ).toBeDefined(); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); diff --git a/packages/edit-post/src/plugins/index.js b/packages/edit-post/src/plugins/index.js index e3bd1b2dd72bda..aa663659acbdc1 100644 --- a/packages/edit-post/src/plugins/index.js +++ b/packages/edit-post/src/plugins/index.js @@ -2,6 +2,9 @@ * WordPress dependencies */ import { MenuItem, VisuallyHidden } from '@wordpress/components'; +import { store as coreStore } from '@wordpress/core-data'; +import { store as editorStore } from '@wordpress/editor'; +import { useSelect } from '@wordpress/data'; import { external } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; import { registerPlugin } from '@wordpress/plugins'; @@ -15,6 +18,34 @@ import KeyboardShortcutsHelpMenuItem from './keyboard-shortcuts-help-menu-item'; import ToolsMoreMenuGroup from '../components/header/tools-more-menu-group'; import WelcomeGuideMenuItem from './welcome-guide-menu-item'; +function ManagePatternsMenuItem() { + const url = useSelect( ( select ) => { + const { canUser } = select( coreStore ); + const { getEditorSettings } = select( editorStore ); + + const isBlockTheme = getEditorSettings().__unstableIsBlockBasedTheme; + const defaultUrl = addQueryArgs( 'edit.php', { + post_type: 'wp_block', + } ); + const patternsUrl = addQueryArgs( 'site-editor.php', { + path: '/patterns', + } ); + + // The site editor and templates both check whether the user has + // edit_theme_options capabilities. We can leverage that here and not + // display the manage patterns link if the user can't access it. + return canUser( 'read', 'templates' ) && isBlockTheme + ? patternsUrl + : defaultUrl; + }, [] ); + + return ( + + { __( 'Manage patterns' ) } + + ); +} + registerPlugin( 'edit-post', { render() { return ( @@ -22,14 +53,7 @@ registerPlugin( 'edit-post', { { ( { onClose } ) => ( <> - - { __( 'Manage Patterns' ) } - + diff --git a/packages/edit-post/src/store/actions.js b/packages/edit-post/src/store/actions.js index f938a9837516e0..0ee1efb62b02ea 100644 --- a/packages/edit-post/src/store/actions.js +++ b/packages/edit-post/src/store/actions.js @@ -11,6 +11,7 @@ import { store as coreStore } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as editorStore } from '@wordpress/editor'; import deprecated from '@wordpress/deprecated'; +import { addFilter } from '@wordpress/hooks'; /** * Internal dependencies @@ -567,33 +568,23 @@ export const initializeMetaBoxes = metaBoxesInitialized = true; - let wasSavingPost = registry.select( editorStore ).isSavingPost(); - let wasAutosavingPost = registry - .select( editorStore ) - .isAutosavingPost(); - - // Save metaboxes when performing a full save on the post. - registry.subscribe( async () => { - const isSavingPost = registry.select( editorStore ).isSavingPost(); - const isAutosavingPost = registry - .select( editorStore ) - .isAutosavingPost(); - - // Save metaboxes on save completion, except for autosaves. - const shouldTriggerMetaboxesSave = - wasSavingPost && - ! wasAutosavingPost && - ! isSavingPost && - select.hasMetaBoxes(); - - // Save current state for next inspection. - wasSavingPost = isSavingPost; - wasAutosavingPost = isAutosavingPost; - - if ( shouldTriggerMetaboxesSave ) { - await dispatch.requestMetaBoxUpdates(); - } - } ); + // Save metaboxes on save completion, except for autosaves. + addFilter( + 'editor.__unstableSavePost', + 'core/edit-post/save-metaboxes', + ( previous, options ) => + previous.then( () => { + if ( options.isAutosave ) { + return; + } + + if ( ! select.hasMetaBoxes() ) { + return; + } + + return dispatch.requestMetaBoxUpdates(); + } ) + ); dispatch( { type: 'META_BOXES_INITIALIZED', diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index d33a5ae22c0fd9..fac5deef18f717 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -41,13 +41,7 @@ } } -// In order to use mix-blend-mode, this element needs to have an explicitly set background-color -// We scope it to .wp-toolbar to be wp-admin only, to prevent bleed into other implementations -html.wp-toolbar { - background: $white; -} - -body.block-editor-page { +body.js.block-editor-page { @include wp-admin-reset( ".block-editor" ); } diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 230faa8857f1c7..06e52ed9fd53e7 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -71,7 +71,6 @@ "downloadjs": "^1.4.7", "fast-deep-equal": "^3.1.3", "is-plain-object": "^5.0.0", - "lodash": "^4.17.21", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0", "rememo": "^4.0.2", diff --git a/packages/edit-site/src/components/add-new-page/index.js b/packages/edit-site/src/components/add-new-page/index.js index 0c8efa3dfaa0ba..09d2e17ff94d1a 100644 --- a/packages/edit-site/src/components/add-new-page/index.js +++ b/packages/edit-site/src/components/add-new-page/index.js @@ -72,9 +72,6 @@ export default function AddNewPageModal( { onSave, onClose } ) {
      { + const settings = select( editSiteStore ).getSettings(); + return !! settings.supportsTemplatePartsMode; + }, [] ); function handleCreatePattern( { pattern, categoryId } ) { setShowPatternModal( false ); @@ -51,21 +57,26 @@ export default function AddNewPattern() { setShowTemplatePartModal( false ); } + const controls = [ + { + icon: symbol, + onClick: () => setShowPatternModal( true ), + title: __( 'Create pattern' ), + }, + ]; + + if ( ! isTemplatePartsMode ) { + controls.push( { + icon: symbolFilled, + onClick: () => setShowTemplatePartModal( true ), + title: __( 'Create template part' ), + } ); + } + return ( <> setShowTemplatePartModal( true ), - title: __( 'Create template part' ), - }, - { - icon: symbol, - onClick: () => setShowPatternModal( true ), - title: __( 'Create pattern' ), - }, - ] } + controls={ controls } toggleProps={ { as: SidebarButton, } } diff --git a/packages/edit-site/src/components/add-new-template/utils.js b/packages/edit-site/src/components/add-new-template/utils.js index 8de37c1f64632c..1869cdb72f5929 100644 --- a/packages/edit-site/src/components/add-new-template/utils.js +++ b/packages/edit-site/src/components/add-new-template/utils.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ @@ -20,6 +15,14 @@ import { blockMeta, post, archive } from '@wordpress/icons'; * @property {string} name The entity's name. */ +const getValueFromObjectPath = ( object, path ) => { + let value = object; + path.split( '.' ).forEach( ( fieldName ) => { + value = value?.[ fieldName ]; + } ); + return value; +}; + /** * Helper util to map records to add a `name` prop from a * provided path, in order to handle all entities in the same @@ -32,7 +35,7 @@ import { blockMeta, post, archive } from '@wordpress/icons'; export const mapToIHasNameAndId = ( entities, path ) => { return ( entities || [] ).map( ( entity ) => ( { ...entity, - name: decodeEntities( get( entity, path ) ), + name: decodeEntities( getValueFromObjectPath( entity, path ) ), } ) ); }; diff --git a/packages/edit-site/src/components/block-editor/constants.js b/packages/edit-site/src/components/block-editor/constants.js index 9f076eea1ca311..ed88c3e14c9473 100644 --- a/packages/edit-site/src/components/block-editor/constants.js +++ b/packages/edit-site/src/components/block-editor/constants.js @@ -1 +1,5 @@ -export const FOCUSABLE_ENTITIES = [ 'wp_template_part', 'wp_navigation' ]; +export const FOCUSABLE_ENTITIES = [ + 'wp_template_part', + 'wp_navigation', + 'wp_block', +]; diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/editor-canvas.js index 34c23cc699bc28..a8bbe75e0261b5 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/editor-canvas.js @@ -88,7 +88,7 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) { enableResizing ? 'min-height:0!important;' : '' }}body{position:relative; ${ canvasMode === 'view' - ? 'cursor: pointer; height: 100vh' + ? 'cursor: pointer; min-height: 100vh;' : '' }}}` } diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index fcd28948ccbb3f..e2d9e2680b263d 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -1,7 +1,3 @@ -/** - * External dependencies - */ - /** * WordPress dependencies */ diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index 732f662f5ddcad..b215f5c34273ea 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; /** @@ -11,16 +11,73 @@ import { store as editSiteStore } from '../../store'; import { unlock } from '../../lock-unlock'; import inserterMediaCategories from './inserter-media-categories'; -export default function useSiteEditorSettings( templateType ) { - const { storedSettings, canvasMode } = useSelect( ( select ) => { - const { getSettings, getCanvasMode } = unlock( - select( editSiteStore ) - ); - return { - storedSettings: getSettings(), - canvasMode: getCanvasMode(), - }; - }, [] ); +function useArchiveLabel( templateSlug ) { + const taxonomyMatches = templateSlug?.match( + /^(category|tag|taxonomy-([^-]+))$|^(((category|tag)|taxonomy-([^-]+))-(.+))$/ + ); + let taxonomy; + let term; + if ( taxonomyMatches ) { + // If is for a all taxonomies of a type + if ( taxonomyMatches[ 1 ] ) { + taxonomy = taxonomyMatches[ 2 ] + ? taxonomyMatches[ 2 ] + : taxonomyMatches[ 1 ]; + } + // If is for a all taxonomies of a type + else if ( taxonomyMatches[ 3 ] ) { + taxonomy = taxonomyMatches[ 6 ] + ? taxonomyMatches[ 6 ] + : taxonomyMatches[ 4 ]; + term = taxonomyMatches[ 7 ]; + } + taxonomy = taxonomy === 'tag' ? 'post_tag' : taxonomy; + + //getTaxonomy( 'category' ); + //wp.data.select('core').getEntityRecords( 'taxonomy', 'category', {slug: 'newcat'} ); + } + return useSelect( + ( select ) => { + const { getEntityRecords, getTaxonomy } = select( coreStore ); + let archiveTypeLabel; + let archiveNameLabel; + if ( taxonomy ) { + archiveTypeLabel = + getTaxonomy( taxonomy )?.labels?.singular_name; + } + if ( term ) { + const records = getEntityRecords( 'taxonomy', taxonomy, { + slug: term, + per_page: 1, + } ); + if ( records && records[ 0 ] ) { + archiveNameLabel = records[ 0 ].name; + } + } + return { + archiveTypeLabel, + archiveNameLabel, + }; + }, + [ taxonomy, term ] + ); +} + +export default function useSiteEditorSettings() { + const { setIsInserterOpened } = useDispatch( editSiteStore ); + const { storedSettings, canvasMode, templateType } = useSelect( + ( select ) => { + const { getSettings, getCanvasMode, getEditedPostType } = unlock( + select( editSiteStore ) + ); + return { + storedSettings: getSettings( setIsInserterOpened ), + canvasMode: getCanvasMode(), + templateType: getEditedPostType(), + }; + }, + [ setIsInserterOpened ] + ); const settingsBlockPatterns = storedSettings.__experimentalAdditionalBlockPatterns ?? // WP 6.0 @@ -29,14 +86,27 @@ export default function useSiteEditorSettings( templateType ) { storedSettings.__experimentalAdditionalBlockPatternCategories ?? // WP 6.0 storedSettings.__experimentalBlockPatternCategories; // WP 5.9 - const { restBlockPatterns, restBlockPatternCategories } = useSelect( - ( select ) => ( { - restBlockPatterns: select( coreStore ).getBlockPatterns(), - restBlockPatternCategories: - select( coreStore ).getBlockPatternCategories(), - } ), - [] - ); + const { restBlockPatterns, restBlockPatternCategories, templateSlug } = + useSelect( ( select ) => { + const { getEditedPostType, getEditedPostId } = + select( editSiteStore ); + const { getEditedEntityRecord } = select( coreStore ); + const usedPostType = getEditedPostType(); + const usedPostId = getEditedPostId(); + const _record = getEditedEntityRecord( + 'postType', + usedPostType, + usedPostId + ); + return { + restBlockPatterns: select( coreStore ).getBlockPatterns(), + restBlockPatternCategories: + select( coreStore ).getBlockPatternCategories(), + templateSlug: _record.slug, + }; + }, [] ); + const archiveLabels = useArchiveLabel( templateSlug ); + const blockPatterns = useMemo( () => [ @@ -82,6 +152,15 @@ export default function useSiteEditorSettings( templateType ) { __experimentalBlockPatterns: blockPatterns, __experimentalBlockPatternCategories: blockPatternCategories, focusMode: canvasMode === 'view' && focusMode ? false : focusMode, + __experimentalArchiveTitleTypeLabel: archiveLabels.archiveTypeLabel, + __experimentalArchiveTitleNameLabel: archiveLabels.archiveNameLabel, }; - }, [ storedSettings, blockPatterns, blockPatternCategories, canvasMode ] ); + }, [ + storedSettings, + blockPatterns, + blockPatternCategories, + canvasMode, + archiveLabels.archiveTypeLabel, + archiveLabels.archiveNameLabel, + ] ); } diff --git a/packages/edit-site/src/components/canvas-spinner/style.scss b/packages/edit-site/src/components/canvas-spinner/style.scss index 2f0626b80363fe..22b1b856257424 100644 --- a/packages/edit-site/src/components/canvas-spinner/style.scss +++ b/packages/edit-site/src/components/canvas-spinner/style.scss @@ -2,10 +2,24 @@ width: 100%; height: 100%; display: flex; + opacity: 0; align-items: center; justify-content: center; + animation: 0.5s ease 1s edit-site-canvas-spinner__fade-in-animation; + animation-fill-mode: forwards; + @include reduce-motion("animation"); + circle { stroke: rgba($black, 0.3); } } + +@keyframes edit-site-canvas-spinner__fade-in-animation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} diff --git a/packages/edit-site/src/components/code-editor/code-editor-text-area.js b/packages/edit-site/src/components/code-editor/code-editor-text-area.js deleted file mode 100644 index 1a907ca8e59f68..00000000000000 --- a/packages/edit-site/src/components/code-editor/code-editor-text-area.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * External dependencies - */ -import Textarea from 'react-autosize-textarea'; - -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { useEffect, useState, useRef } from '@wordpress/element'; -import { useInstanceId } from '@wordpress/compose'; -import { VisuallyHidden } from '@wordpress/components'; - -export default function CodeEditorTextArea( { value, onChange, onInput } ) { - const [ stateValue, setStateValue ] = useState( value ); - const [ isDirty, setIsDirty ] = useState( false ); - const instanceId = useInstanceId( CodeEditorTextArea ); - const valueRef = useRef(); - - if ( ! isDirty && stateValue !== value ) { - setStateValue( value ); - } - - /** - * Handles a textarea change event to notify the onChange prop callback and - * reflect the new value in the component's own state. This marks the start - * of the user's edits, if not already changed, preventing future props - * changes to value from replacing the rendered value. This is expected to - * be followed by a reset to dirty state via `stopEditing`. - * - * @see stopEditing - * - * @param {Event} event Change event. - */ - const onChangeHandler = ( event ) => { - const newValue = event.target.value; - onInput( newValue ); - setStateValue( newValue ); - setIsDirty( true ); - valueRef.current = newValue; - }; - - /** - * Function called when the user has completed their edits, responsible for - * ensuring that changes, if made, are surfaced to the onPersist prop - * callback and resetting dirty state. - */ - const stopEditing = () => { - if ( isDirty ) { - onChange( stateValue ); - setIsDirty( false ); - } - }; - - // Ensure changes aren't lost when component unmounts. - useEffect( () => { - return () => { - if ( valueRef.current ) { - onChange( valueRef.current ); - } - }; - }, [] ); - - return ( - <> - - { __( 'Type text or HTML' ) } - -