diff --git a/.eslintrc.js b/.eslintrc.js index 490c542f9d4565..9240b96c033b48 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -104,6 +104,10 @@ module.exports = { rules: { 'jest/expect-expect': 'off', 'react/jsx-boolean-value': 'error', + 'react/jsx-curly-brace-presence': [ + 'error', + { props: 'never', children: 'never' }, + ], '@wordpress/dependency-group': 'error', '@wordpress/wp-global-usage': 'error', '@wordpress/react-no-unsafe-timeout': 'error', diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index f6527c3de9d978..477cd4fbacb400 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -18,3 +18,6 @@ c56e8a1910ed74f405b74bbb12fe81dea974e5c3 # Autofix eslint curly rule. 0221522f253e094b277a1485b7a2d186cb172632 + +# ESLint: Enable react/jsx-curly-brace-presence +5d4baa9ab5f57d207cc3a048003216a8574574d9 diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 149faee274206e..a572074f72c9c8 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -69,7 +69,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -165,7 +165,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: ${{ needs.bump-version.outputs.release_branch || github.ref }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -222,7 +222,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 2 ref: ${{ needs.bump-version.outputs.release_branch }} @@ -311,14 +311,14 @@ jobs: if: ${{ endsWith( needs.bump-version.outputs.new_version, '-rc.1' ) }} steps: - name: Checkout (for CLI) - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: main ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Checkout (for publishing) - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: publish # Later, we switch this branch in the script that publishes packages. diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 1065421044373b..2e7f2c98305fb2 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 1 show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/check-backport-changelog.yml b/.github/workflows/check-backport-changelog.yml index dd3fe28fbfd59f..99d7e1ca5b53a8 100644 --- a/.github/workflows/check-backport-changelog.yml +++ b/.github/workflows/check-backport-changelog.yml @@ -22,7 +22,7 @@ jobs: - name: 'Get PR commit count' run: echo "PR_COMMIT_COUNT=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> "${GITHUB_ENV}" - name: Checkout code - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.github/workflows/check-components-changelog.yml b/.github/workflows/check-components-changelog.yml index d995d641fae57d..ccc6efac3c3788 100644 --- a/.github/workflows/check-components-changelog.yml +++ b/.github/workflows/check-components-changelog.yml @@ -20,7 +20,7 @@ jobs: - name: 'Get PR commit count' run: echo "PR_COMMIT_COUNT=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> "${GITHUB_ENV}" - name: Checkout code - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.github/workflows/create-block.yml b/.github/workflows/create-block.yml index 7c26cb6e14e760..ff1f1eb980347b 100644 --- a/.github/workflows/create-block.yml +++ b/.github/workflows/create-block.yml @@ -24,7 +24,7 @@ jobs: os: ['macos-latest', 'ubuntu-latest', 'windows-latest'] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index 16680038e0db64..c4f165d42699d2 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -27,7 +27,7 @@ jobs: totalParts: [8] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -102,7 +102,7 @@ jobs: steps: # Checkout defaults to using the branch which triggered the event, which # isn't necessarily `trunk` (e.g. in the case of a merge). - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 633f62d5ed28c9..b444b8f974df77 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -6,7 +6,7 @@ jobs: name: 'Validation' runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - uses: gradle/wrapper-validation-action@v3 diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 2a80325dba9d4d..9090ab305dadfa 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -33,7 +33,7 @@ jobs: WP_ARTIFACTS_PATH: ${{ github.workspace }}/artifacts steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/php-changes-detection.yml b/.github/workflows/php-changes-detection.yml index 6d0a0a451a5153..2642485bce1ce2 100644 --- a/.github/workflows/php-changes-detection.yml +++ b/.github/workflows/php-changes-detection.yml @@ -10,14 +10,14 @@ jobs: if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} steps: - name: Check out code - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Get changed PHP files id: changed-files-php - uses: tj-actions/changed-files@a29e8b565651ce417abb5db7164b4a2ad8b6155c # v44.4.0 + uses: tj-actions/changed-files@03334d095e2739fa9ac4034ec16f66d5d01e9eba # v44.5.1 with: files: | lib/** diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml index 94397afd7b4bca..ab139c19e6ddec 100644 --- a/.github/workflows/publish-npm-packages.yml +++ b/.github/workflows/publish-npm-packages.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout (for CLI) if: ${{ github.event.inputs.release_type != 'wp' }} - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: cli ref: trunk @@ -39,7 +39,7 @@ jobs: - name: Checkout (for publishing) if: ${{ github.event.inputs.release_type != 'wp' }} - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: publish # Later, we switch this branch in the script that publishes packages. @@ -49,7 +49,7 @@ jobs: - name: Checkout (for publishing WP major version) if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }} - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: publish ref: wp/${{ github.event.inputs.wp_version }} diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml index 099203bbffe720..61c522474a7b3d 100644 --- a/.github/workflows/pull-request-automation.yml +++ b/.github/workflows/pull-request-automation.yml @@ -12,7 +12,7 @@ jobs: steps: # Checkout defaults to using the branch which triggered the event, which # isn't necessarily `trunk` (e.g. in the case of a merge). - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index 850f0be6631344..bd23fb219ce752 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -23,7 +23,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/rnmobile-ios-runner.yml b/.github/workflows/rnmobile-ios-runner.yml index 516f783c11e401..9640243e86629c 100644 --- a/.github/workflows/rnmobile-ios-runner.yml +++ b/.github/workflows/rnmobile-ios-runner.yml @@ -23,11 +23,11 @@ jobs: native-test-name: [gutenberg-editor-rendering] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 + - uses: ruby/setup-ruby@943103cae7d3f1bb1e4951d5fcc7928b40e4b742 # v1.177.1 with: # `.ruby-version` file location working-directory: packages/react-native-editor/ios diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index ff3fe96d505f6f..32d7c609681196 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -22,7 +22,7 @@ jobs: if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml index 7486ea32533e6a..dfe301386849f6 100644 --- a/.github/workflows/storybook-pages.yml +++ b/.github/workflows/storybook-pages.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index a4a639e183d5bf..dd50ea68e0e5d5 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -70,7 +70,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -121,7 +121,7 @@ jobs: name: Build JavaScript assets for PHP unit tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -170,7 +170,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -281,7 +281,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -351,7 +351,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 8f57a749b0601d..60ecacb2a8d705 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -96,7 +96,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: ${{ matrix.branch }} token: ${{ secrets.GUTENBERG_TOKEN }} diff --git a/backport-changelog/6.6/6590.md b/backport-changelog/6.6/6590.md new file mode 100644 index 00000000000000..47ef89e0db40cb --- /dev/null +++ b/backport-changelog/6.6/6590.md @@ -0,0 +1,5 @@ +https://github.com/WordPress/wordpress-develop/pull/6590 + +* https://github.com/WordPress/gutenberg/pull/59531 +* https://github.com/WordPress/gutenberg/pull/61182 +* https://github.com/WordPress/gutenberg/pull/61717 diff --git a/backport-changelog/6.6/6616.md b/backport-changelog/6.6/6616.md new file mode 100644 index 00000000000000..91261f78fb5c7a --- /dev/null +++ b/backport-changelog/6.6/6616.md @@ -0,0 +1,5 @@ +https://github.com/WordPress/wordpress-develop/pull/6616 + +* https://github.com/WordPress/gutenberg/pull/58409 +* https://github.com/WordPress/gutenberg/pull/61328 +* https://github.com/WordPress/gutenberg/pull/61842 \ No newline at end of file diff --git a/backport-changelog/6.6/6662.md b/backport-changelog/6.6/6662.md index 2dfbc68dd23e03..5b25fc99304919 100644 --- a/backport-changelog/6.6/6662.md +++ b/backport-changelog/6.6/6662.md @@ -1,3 +1,4 @@ https://github.com/WordPress/wordpress-develop/pull/6662 * https://github.com/WordPress/gutenberg/pull/57908 +* https://github.com/WordPress/gutenberg/pull/62125 diff --git a/backport-changelog/6.6/6694.md b/backport-changelog/6.6/6694.md new file mode 100644 index 00000000000000..a9eb5a7f37ef5b --- /dev/null +++ b/backport-changelog/6.6/6694.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/6694 + +* https://github.com/WordPress/gutenberg/pull/60694 diff --git a/changelog.txt b/changelog.txt index 5791fb711c2c43..006ca4a7c3e6b6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,472 @@ == Changelog == += 18.5.0-rc.1 = + + +## Changelog + +### Features + +#### Global Styles +- Add defaultSpacingSizes option (theme.json v3). ([61842](https://github.com/WordPress/gutenberg/pull/61842)) +- Edit/create shadows in global styles. ([60706](https://github.com/WordPress/gutenberg/pull/60706)) +- Relocate Background Image controls to sit under Layout. ([61886](https://github.com/WordPress/gutenberg/pull/61886)) + +#### Block Library +- Enable shadow support for cover block. ([61883](https://github.com/WordPress/gutenberg/pull/61883)) + +#### Block bindings +- Allow editing in post meta source. ([61753](https://github.com/WordPress/gutenberg/pull/61753)) + +#### Script Modules API +- Add script module data implementation. ([61658](https://github.com/WordPress/gutenberg/pull/61658)) + +#### Synced Patterns +- Add `__default` binding for pattern overrides. ([60694](https://github.com/WordPress/gutenberg/pull/60694)) + +#### Block Styles +- Extend block style variations as mechanism for achieving section styling. ([57908](https://github.com/WordPress/gutenberg/pull/57908)) + + +### Enhancements + +- Block card: Fix typographic widow. ([61438](https://github.com/WordPress/gutenberg/pull/61438)) +- Block settings: Update variant of "Apply globally" Button component to secondary. ([61850](https://github.com/WordPress/gutenberg/pull/61850)) +- Editor: Align the Post Format control design with the rest of the post sidebar controls. ([62066](https://github.com/WordPress/gutenberg/pull/62066)) +- Editor: Polish the style of some of the post summary rows. ([61645](https://github.com/WordPress/gutenberg/pull/61645)) +- Format Library: Refactor 'Inline Image' edit component. ([62135](https://github.com/WordPress/gutenberg/pull/62135)) +- Playwright end-to-end Utils: Add fullscreenMode option to createNewPost. ([61766](https://github.com/WordPress/gutenberg/pull/61766)) +- Post Sticky Toggle: Improve the design. ([62012](https://github.com/WordPress/gutenberg/pull/62012)) +- Post Summary: Move PostTemplatePanel below URL and Author. ([62137](https://github.com/WordPress/gutenberg/pull/62137)) +- Remove trash button in post/page inspector. ([61792](https://github.com/WordPress/gutenberg/pull/61792)) +- Shadows instead of borders on interface skeleton. ([61835](https://github.com/WordPress/gutenberg/pull/61835)) +- Tweak contextual block toolbar position. ([61836](https://github.com/WordPress/gutenberg/pull/61836)) +- Update: For synced entities the icon should be purple. ([62024](https://github.com/WordPress/gutenberg/pull/62024)) +- Update: Implement new author panel design. ([61362](https://github.com/WordPress/gutenberg/pull/61362)) +- Update: Implement new parent and order design. ([61918](https://github.com/WordPress/gutenberg/pull/61918)) +- Update: Move duplicate pattern and template part actions to the editor package. ([61879](https://github.com/WordPress/gutenberg/pull/61879)) + +#### Site Editor +- Block Editor: Check for multiple block usage in the block-editor package. ([62086](https://github.com/WordPress/gutenberg/pull/62086)) +- Copy custom CSS between variations when switching. ([61752](https://github.com/WordPress/gutenberg/pull/61752)) +- Data views: Align page headers. ([62115](https://github.com/WordPress/gutenberg/pull/62115)) +- Inspector summary rows: Make tooltips appear middle-left. ([61815](https://github.com/WordPress/gutenberg/pull/61815)) +- Inspector: Add '/' prefix to Link button. ([62073](https://github.com/WordPress/gutenberg/pull/62073)) +- Inspector: Display home / posts page badge. ([62071](https://github.com/WordPress/gutenberg/pull/62071)) +- Inspector: Remove revisions panel. ([61867](https://github.com/WordPress/gutenberg/pull/61867)) +- Make post meta row button treatment consistent. ([61954](https://github.com/WordPress/gutenberg/pull/61954)) +- Remove 'Manage...' prefix in Pages / Templates data views. ([62107](https://github.com/WordPress/gutenberg/pull/62107)) +- Remove the details pages. ([61741](https://github.com/WordPress/gutenberg/pull/61741)) +- Update actions order in site editor for template and template parts. ([61803](https://github.com/WordPress/gutenberg/pull/61803)) +- Use site title as a link. ([61258](https://github.com/WordPress/gutenberg/pull/61258)) +- [Site Editor]: Add create pattern button in patterns page. ([60302](https://github.com/WordPress/gutenberg/pull/60302)) +- withRegistryProvider: Prevent intermediate state with no children. ([61859](https://github.com/WordPress/gutenberg/pull/61859)) + +#### Data Views +- Add badge to title for posts & front pages. ([61718](https://github.com/WordPress/gutenberg/pull/61718)) +- Clarify `date` value in Pages. ([61709](https://github.com/WordPress/gutenberg/pull/61709)) +- DataViews: `label` prop in Actions API can be either a `string` or a `function`. ([61942](https://github.com/WordPress/gutenberg/pull/61942)) +- Fix pagination position on pages with short lists. ([61712](https://github.com/WordPress/gutenberg/pull/61712)) +- Pages data view: Add Pending and Private views. ([62138](https://github.com/WordPress/gutenberg/pull/62138)) +- Pages sidebar: Adds published & scheduled items. ([62021](https://github.com/WordPress/gutenberg/pull/62021)) +- Stop Patterns data view header shrinking. ([61801](https://github.com/WordPress/gutenberg/pull/61801)) +- Update grid layout on small screens. ([61820](https://github.com/WordPress/gutenberg/pull/61820)) +- Update list layout action styling. ([61797](https://github.com/WordPress/gutenberg/pull/61797)) +- Update page component (and some data view elements) spacing metrics. ([61333](https://github.com/WordPress/gutenberg/pull/61333)) +- Visually hide 'Actions' column header. ([61710](https://github.com/WordPress/gutenberg/pull/61710)) + +#### Global Styles +- Add block-level Text Alignment UI. ([61717](https://github.com/WordPress/gutenberg/pull/61717)) +- Add option to remove site-wide theme background image. ([61998](https://github.com/WordPress/gutenberg/pull/61998)) +- Background image: Add support for relative theme path URLs in top-level theme.json styles. ([61271](https://github.com/WordPress/gutenberg/pull/61271)) +- Background image: Update controls defaults and layout. ([62000](https://github.com/WordPress/gutenberg/pull/62000)) +- Background images: Add defaults for background size. ([62046](https://github.com/WordPress/gutenberg/pull/62046)) +- Don't filter out typography variations where the heading and body fonts are the same. ([61327](https://github.com/WordPress/gutenberg/pull/61327)) +- Make color variations fit in a bit better visually. ([61617](https://github.com/WordPress/gutenberg/pull/61617)) +- Make it clearer how to edit a site's palette. ([61364](https://github.com/WordPress/gutenberg/pull/61364)) +- Move type presets below elements. ([61863](https://github.com/WordPress/gutenberg/pull/61863)) +- Restore the default variation to the color and typography style tiles. ([61901](https://github.com/WordPress/gutenberg/pull/61901)) +- Show shadow tool by default under global styles. ([61981](https://github.com/WordPress/gutenberg/pull/61981)) + +#### Components +- Add vw and vh units to the custom font size picker. ([60607](https://github.com/WordPress/gutenberg/pull/60607)) +- CustomSelectControlV2: Use `InputBase` for styling. ([60261](https://github.com/WordPress/gutenberg/pull/60261)) +- Tabs: Indicator animation. ([60560](https://github.com/WordPress/gutenberg/pull/60560)) +- Try: Add CSS Custom Properties to CSS types. ([61872](https://github.com/WordPress/gutenberg/pull/61872)) + +#### Zoom Out +- Hide inserters behind the experiment flag. ([61866](https://github.com/WordPress/gutenberg/pull/61866)) +- Inserter: Auto-close the inserter unless the zoom out experiment is on. ([61856](https://github.com/WordPress/gutenberg/pull/61856)) +- Show the inserters only when a section is selected. ([61559](https://github.com/WordPress/gutenberg/pull/61559)) +- The patterns tab behind a new experiment. ([61601](https://github.com/WordPress/gutenberg/pull/61601)) + +#### Block Editor +- Adjust pattern list items resting, hover, focus styles. ([61831](https://github.com/WordPress/gutenberg/pull/61831)) +- Tweak pattern categories sidebar. ([62113](https://github.com/WordPress/gutenberg/pull/62113)) +- Writing flow: Remove first empty paragraph on Backspace. ([61889](https://github.com/WordPress/gutenberg/pull/61889)) + +#### Block bindings +- Add Block Bindings Panel to Block Inspector. ([61527](https://github.com/WordPress/gutenberg/pull/61527)) +- Add indicator for metadata changes to Save Panel when reviewing modified entities. ([61811](https://github.com/WordPress/gutenberg/pull/61811)) +- Lock binding editing with functions. ([61734](https://github.com/WordPress/gutenberg/pull/61734)) + +#### Block Variations +- Have `getActiveBlockVariation` return variation with highest specificity. ([62031](https://github.com/WordPress/gutenberg/pull/62031)) +- Support dot notation in `isActive` string array. ([62088](https://github.com/WordPress/gutenberg/pull/62088)) + +#### Layout +- More consistent root padding. ([60715](https://github.com/WordPress/gutenberg/pull/60715)) +- Try using coloured overlay instead of border for grid visualiser. ([61390](https://github.com/WordPress/gutenberg/pull/61390)) + +#### Block Library +- Added Bluesky icon to the Social Icon Block. ([61372](https://github.com/WordPress/gutenberg/pull/61372)) +- Media & Text: Replace the deprecated __experimentalImageSizeControl with ResolutionTool. ([57540](https://github.com/WordPress/gutenberg/pull/57540)) + +#### Inspector Controls +- Align both "Design" pattern list panels. ([62161](https://github.com/WordPress/gutenberg/pull/62161)) + +#### Post Editor +- Add home template details to inspector controls. ([61762](https://github.com/WordPress/gutenberg/pull/61762)) + +#### Interactivity API +- Clarify some warning messages. ([61720](https://github.com/WordPress/gutenberg/pull/61720)) + +#### Patterns +- Adjust the icons and text of the binding connected blocks. ([61560](https://github.com/WordPress/gutenberg/pull/61560)) + + +### Bug Fixes + +- Compose: Fix 'useFocusOnMount' cleanup callback. ([62053](https://github.com/WordPress/gutenberg/pull/62053)) +- Do not auto save post status changes. ([62171](https://github.com/WordPress/gutenberg/pull/62171)) +- Editor: Fix canvas padding in post editor. ([61893](https://github.com/WordPress/gutenberg/pull/61893)) +- EntityProvider: Avoid remounts and simplify. ([61882](https://github.com/WordPress/gutenberg/pull/61882)) +- Fix shadow and border for pattern categories panel. ([62158](https://github.com/WordPress/gutenberg/pull/62158)) +- Image Block: Conditionally Render Block Control Based on Component Presence. ([62132](https://github.com/WordPress/gutenberg/pull/62132)) +- Interactivity API: Fix null and number strings as namespaces runtime error. ([61960](https://github.com/WordPress/gutenberg/pull/61960)) +- PostCardPanel: Fix ESLint error. ([62109](https://github.com/WordPress/gutenberg/pull/62109)) +- Remove build-types/ clean from clean:Packages. ([62008](https://github.com/WordPress/gutenberg/pull/62008)) +- Script Modules: Fix private method reflection access. ([62154](https://github.com/WordPress/gutenberg/pull/62154)) +- ServerSideRender: Fix data loading in development mode. ([62140](https://github.com/WordPress/gutenberg/pull/62140)) +- Shadow Panel: Make subtitle translatable. ([62022](https://github.com/WordPress/gutenberg/pull/62022)) +- Site Editor: Fix the Root Padding styles. ([61906](https://github.com/WordPress/gutenberg/pull/61906)) +- Writing flow: Fix heading crash on split (via paste). ([61900](https://github.com/WordPress/gutenberg/pull/61900)) +- e2e: Fix Site Editor Styles test. ([62111](https://github.com/WordPress/gutenberg/pull/62111)) + +#### Post Editor +- Consolidate and fix `delete` and `edit` post actions. ([61912](https://github.com/WordPress/gutenberg/pull/61912)) +- Consolidate and fix `rename` post action. ([61857](https://github.com/WordPress/gutenberg/pull/61857)) +- Document Bar: Decode HTML entities and take into account cases where there is no title. ([62087](https://github.com/WordPress/gutenberg/pull/62087)) +- Editor: Don't apply purple accent to the unsynced pattern title. ([61704](https://github.com/WordPress/gutenberg/pull/61704)) +- Editor: Ensure Copy button in sidebar copies whole permalink, *with* URL protocol. ([61876](https://github.com/WordPress/gutenberg/pull/61876)) +- Editor: Fix the 'DocumentBar' position for long titles. ([61691](https://github.com/WordPress/gutenberg/pull/61691)) +- Editor: Render publish date control when the status is `future`(scheduled). ([62070](https://github.com/WordPress/gutenberg/pull/62070)) +- Editor: Unify button size in pre-publish panel. ([62123](https://github.com/WordPress/gutenberg/pull/62123)) +- Editor: Use edited entity for post actions. ([61892](https://github.com/WordPress/gutenberg/pull/61892)) +- Fix read only post status styles. ([61722](https://github.com/WordPress/gutenberg/pull/61722)) +- Post Actions: Hide the trash action for auto-drafts. ([61865](https://github.com/WordPress/gutenberg/pull/61865)) + +#### Block Editor +- Fix being unable to switch modes while inserter is open. ([61563](https://github.com/WordPress/gutenberg/pull/61563)) +- Fix editor inserter tabs indicator. ([61973](https://github.com/WordPress/gutenberg/pull/61973)) +- Fix positioning of close icons in panels to be consistent. ([61832](https://github.com/WordPress/gutenberg/pull/61832)) +- Fix syncing of publish date between publish and post status panel. ([62165](https://github.com/WordPress/gutenberg/pull/62165)) +- Improve link conrol preview when show button text label is enabled. ([61726](https://github.com/WordPress/gutenberg/pull/61726)) +- Inserter: Show all blocks (alternative). ([62169](https://github.com/WordPress/gutenberg/pull/62169)) +- InspectorControls: Text not displayed when "Show button text labels" is enabled. ([61949](https://github.com/WordPress/gutenberg/pull/61949)) +- Link Control: Fix focus handlers in development mode. ([62141](https://github.com/WordPress/gutenberg/pull/62141)) +- Media & Text block: Remove the link option when the featured image is used. ([60510](https://github.com/WordPress/gutenberg/pull/60510)) +- Writing flow: Fix paste for input fields. ([61389](https://github.com/WordPress/gutenberg/pull/61389)) + +#### Block Library +- Classic block: Fix content syncing effect for React StrictMode. ([62051](https://github.com/WordPress/gutenberg/pull/62051)) +- Don't steal focus when opening browse all blocks. ([61975](https://github.com/WordPress/gutenberg/pull/61975)) +- Fix: The latest post block - post titles overlapping. ([61356](https://github.com/WordPress/gutenberg/pull/61356)) +- Fixed : Update `alt text decision tree` links to be translatable. ([62076](https://github.com/WordPress/gutenberg/pull/62076)) +- Fixed: Custom HTML Block should display content in LTR layout for all languages. ([62083](https://github.com/WordPress/gutenberg/pull/62083)) +- More block: Fix React warning when adding custom text. ([61936](https://github.com/WordPress/gutenberg/pull/61936)) +- useUploadMediaFromBlobURL: Prevent duplicate uploads in StrictMode. ([62059](https://github.com/WordPress/gutenberg/pull/62059)) + +#### Global Styles +- Fix make dimensions.aspectRatios key of theme.json files translatable. ([61774](https://github.com/WordPress/gutenberg/pull/61774)) +- Hide the presets panel for when there are less or exactly one presets available. ([62074](https://github.com/WordPress/gutenberg/pull/62074)) +- Prevent Typography panel title from wrapping. ([62124](https://github.com/WordPress/gutenberg/pull/62124)) +- Shadow Panel: Generates unique shadow slugs by finding max suffix and incrementing it. ([61997](https://github.com/WordPress/gutenberg/pull/61997)) +- Styles: try wrapping with :Root to fix reset styles. ([61638](https://github.com/WordPress/gutenberg/pull/61638)) +- Transform Styles: Update selector so that styles work when custom fields panel is active. ([62121](https://github.com/WordPress/gutenberg/pull/62121)) + +#### Site Editor +- Align the template title to the center in the 'Add template' screen. ([62175](https://github.com/WordPress/gutenberg/pull/62175)) +- Close publish sidebar if not in `edit` mode. ([61707](https://github.com/WordPress/gutenberg/pull/61707)) +- Fix the site editor Admin Bar menu item. ([61851](https://github.com/WordPress/gutenberg/pull/61851)) +- Use a consistent snackbar position. ([61756](https://github.com/WordPress/gutenberg/pull/61756)) + +#### Components +- Fix: The focus styles for tabPanel. ([61317](https://github.com/WordPress/gutenberg/pull/61317)) +- InputControl: Fix z-index issue causing slider dots to appear in front of the Appearance dropdown. ([61937](https://github.com/WordPress/gutenberg/pull/61937)) +- getAutocompleterUI: Don't redefine ListBox component on every render. ([61877](https://github.com/WordPress/gutenberg/pull/61877)) + +#### Synced Patterns +- Block Bindings: Filter pattern overrides source in bindings panel. ([62015](https://github.com/WordPress/gutenberg/pull/62015)) +- Fix detaching patterns when a pattern has overrides, but there are no override values. ([62014](https://github.com/WordPress/gutenberg/pull/62014)) + +#### Block bindings +- Don't show non-existing and not supported attributes in block bindings panel. ([62183](https://github.com/WordPress/gutenberg/pull/62183)) + +#### Layout +- Remove extra bracket in the site editor root padding styles. ([62159](https://github.com/WordPress/gutenberg/pull/62159)) + +#### Block Styles +- Fix block style variation styles for blocks with complex selectors. ([62125](https://github.com/WordPress/gutenberg/pull/62125)) + +#### Code Editor +- Editor: Unify text/code editor between post and site editors. ([61934](https://github.com/WordPress/gutenberg/pull/61934)) + +#### Page Content Focus +- Remove lock icons from Content blocks inner blocks when editing a page in the site editor. ([61922](https://github.com/WordPress/gutenberg/pull/61922)) + +#### Patterns +- Templates: Only resolve patterns for REST API endpoints. ([61757](https://github.com/WordPress/gutenberg/pull/61757)) + +#### Interactivity API +- Turn named capturing groups back into numbered ones inside `toVdom`. ([61728](https://github.com/WordPress/gutenberg/pull/61728)) + +#### Block API +- Fix: Enable Text Align UI to be controlled correctly with theme.json. ([61182](https://github.com/WordPress/gutenberg/pull/61182)) + +#### REST API +- Return an empty object when no fallback templates are found (wp/v2/templates/lookup). ([60925](https://github.com/WordPress/gutenberg/pull/60925)) + + +### Accessibility + +#### Global Styles +- Shadow Panel: Improve a11y and fix browser console error. ([61980](https://github.com/WordPress/gutenberg/pull/61980)) + +#### Data Views +- Always show Actions table header. ([61847](https://github.com/WordPress/gutenberg/pull/61847)) + +#### Block Library +- Fix: Adds help props for description of Play Inline toggle. ([61310](https://github.com/WordPress/gutenberg/pull/61310)) + + +### Performance + +- Perf: Batch block list settings in single action. ([61329](https://github.com/WordPress/gutenberg/pull/61329)) +- Remove additional call to `WP_Theme_JSON_Gutenberg::__construct`. ([61262](https://github.com/WordPress/gutenberg/pull/61262)) + +#### Interactivity API +- Introduce `wp-on-async` directive as performant alternative over synchronous `wp-on` directive. ([61885](https://github.com/WordPress/gutenberg/pull/61885)) + +#### Post Editor +- DocumentBar: Only selected data needed for rendering. ([61706](https://github.com/WordPress/gutenberg/pull/61706)) + + +### Experiments + +#### Interactivity API +- Use output buffer and HTML tag processor to inject directives on BODY tag for full-page client-side navigation. ([61212](https://github.com/WordPress/gutenberg/pull/61212)) + + +### Documentation + +- Add JSDoc to PostVisibility, PostVisibilityCheck, and PostVisibilityLabel. ([61735](https://github.com/WordPress/gutenberg/pull/61735)) +- Add PostURL component documentation. ([61737](https://github.com/WordPress/gutenberg/pull/61737)) +- Add a section about block filters to the Filters and Hooks doc. ([61771](https://github.com/WordPress/gutenberg/pull/61771)) +- Add an example and improve readability of the Block Filters doc. ([61770](https://github.com/WordPress/gutenberg/pull/61770)) +- Add docblock to PostTitle and PostTitleRaw component. ([61740](https://github.com/WordPress/gutenberg/pull/61740)) +- Add documentation for DocumentBar. ([61733](https://github.com/WordPress/gutenberg/pull/61733)) +- Add documentation for PostFeaturedImage, PostFeaturedImageCheck, PostFeaturedImagePanel. ([61165](https://github.com/WordPress/gutenberg/pull/61165)) +- Add documentation for PostLastRevision, PostLastRevisionCheck, PostLastRevisionPanel components. ([61166](https://github.com/WordPress/gutenberg/pull/61166)) +- Add documentation for PostSchedule, PostScheduleCheck, PostSchedulePanel, PostScheduleLabel, usePostScheduleLabel components. ([61345](https://github.com/WordPress/gutenberg/pull/61345)) +- Add documentation for the EditorNotices component. ([61736](https://github.com/WordPress/gutenberg/pull/61736)) +- Add documentation for the EditorProvider and ExperimentalEditorProvider components. ([61739](https://github.com/WordPress/gutenberg/pull/61739)) +- Added missing @global documentation. ([61537](https://github.com/WordPress/gutenberg/pull/61537)) +- Changelog: Add note about removing legacy operators. ([62013](https://github.com/WordPress/gutenberg/pull/62013)) +- Docs: Fix spacing in PHP doc block in comments block. ([61911](https://github.com/WordPress/gutenberg/pull/61911)) +- EditorBoundary editor component. ([61950](https://github.com/WordPress/gutenberg/pull/61950)) +- Fix typo. ([61830](https://github.com/WordPress/gutenberg/pull/61830)) +- Fix: Block library README.md link. ([62081](https://github.com/WordPress/gutenberg/pull/62081)) +- Fix: Custom block editor link. ([61962](https://github.com/WordPress/gutenberg/pull/61962)) +- For `PostTextEditor` component. ([62099](https://github.com/WordPress/gutenberg/pull/62099)) +- LocalAutosaveMonitor editor component. ([61951](https://github.com/WordPress/gutenberg/pull/61951)) +- PageTemplate + PostTemplatePanel editor components. ([61961](https://github.com/WordPress/gutenberg/pull/61961)) +- PostComments editor component. ([61964](https://github.com/WordPress/gutenberg/pull/61964)) +- PostDiscussionPanel editor component. ([61966](https://github.com/WordPress/gutenberg/pull/61966)) +- PostExcerptPanel editor component. ([61967](https://github.com/WordPress/gutenberg/pull/61967)) +- PostLockedModal editor component. ([61968](https://github.com/WordPress/gutenberg/pull/61968)) +- PostPendingStatus + PostPendingStatusCheck editor components. ([61970](https://github.com/WordPress/gutenberg/pull/61970)) +- PostPingbacks editor component. ([62035](https://github.com/WordPress/gutenberg/pull/62035)) +- PostPreviewButton editor component. ([62036](https://github.com/WordPress/gutenberg/pull/62036)) +- Storybook: Add badges based on `tags`. ([61111](https://github.com/WordPress/gutenberg/pull/61111)) +- Update PostFormat, PostFormatCheck editor component documentation. ([61732](https://github.com/WordPress/gutenberg/pull/61732)) +- Update block.json file with correct links. ([61880](https://github.com/WordPress/gutenberg/pull/61880)) +- Update link to architecture key concepts. ([61965](https://github.com/WordPress/gutenberg/pull/61965)) +- Update links to correct lodash website. ([62188](https://github.com/WordPress/gutenberg/pull/62188)) +- Update plugin-document-setting-panel.md. ([61782](https://github.com/WordPress/gutenberg/pull/61782)) +- Update tutorial.md. ([62054](https://github.com/WordPress/gutenberg/pull/62054)) + + +### Code Quality + +- Add curly brace autofix commit to `.git-blame-ignore-revs`. ([62144](https://github.com/WordPress/gutenberg/pull/62144)) +- Add eslint rule for curly brace presence in JSX. ([62026](https://github.com/WordPress/gutenberg/pull/62026)) +- Blocks: Remove pipe usage and dependency on compose. ([62127](https://github.com/WordPress/gutenberg/pull/62127)) +- Clean up packages build-types when cleaning types. ([61939](https://github.com/WordPress/gutenberg/pull/61939)) +- Command Palette: Remove unused URL parameter. ([61783](https://github.com/WordPress/gutenberg/pull/61783)) +- Commands: Unify the editor context between post and site editors. ([61862](https://github.com/WordPress/gutenberg/pull/61862)) +- Dataviews: Remove unused dependencies. ([62010](https://github.com/WordPress/gutenberg/pull/62010)) +- Distraction Free: Unify the header animation. ([62167](https://github.com/WordPress/gutenberg/pull/62167)) +- Editor: Move editor toggle commands to the editor package. ([62093](https://github.com/WordPress/gutenberg/pull/62093)) +- Editor: Move the InterfaceSkeleton to the editor package. ([62118](https://github.com/WordPress/gutenberg/pull/62118)) +- Editor: Move the resizing of the editor to the EditorCanvas component. ([61896](https://github.com/WordPress/gutenberg/pull/61896)) +- Editor: Remove extra div container and unify the container between post and site editors. ([62016](https://github.com/WordPress/gutenberg/pull/62016)) +- Editor: Remove obsolete `listViewLabel` prop from DocumentTools. ([62032](https://github.com/WordPress/gutenberg/pull/62032)) +- Editor: Remove useless props from InserterSidebar component. ([62103](https://github.com/WordPress/gutenberg/pull/62103)) +- Editor: Unify the MediaUpload hook between post and site editors. ([62085](https://github.com/WordPress/gutenberg/pull/62085)) +- Editor: Unify the content area of the post and site editors. ([61860](https://github.com/WordPress/gutenberg/pull/61860)) +- Fix: React compiler error on button. ([61958](https://github.com/WordPress/gutenberg/pull/61958)) +- Fix: Remove unused css block on patterns page. ([62058](https://github.com/WordPress/gutenberg/pull/62058)) +- Fix: Remove unused css code from the navigation screen. ([62060](https://github.com/WordPress/gutenberg/pull/62060)) +- Fix: Some jsdoc return types on edit site selector. ([62061](https://github.com/WordPress/gutenberg/pull/62061)) +- Improve distclean script. ([62019](https://github.com/WordPress/gutenberg/pull/62019)) +- Interactivity API: Move all utils inside `utils.ts`. ([61721](https://github.com/WordPress/gutenberg/pull/61721)) +- Interactivity API: Move init.js to TypeScript. ([61723](https://github.com/WordPress/gutenberg/pull/61723)) +- Make onPatternCategorySelection private. ([62130](https://github.com/WordPress/gutenberg/pull/62130)) +- Remove useless clsx calls. ([61969](https://github.com/WordPress/gutenberg/pull/61969)) +- Rename backport-changelog/6279.md to backport-changelog/6.6/6279.md. ([61894](https://github.com/WordPress/gutenberg/pull/61894)) +- Update: Remove unused components. ([61955](https://github.com/WordPress/gutenberg/pull/61955)) +- end-to-end Tests: Fix React warnings triggered by test plugins. ([61935](https://github.com/WordPress/gutenberg/pull/61935)) + +#### Components +- CustomSelectControl: Fix `menuProps` mutation. ([62149](https://github.com/WordPress/gutenberg/pull/62149)) +- Fix remaining warning in ColorPanelDropdown. ([61933](https://github.com/WordPress/gutenberg/pull/61933)) +- Make the `ProgressBar` public. ([61062](https://github.com/WordPress/gutenberg/pull/61062)) +- Remove reduceMotion utility. ([61963](https://github.com/WordPress/gutenberg/pull/61963)) +- SlotFills: Use state for registry initialization. ([61802](https://github.com/WordPress/gutenberg/pull/61802)) +- Style Book: Use state to initialize examples. ([61848](https://github.com/WordPress/gutenberg/pull/61848)) +- Tooltip: Fix Ariakit tooltip store usage. ([61858](https://github.com/WordPress/gutenberg/pull/61858)) +- `ProgressBar`: Simplify default `width` implementation and make it more easily overridable. ([61976](https://github.com/WordPress/gutenberg/pull/61976)) + +#### Block Editor +- Fix `ZoomOutModeInserters` dependencies. ([61908](https://github.com/WordPress/gutenberg/pull/61908)) +- Fix wrapper props mutation in BlockListBlock. ([61789](https://github.com/WordPress/gutenberg/pull/61789)) +- Remove some utility functions. ([61784](https://github.com/WordPress/gutenberg/pull/61784)) +- Shadows: Unlock private components and hooks at the file level. ([61790](https://github.com/WordPress/gutenberg/pull/61790)) +- Unlock private setting keys at the file level. ([61813](https://github.com/WordPress/gutenberg/pull/61813)) +- Unlock the private 'kebabCase' function at a file level. ([60755](https://github.com/WordPress/gutenberg/pull/60755)) +- useBlockInspectorAnimationSettings: Remove unnecessary deps. ([61822](https://github.com/WordPress/gutenberg/pull/61822)) + +#### Data Views +- DataViews: Full type the dataviews package. ([61854](https://github.com/WordPress/gutenberg/pull/61854)) +- DataViews: Remove non-used file. ([61853](https://github.com/WordPress/gutenberg/pull/61853)) +- DataViews: Remove unnecessary dependency for pattern fields memo. ([61870](https://github.com/WordPress/gutenberg/pull/61870)) +- DataViews: Type all the filters components. ([61795](https://github.com/WordPress/gutenberg/pull/61795)) +- DataViews: Type the BulkActionsToolbar component. ([61673](https://github.com/WordPress/gutenberg/pull/61673)) +- DataViews: Type the ViewActions component. ([61729](https://github.com/WordPress/gutenberg/pull/61729)) +- DataViews: Type the ViewTable component. ([61682](https://github.com/WordPress/gutenberg/pull/61682)) + +#### Block Library +- Added unit test for post excerpt block render function. ([43451](https://github.com/WordPress/gutenberg/pull/43451)) +- Avoid using component naming conventions for non-component code. ([61793](https://github.com/WordPress/gutenberg/pull/61793)) +- Button: Fix ESLint warning. ([62126](https://github.com/WordPress/gutenberg/pull/62126)) +- Remove CSS hack for Internet Explorer 11. ([62043](https://github.com/WordPress/gutenberg/pull/62043)) +- Remove useless styles. ([62017](https://github.com/WordPress/gutenberg/pull/62017)) +- Search Block: Fix `borderRadius` mutation. ([61794](https://github.com/WordPress/gutenberg/pull/61794)) + +#### Site Editor +- History: Add getLocationWithParams method. ([61823](https://github.com/WordPress/gutenberg/pull/61823)) +- Navigation Focus Mode: Remove leftover code. ([61897](https://github.com/WordPress/gutenberg/pull/61897)) +- Remove useless onClick handler. ([61902](https://github.com/WordPress/gutenberg/pull/61902)) +- Update to use the EditorInterface component from the editor package. ([62146](https://github.com/WordPress/gutenberg/pull/62146)) + +#### Block hooks +- Navigation block: Check for insert_hooked_blocks_into_rest_response i…. ([62134](https://github.com/WordPress/gutenberg/pull/62134)) +- Navigation block: Check for update_ignored_hooked_blocks_postmeta in core. ([61903](https://github.com/WordPress/gutenberg/pull/61903)) + +#### Font Library +- Font Library Modal: Remove some contexts. ([62042](https://github.com/WordPress/gutenberg/pull/62042)) + +#### Post Editor +- Template Actions: Fix console error when resetting template. ([61921](https://github.com/WordPress/gutenberg/pull/61921)) + +#### Global Styles +- Components: Fix React Warning triggers by the new JSX transform. ([61917](https://github.com/WordPress/gutenberg/pull/61917)) + +#### Interactivity API +- Interactivity API : Refactor interactivity-router to TS. ([61730](https://github.com/WordPress/gutenberg/pull/61730)) + +#### CSS & Styling +- Fix editor view mode canvas shadow. ([61688](https://github.com/WordPress/gutenberg/pull/61688)) + + +### Tools + +- Build: Use globalThis over process.env and enable TS lib checking. ([61486](https://github.com/WordPress/gutenberg/pull/61486)) + +#### Testing +- E2E: Fix canvas waiter in visitSiteEditor. ([61816](https://github.com/WordPress/gutenberg/pull/61816)) +- PaletteEdit: Fix another flaky test. ([61818](https://github.com/WordPress/gutenberg/pull/61818)) +- PaletteEdit: Fix flaky test. ([61791](https://github.com/WordPress/gutenberg/pull/61791)) +- Shadow: Add unit tests for shadow support. ([60063](https://github.com/WordPress/gutenberg/pull/60063)) +- Skip flaky 'Zoom out' end-to-end test. ([61925](https://github.com/WordPress/gutenberg/pull/61925)) +- Synced Pattern: Wait for pattern creation in end-to-end tests. ([62174](https://github.com/WordPress/gutenberg/pull/62174)) +- Tests: Change how directives processing gets disabled. ([62095](https://github.com/WordPress/gutenberg/pull/62095)) +- Workflows: Try a backport changelog. ([61785](https://github.com/WordPress/gutenberg/pull/61785)) + +#### Build Tooling +- Add 60 minute timeout to performance job. ([61957](https://github.com/WordPress/gutenberg/pull/61957)) +- Enable parallel processing for PHPCS sniffs. ([61700](https://github.com/WordPress/gutenberg/pull/61700)) +- Fix an issue causing wp-scripts commands to fail if the file path contained a space character. ([61748](https://github.com/WordPress/gutenberg/pull/61748)) +- React: Upgrade to the new JSX transform. ([61692](https://github.com/WordPress/gutenberg/pull/61692)) +- Workflows: Test to check for label and skip backport changelog. ([61808](https://github.com/WordPress/gutenberg/pull/61808)) + + +### Various + +- Inserter: Encapsulate styles for tablist and close button. ([61760](https://github.com/WordPress/gutenberg/pull/61760)) +- Update 'Add template' screen to prefer template_name label instead of singular_name. ([60367](https://github.com/WordPress/gutenberg/pull/60367)) +- Update: Move pattern actions to the editor package. [take 2]. ([61612](https://github.com/WordPress/gutenberg/pull/61612)) + +#### Global Styles +- Update copy for color variations from "Presets" to "Palettes". ([62147](https://github.com/WordPress/gutenberg/pull/62147)) + +#### Synced Patterns +- Remove `IS_GUTENBERG_PLUGIN` check to ensure pattern overrides ship in 6.6. ([62011](https://github.com/WordPress/gutenberg/pull/62011)) + +#### npm Packages +- Packages: Increase the minimum required Node.js version to v18.12.0. ([61930](https://github.com/WordPress/gutenberg/pull/61930)) + +#### Layout +- Update child layout selector to match core. ([61777](https://github.com/WordPress/gutenberg/pull/61777)) + +#### Components +- Introduce Combobox `expandOnFocus` property. ([61705](https://github.com/WordPress/gutenberg/pull/61705)) + + +## First time contributors + +The following PRs were merged by first time contributors: + +- @akashdhawade2005: Fix: Block library README.md link. ([62081](https://github.com/WordPress/gutenberg/pull/62081)) +- @amitraj2203: Added Bluesky icon to the Social Icon Block. ([61372](https://github.com/WordPress/gutenberg/pull/61372)) +- @dbrian: Add JSDoc to PostVisibility, PostVisibilityCheck, and PostVisibilityLabel. ([61735](https://github.com/WordPress/gutenberg/pull/61735)) +- @gemkev: Update tutorial.md. ([62054](https://github.com/WordPress/gutenberg/pull/62054)) +- @kellenmace: Fix an issue causing wp-scripts commands to fail if the file path contained a space character. ([61748](https://github.com/WordPress/gutenberg/pull/61748)) +- @narenin: Fixed: Custom HTML Block should display content in LTR layout for all languages. ([62083](https://github.com/WordPress/gutenberg/pull/62083)) +- @nateinaction: Add documentation for the EditorProvider and ExperimentalEditorProvider components. ([61739](https://github.com/WordPress/gutenberg/pull/61739)) +- @paolopiaggio: Playwright end-to-end Utils: Add fullscreenMode option to createNewPost. ([61766](https://github.com/WordPress/gutenberg/pull/61766)) +- @sanjucta: Add docblock to PostTitle and PostTitleRaw component. ([61740](https://github.com/WordPress/gutenberg/pull/61740)) +- @vipul0425: Fix: The latest post block - post titles overlapping. ([61356](https://github.com/WordPress/gutenberg/pull/61356)) + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @abhi3315 @afercia @ajlende @akashdhawade2005 @akasunil @Aljullu @amitraj2203 @andrewserong @anton-vlasenko @anver @artemiomorales @carolinan @cbravobernal @colorful-tones @creativecoder @DaniGuardiola @DAreRodz @dbrian @draganescu @ellatrix @fabiankaegy @fullofcaffeine @gemkev @geriux @glendaviesnz @gziolo @jameskoster @jasmussen @jeryj @jorgefilipecosta @jsnajdr @kellenmace @kevin940726 @kt-12 @madhusudhand @Mamaduka @mattsherman @mcsf @michalczaplinski @mirka @narenin @nateinaction @ndiego @ntsekouras @oandregal @ockham @paolopiaggio @ramonjd @retrofox @richtabor @sanjucta @SantosGuillamot @scruffian @senadir @shail-mehta @sirreal @stokesman @t-hamano @talldan @taylorgorman @tellthemachines @tjcafferkey @twstokes @tyxla @vcanales @vipul0425 @westonruter @WunderBart @youknowriad + + = 18.4.1 = ## Changelog diff --git a/docs/how-to-guides/themes/global-settings-and-styles.md b/docs/how-to-guides/themes/global-settings-and-styles.md index 69f0606c936490..a5c3e828a2d661 100644 --- a/docs/how-to-guides/themes/global-settings-and-styles.md +++ b/docs/how-to-guides/themes/global-settings-and-styles.md @@ -336,16 +336,7 @@ The following presets can be defined via `theme.json`: - `color.palette`: - generates 3 classes per preset value: color, background-color, and border-color. - generates a single custom property per preset value. -- `spacing.spacingScale`: used to generate an array of spacing preset sizes for use with padding, margin, and gap settings. - - `operator`: specifies how to calculate the steps with either `*` for multiplier, or `+` for sum. - - `increment`: the amount to increment each step by. Core by default uses a 'perfect 5th' multiplier of `1.5`. - - `steps`: the number of steps to generate in the spacing scale. The default is 7. To prevent the generation of the spacing presets, and to disable the related UI, this can be set to `0`. - - `mediumStep`: the steps in the scale are generated descending and ascending from a medium step, so this should be the size value of the medium space, without the unit. The default medium step is `1.5rem` so the mediumStep value is `1.5`. - - `unit`: the unit the scale uses, eg. `px, rem, em, %`. The default is `rem`. -- `spacing.spacingSizes`: themes can choose to include a static `spacing.spacingSizes` array of spacing preset sizes if they have a sequence of sizes that can't be generated via an increment or multiplier. - - `name`: a human readable name for the size, eg. `Small, Medium, Large`. - - `slug`: the machine readable name. In order to provide the best cross site/theme compatibility the slugs should be in the format, "10","20","30","40","50","60", with "50" representing the `Medium` size value. - - `size`: the size, including the unit, eg. `1.5rem`. It is possible to include fluid values like `clamp(2rem, 10vw, 20rem)`. +- `spacing.spacingSizes`/`spacing.spacingScale`: generates a single custom property per preset value. - `typography.fontSizes`: generates a single class and custom property per preset value. - `typography.fontFamilies`: generates a single custom property per preset value. diff --git a/docs/manifest.json b/docs/manifest.json index a2577530463555..ac269e47846333 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1121,6 +1121,12 @@ "markdown_source": "../packages/components/src/popover/README.md", "parent": "components" }, + { + "title": "ProgressBar", + "slug": "progress-bar", + "markdown_source": "../packages/components/src/progress-bar/README.md", + "parent": "components" + }, { "title": "QueryControls", "slug": "query-controls", diff --git a/docs/reference-guides/block-api/block-variations.md b/docs/reference-guides/block-api/block-variations.md index 0440858810b65a..ffd3cc49adda80 100644 --- a/docs/reference-guides/block-api/block-variations.md +++ b/docs/reference-guides/block-api/block-variations.md @@ -129,9 +129,9 @@ While the `isActive` property is optional, it's recommended. This API is used by If `isActive` is not set, the Editor cannot distinguish between an instance of the original block and your variation, so the original block information will be displayed. -The property can be set to either a function or an array of strings (`string[]`). +The property can be set to either an array of strings (`string[]`), or a function. It is recommended to use the string array version whenever possible. -The function version of this property accepts a block instance's `blockAttributes` as the first argument, and the `variationAttributes` declared for a variation as the second argument. These arguments can be used to determine if a variation is active by comparing them and returning a `true` or `false` (indicating whether this variation is inactive for this block instance). +The `string[]` version is used to declare which of the block instance's attributes should be compared to the given variation's. Each attribute will be checked and the variation will be active if all of them match. As an example, in the core Embed block, the `providerNameSlug` attribute is used to determine the embed provider (e.g. 'youtube' or 'twitter'). The variations may be declared like this: @@ -162,22 +162,32 @@ const variations = [ ] ``` - The `isActive` function can compare the block instance value for `providerNameSlug` to the value declared in the variation's declaration (the values in the code snippet above) to determine which embed variation is active: +The `isActive` property would then look like this: ```js -isActive: ( blockAttributes, variationAttributes ) => - blockAttributes.providerNameSlug === variationAttributes.providerNameSlug, +isActive: [ 'providerNameSlug' ] ``` -The `string[]` version is used to declare which attributes should be compared as a shorthand. Each attribute will be checked and the variation will be active if all of them match. Using the same example for the embed block, the string version would look like this: +This will cause the block instance value for `providerNameSlug` to be compared to the value declared in the variation's declaration (the values in the code snippet above) to determine which embed variation is active. + +Nested object paths are also supported. For example, consider a block variation that has a `query` object as an attribute. It is possible to determine if the variation is active solely based on that object's `postType` property (while ignoring all its other properties): ```js -isActive: [ 'providerNameSlug' ] +isActive: [ 'query.postType' ] +``` + +The function version of this property accepts a block instance's `blockAttributes` as the first argument, and the `variationAttributes` declared for a variation as the second argument. These arguments can be used to determine if a variation is active by comparing them and returning a `true` or `false` (indicating whether this variation is inactive for this block instance). + +Using the same example for the embed block, the function version would look like this: + +```js +isActive: ( blockAttributes, variationAttributes ) => + blockAttributes.providerNameSlug === variationAttributes.providerNameSlug, ``` -### Caveats to using `isActive` +### Specificity of `isActive` matches -The `isActive` property can return false positives if multiple variations exist for a specific block and the `isActive` checks are not specific enough. To demonstrate this, consider the following example: +If there are multiple variations whose `isActive` check matches a given block instance, and all of them are string arrays, then the variation with the highest _specificity_ will be chosen. Consider the following example: ```js wp.blocks.registerBlockVariation( @@ -206,6 +216,6 @@ wp.blocks.registerBlockVariation( ); ``` -The `isActive` check on both variations tests the `textColor`, but each variations uses `vivid-red`. Since the `paragraph-red` variation is registered first, once the `paragraph-red-grey` variation is inserted into the Editor, it will have the title `Red Paragraph` instead of `Red/Grey Paragraph`. As soon as the Editor finds a match, it stops checking. +If a block instance has attributes `textColor: vivid-red` and `backgroundColor: cyan-bluish-gray`, both variations' `isActive` criterion will match that block instance. In this case, the more _specific_ match will be determined to be the active variation, where specificity is calculated as the length of each `isActive` array. This means that the `Red/Grey Paragraph` will be shown as the active variation. -There have been [discussions](https://github.com/WordPress/gutenberg/issues/41303#issuecomment-1526193087) around how the API can be improved, but as of WordPress 6.3, this remains an issue to watch out for. +Note that specificity cannot be determined for a matching variation if its `isActive` property is a function rather than a `string[]`. In this case, the first matching variation will be determined to be the active variation. For this reason, it is generally recommended to use a `string[]` rather than a `function` for the `isActive` property. diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index f752fe8104a568..59a820a16697c9 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -167,6 +167,7 @@ Settings related to spacing. | padding | boolean | false | | | units | array | px,em,rem,vh,vw,% | | | customSpacingSize | boolean | true | | +| defaultSpacingSizes | boolean | true | | | spacingSizes | array | | name, size, slug | | spacingScale | object | | | diff --git a/docs/reference-guides/theme-json-reference/theme-json-migrations.md b/docs/reference-guides/theme-json-reference/theme-json-migrations.md index c304bfe39493ee..8e9d56ed054e68 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-migrations.md +++ b/docs/reference-guides/theme-json-reference/theme-json-migrations.md @@ -88,8 +88,27 @@ The new `defaultFontSizes` option gives control over showing default font sizes It is `true` by default when switching to v3. This is to be consistent with how other `default*` options work such as `settings.color.defaultPalette`, but differs from the behavior in v2. -In theme.json v2, the default font sizes were only shown when theme sizes were not defined. A theme providing font sizes with the same slugs as the defaults would always override the default ones. - To keep behavior similar to v2 with a v3 theme.json: * If you do not have any `fontSizes` defined, `defaultFontSizes` can be left out or set to `true`. * If you have some `fontSizes` defined, set `defaultFontSizes` to `false`. + +#### `settings.spacing.defaultSpacingSizes` + +In theme.json v2, there are two settings that could be used to set theme level spacing sizes: `settings.spacing.spacingSizes` and `settings.spacing.spacingScale`. Setting both `spacingSizes` _and_ `spacingScale` would only use the values from `spacingSizes`. And setting either of them would always replace the entire set of default spacing sizes provided by WordPress. + +The default `spacingSizes` slugs provided by WordPress are: `20`, `30`, `40`, `50`, `60`, `70`, and `80`. + +The new `defaultSpacingSizes` option gives control over showing default spacing sizes and preventing those defaults from being overridden. + +- When set to `true` it will show the default spacing sizes and prevent them from being overridden by the theme. +- When set to `false` it will hide the default spacing sizes and allow the theme to use the default slugs. + +`defaultSpacingSizes` is `true` by default when switching to v3. This is to be consistent with how other `default*` options work such as `settings.color.defaultPalette`, but differs from the behavior in v2. + +Additionally, in v3 both `spacingSizes` and `spacingScale` can be set at the same time. Presets defined in `spacingSizes` with slugs matching the generated presets from `spacingSizes` will override the generated ones. + +To keep behavior similar to v2 with a v3 theme.json: +* If you do not have any `spacingSizes` presets or `spacingScale` config defined, `defaultSpacingSizes` can be left out or set to `true`. +* If you disabled default spacing sizes by setting `spacingScale` to `{ "steps": 0 }`, remove the `spacingScale` config and set `defaultSpacingSizes` to `false`. +* If you defined only one of either `spacingScale` or `spacingSizes` for your presets, set `defaultSpacingSizes` to `false`. +* If you defined both `spacingScale` and `spacingSizes`, remove the `spacingSizes` config _and_ set `defaultSpacingSizes` to `false`. diff --git a/docs/tool/manifest.js b/docs/tool/manifest.js index e830012bc57708..3c1f0fee2090ff 100644 --- a/docs/tool/manifest.js +++ b/docs/tool/manifest.js @@ -16,7 +16,6 @@ const componentPaths = glob( 'packages/components/src/*/**/README.md', { 'packages/components/src/theme/README.md', 'packages/components/src/view/README.md', 'packages/components/src/dropdown-menu-v2/README.md', - 'packages/components/src/progress-bar/README.md', 'packages/components/src/tabs/README.md', 'packages/components/src/custom-select-control-v2/README.md', ], diff --git a/gutenberg.php b/gutenberg.php index becad26a3e9f3a..aadebe998d7ab5 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.4 * Requires PHP: 7.2 - * Version: 18.4.1 + * Version: 18.5.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/block-supports/block-style-variations.php b/lib/block-supports/block-style-variations.php index 6fc89b01c6793d..e09bd4ce90bf0e 100644 --- a/lib/block-supports/block-style-variations.php +++ b/lib/block-supports/block-style-variations.php @@ -7,17 +7,17 @@ */ /** - * Get the class name for this application of this block's variation styles. + * Generate block style variation instance name. * * @since 6.6.0 * * @param array $block Block object. * @param string $variation Slug for the block style variation. * - * @return string The unique class name. + * @return string The unique variation name. */ -function gutenberg_get_block_style_variation_class_name( $block, $variation ) { - return 'is-style-' . $variation . '--' . md5( serialize( $block ) ); +function gutenberg_create_block_style_variation_instance_name( $block, $variation ) { + return $variation . '--' . md5( serialize( $block ) ); } /** @@ -79,31 +79,69 @@ function gutenberg_render_block_style_variation_support_styles( $parsed_block ) return $parsed_block; } + $variation_instance = gutenberg_create_block_style_variation_instance_name( $parsed_block, $variation ); + $class_name = "is-style-$variation_instance"; + $updated_class_name = $parsed_block['attrs']['className'] . " $class_name"; + + /* + * Even though block style variations are effectively theme.json partials, + * they can't be processed completely as though they are. + * + * Block styles support custom selectors to direct specific types of styles + * to inner elements. For example, borders on Image block's get applied to + * the inner `img` element rather than the wrapping `figure`. + * + * The following relocates the "root" block style variation styles to + * under an appropriate blocks property to leverage the preexisting style + * generation for simple block style variations. This way they get the + * custom selectors they need. + * + * The inner elements and block styles for the variation itself are + * still included at the top level but scoped by the variation's selector + * when the stylesheet is generated. + */ + $elements_data = $variation_data['elements'] ?? array(); + $blocks_data = $variation_data['blocks'] ?? array(); + unset( $variation_data['elements'] ); + unset( $variation_data['blocks'] ); + + _wp_array_set( + $blocks_data, + array( $parsed_block['blockName'], 'variations', $variation_instance ), + $variation_data + ); + $config = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, - 'styles' => $variation_data, + 'styles' => array( + 'elements' => $elements_data, + 'blocks' => $blocks_data, + ), ); - $class_name = gutenberg_get_block_style_variation_class_name( $parsed_block, $variation ); - $updated_class_name = $parsed_block['attrs']['className'] . " $class_name"; - - $class_name = ".$class_name"; - + // Turn off filter that excludes block nodes. They are needed here for the variation's inner block types. if ( ! is_admin() ) { remove_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' ); } + // Temporarily prevent variation instance from being sanitized while processing theme.json. + $styles_registry = WP_Block_Styles_Registry::get_instance(); + $styles_registry->register( $parsed_block['blockName'], array( 'name' => $variation_instance ) ); + $variation_theme_json = new WP_Theme_JSON_Gutenberg( $config, 'blocks' ); $variation_styles = $variation_theme_json->get_stylesheet( array( 'styles' ), array( 'custom' ), array( - 'root_selector' => $class_name, 'skip_root_layout_styles' => true, - 'scope' => $class_name, + 'scope' => ".$class_name", ) ); + // Clean up temporary block style now instance styles have been processed. + $styles_registry->unregister( $parsed_block['blockName'], $variation_instance ); + + // Restore filter that excludes block nodes. if ( ! is_admin() ) { add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' ); } @@ -112,7 +150,7 @@ function gutenberg_render_block_style_variation_support_styles( $parsed_block ) return $parsed_block; } - wp_register_style( 'block-style-variation-styles', false, array( 'global-styles' ) ); + wp_register_style( 'block-style-variation-styles', false, array( 'global-styles', 'wp-block-library' ) ); wp_add_inline_style( 'block-style-variation-styles', $variation_styles ); /* @@ -147,7 +185,7 @@ function gutenberg_render_block_style_variation_class_name( $block_content, $blo * Matches a class prefixed by `is-style`, followed by the * variation slug, then `--`, and finally a hash. * - * See `gutenberg_get_block_style_variation_class_name` for class generation. + * See `gutenberg_create_block_style_variation_instance_name` for class generation. */ preg_match( '/\bis-style-(\S+?--\w+)\b/', $block['attrs']['className'], $matches ); @@ -179,7 +217,7 @@ function gutenberg_render_block_style_variation_class_name( $block_content, $blo * * @param array $variations Shared block style variations. * - * @return array Block variations data to be merged under styles.blocks + * @return array Block variations data to be merged under `styles.blocks`. */ function gutenberg_resolve_and_register_block_style_variations( $variations ) { $variations_data = array(); diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 938d8afa9d4faf..ad4e2fe105b0c3 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -123,6 +123,7 @@ class WP_Theme_JSON_Gutenberg { * @since 6.0.0 Replaced `override` with `prevent_override` and updated the * `prevent_override` value for `color.duotone` to use `color.defaultDuotone`. * @since 6.2.0 Added 'shadow' presets. + * @since 6.6.0 Updated the 'prevent_override' value for font size presets to use 'typography.defaultFontSizes' and spacing size presets to use `spacing.defaultSpacingSizes`. * @since 6.6.0 Added `aspectRatios`. * @var array */ @@ -187,7 +188,7 @@ class WP_Theme_JSON_Gutenberg { ), array( 'path' => array( 'spacing', 'spacingSizes' ), - 'prevent_override' => false, + 'prevent_override' => array( 'spacing', 'defaultSpacingSizes' ), 'use_default_names' => true, 'value_key' => 'size', 'css_vars' => '--wp--preset--spacing--$slug', @@ -427,13 +428,14 @@ class WP_Theme_JSON_Gutenberg { 'sticky' => null, ), 'spacing' => array( - 'customSpacingSize' => null, - 'spacingSizes' => null, - 'spacingScale' => null, - 'blockGap' => null, - 'margin' => null, - 'padding' => null, - 'units' => null, + 'customSpacingSize' => null, + 'defaultSpacingSizes' => null, + 'spacingSizes' => null, + 'spacingScale' => null, + 'blockGap' => null, + 'margin' => null, + 'padding' => null, + 'units' => null, ), 'shadow' => array( 'presets' => null, @@ -727,6 +729,8 @@ public static function get_element_class_name( $element ) { * Constructor. * * @since 5.8.0 + * @since 6.6.0 Key spacingScale by origin, and pre-generate the + * spacingSizes from spacingScale. * * @param array $theme_json A structure that follows the theme.json schema. * @param string $origin Optional. What source of data this object represents. @@ -742,8 +746,8 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut $valid_block_names = array_keys( $registry->get_all_registered() ); $valid_element_names = array_keys( static::ELEMENTS ); $valid_variations = static::get_valid_block_style_variations(); - $theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations ); - $this->theme_json = static::maybe_opt_in_into_settings( $theme_json ); + $this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations ); + $this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json ); // Internally, presets are keyed by origin. $nodes = static::get_setting_nodes( $this->theme_json ); @@ -762,6 +766,27 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut } } } + + // In addition to presets, spacingScale (which generates presets) is also keyed by origin. + $scale_path = array( 'settings', 'spacing', 'spacingScale' ); + $spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null ); + if ( null !== $spacing_scale ) { + // If the spacingScale is not already keyed by origin. + if ( empty( array_intersect( array_keys( $spacing_scale ), static::VALID_ORIGINS ) ) ) { + _wp_array_set( $this->theme_json, $scale_path, array( $origin => $spacing_scale ) ); + } + } + + // Pre-generate the spacingSizes from spacingScale. + $scale_path = array( 'settings', 'spacing', 'spacingScale', $origin ); + $spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null ); + if ( isset( $spacing_scale ) ) { + $sizes_path = array( 'settings', 'spacing', 'spacingSizes', $origin ); + $spacing_sizes = _wp_array_get( $this->theme_json, $sizes_path, array() ); + $spacing_scale_sizes = static::compute_spacing_sizes( $spacing_scale ); + $merged_spacing_sizes = static::merge_spacing_sizes( $spacing_scale_sizes, $spacing_sizes ); + _wp_array_set( $this->theme_json, $sizes_path, $merged_spacing_sizes ); + } } /** @@ -2947,6 +2972,8 @@ protected static function get_metadata_boolean( $data, $path, $default_value = f * * @since 5.8.0 * @since 5.9.0 Duotone preset also has origins. + * @since 6.6.0 Use the spacingScale keyed by origin, and re-generate the + * spacingSizes from spacingScale. * * @param WP_Theme_JSON_Gutenberg $incoming Data to merge. */ @@ -2954,6 +2981,40 @@ public function merge( $incoming ) { $incoming_data = $incoming->get_raw_data(); $this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data ); + /* + * Recompute all the spacing sizes based on the new hierarchy of data. In the constructor + * spacingScale and spacingSizes are both keyed by origin and VALID_ORIGINS is ordered, so + * we can allow partial spacingScale data to inherit missing data from earlier layers when + * computing the spacing sizes. + * + * This happens before the presets are merged to ensure that default spacing sizes can be + * removed from the theme origin if $prevent_override is true. + */ + $flattened_spacing_scale = array(); + foreach ( static::VALID_ORIGINS as $origin ) { + $scale_path = array( 'settings', 'spacing', 'spacingScale', $origin ); + + // Apply the base spacing scale to the current layer. + $base_spacing_scale = _wp_array_get( $this->theme_json, $scale_path, array() ); + $flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale ); + + $spacing_scale = _wp_array_get( $incoming_data, $scale_path, null ); + if ( ! isset( $spacing_scale ) ) { + continue; + } + + // Allow partial scale settings by merging with lower layers. + $flattened_spacing_scale = array_replace( $flattened_spacing_scale, $spacing_scale ); + + // Generate and merge the scales for this layer. + $sizes_path = array( 'settings', 'spacing', 'spacingSizes', $origin ); + $spacing_sizes = _wp_array_get( $incoming_data, $sizes_path, array() ); + $spacing_scale_sizes = static::compute_spacing_sizes( $flattened_spacing_scale ); + $merged_spacing_sizes = static::merge_spacing_sizes( $spacing_scale_sizes, $spacing_sizes ); + + _wp_array_set( $incoming_data, $sizes_path, $merged_spacing_sizes ); + } + /* * The array_replace_recursive algorithm merges at the leaf level, * but we don't want leaf arrays to be merged, so we overwrite it. @@ -3733,12 +3794,19 @@ public function get_data() { /** * Sets the spacingSizes array based on the spacingScale values from theme.json. * + * No longer used since theme.json version 3 as the spacingSizes are now + * automatically generated during construction and merge instead of manually + * set in the resolver. + * * @since 6.1.0 + * @deprecated 6.6.0 * * @return null|void */ public function set_spacing_sizes() { - $spacing_scale = $this->theme_json['settings']['spacing']['spacingScale'] ?? array(); + _deprecated_function( __METHOD__, '6.6.0' ); + + $spacing_scale = $this->theme_json['settings']['spacing']['spacingScale']['default'] ?? array(); // Gutenberg didn't have the 1st isset check. if ( ! isset( $spacing_scale['steps'] ) @@ -3762,6 +3830,94 @@ public function set_spacing_sizes() { return null; } + $spacing_sizes = static::compute_spacing_sizes( $spacing_scale ); + + // If there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes. + if ( $spacing_scale['steps'] <= 7 ) { + for ( $spacing_sizes_count = 0; $spacing_sizes_count < count( $spacing_sizes ); $spacing_sizes_count++ ) { + $spacing_sizes[ $spacing_sizes_count ]['name'] = (string) ( $spacing_sizes_count + 1 ); + } + } + + _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes ); + } + + /** + * Merges two sets of spacing size presets. + * + * @since 6.6.0 + * + * @param array $base The base set of spacing sizes. + * @param array $incoming The set of spacing sizes to merge with the base. Duplicate slugs will override the base values. + * @return array The merged set of spacing sizes. + */ + private static function merge_spacing_sizes( $base, $incoming ) { + $merged = array(); + foreach ( $base as $item ) { + $merged[ $item['slug'] ] = $item; + } + foreach ( $incoming as $item ) { + $merged[ $item['slug'] ] = $item; + } + return array_values( $merged ); + } + + /** + * Generates a set of spacing sizes by starting with a medium size and + * applying an operator with an increment value to generate the rest of the + * sizes outward from the medium size. The medium slug is '50' with the rest + * of the slugs being 10 apart. The generated names use t-shirt sizing. + * + * Example: + * + * $spacing_scale = array( + * 'steps' => 4, + * 'mediumStep' => 16, + * 'unit' => 'px', + * 'operator' => '+', + * 'increment' => 2, + * ); + * $spacing_sizes = static::compute_spacing_sizes( $spacing_scale ); + * // -> array( + * // array( 'name' => 'Small', 'slug' => '40', 'size' => '14px' ), + * // array( 'name' => 'Medium', 'slug' => '50', 'size' => '16px' ), + * // array( 'name' => 'Large', 'slug' => '60', 'size' => '18px' ), + * // array( 'name' => 'X-Large', 'slug' => '70', 'size' => '20px' ), + * // ) + * + * @since 6.6.0 + * + * @param array $spacing_scale { + * The spacing scale values. All are required. + * + * @type int $steps The number of steps in the scale. (up to 10 steps are supported.) + * @type float $mediumStep The middle value that gets the slug '50'. (For even number of steps, this becomes the first middle value.) + * @type string $unit The CSS unit to use for the sizes. + * @type string $operator The mathematical operator to apply to generate the other sizes. Either '+' or '*'. + * @type float $increment The value used with the operator to generate the other sizes. + * } + * @return array The spacing sizes presets or an empty array if some spacing scale values are missing or invalid. + */ + private static function compute_spacing_sizes( $spacing_scale ) { + /* + * This condition is intentionally missing some checks on ranges for the values in order to + * keep backwards compatibility with the previous implementation. + */ + if ( + ! isset( $spacing_scale['steps'] ) || + ! is_numeric( $spacing_scale['steps'] ) || + 0 === $spacing_scale['steps'] || + ! isset( $spacing_scale['mediumStep'] ) || + ! is_numeric( $spacing_scale['mediumStep'] ) || + ! isset( $spacing_scale['unit'] ) || + ! isset( $spacing_scale['operator'] ) || + ( '+' !== $spacing_scale['operator'] && '*' !== $spacing_scale['operator'] ) || + ! isset( $spacing_scale['increment'] ) || + ! is_numeric( $spacing_scale['increment'] ) + ) { + return array(); + } + $unit = '%' === $spacing_scale['unit'] ? '%' : sanitize_title( $spacing_scale['unit'] ); $current_step = $spacing_scale['mediumStep']; $steps_mid_point = round( $spacing_scale['steps'] / 2, 0 ); @@ -3844,14 +4000,7 @@ public function set_spacing_sizes() { $spacing_sizes[] = $above_sizes_item; } - // If there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes. - if ( $spacing_scale['steps'] <= 7 ) { - for ( $spacing_sizes_count = 0; $spacing_sizes_count < count( $spacing_sizes ); $spacing_sizes_count++ ) { - $spacing_sizes[ $spacing_sizes_count ]['name'] = (string) ( $spacing_sizes_count + 1 ); - } - } - - _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes ); + return $spacing_sizes; } /** diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index b21fb956ff8ff7..84f999e4e9d020 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -600,7 +600,6 @@ public static function get_merged_data( $origin = 'custom' ) { $result = new WP_Theme_JSON_Gutenberg(); $result->merge( static::get_core_data() ); if ( 'default' === $origin ) { - $result->set_spacing_sizes(); return $result; } @@ -611,12 +610,10 @@ public static function get_merged_data( $origin = 'custom' ) { $result->merge( static::get_theme_data() ); if ( 'theme' === $origin ) { - $result->set_spacing_sizes(); return $result; } $result->merge( static::get_user_data() ); - $result->set_spacing_sizes(); return $result; } diff --git a/lib/class-wp-theme-json-schema-gutenberg.php b/lib/class-wp-theme-json-schema-gutenberg.php index 1eea7ddaa27368..0def88f86a23a7 100644 --- a/lib/class-wp-theme-json-schema-gutenberg.php +++ b/lib/class-wp-theme-json-schema-gutenberg.php @@ -131,7 +131,7 @@ private static function migrate_v2_to_v3( $old ) { * affect the generated CSS. And in v2 we provided default font sizes * when the theme did not provide any. */ - if ( isset( $new['settings']['typography']['fontSizes'] ) ) { + if ( isset( $old['settings']['typography']['fontSizes'] ) ) { if ( ! isset( $new['settings'] ) ) { $new['settings'] = array(); } @@ -141,6 +141,38 @@ private static function migrate_v2_to_v3( $old ) { $new['settings']['typography']['defaultFontSizes'] = false; } + /* + * Similarly to defaultFontSizes, we need to migrate defaultSpacingSizes + * as it controls the PRESETS_METADATA prevent_override which was + * previously hardcoded to false. This only needs to happen when the + * theme provided spacing sizes via spacingSizes or spacingScale. + */ + if ( + isset( $old['settings']['spacing']['spacingSizes'] ) || + isset( $old['settings']['spacing']['spacingScale'] ) + ) { + if ( ! isset( $new['settings'] ) ) { + $new['settings'] = array(); + } + if ( ! isset( $new['settings']['spacing'] ) ) { + $new['settings']['spacing'] = array(); + } + $new['settings']['spacing']['defaultSpacingSizes'] = false; + } + + /* + * In v3 spacingSizes is merged with the generated spacingScale sizes + * instead of completely replacing them. The v3 behavior is what was + * documented for the v2 schema, but the code never actually did work + * that way. Instead of surprising users with a behavior change two + * years after the fact at the same time as a v3 update is introduced, + * we'll continue using the "bugged" behavior for v2 themes. And treat + * the "bug fix" as a breaking change for v3. + */ + if ( isset( $old['settings']['spacing']['spacingSizes'] ) ) { + unset( $new['settings']['spacing']['spacingScale'] ); + } + return $new; } diff --git a/lib/compat/wordpress-6.6/blocks.php b/lib/compat/wordpress-6.6/blocks.php new file mode 100644 index 00000000000000..0d8805a489d9cb --- /dev/null +++ b/lib/compat/wordpress-6.6/blocks.php @@ -0,0 +1,46 @@ + array( 'content' ), + 'core/heading' => array( 'content' ), + 'core/image' => array( 'id', 'url', 'title', 'alt' ), + 'core/button' => array( 'url', 'text', 'linkTarget', 'rel' ), + ); + + $bindings = $parsed_block['attrs']['metadata']['bindings'] ?? array(); + if ( + isset( $bindings['__default']['source'] ) && + 'core/pattern-overrides' === $bindings['__default']['source'] + ) { + $updated_bindings = array(); + + // Build an binding array of all supported attributes. + // Note that this also omits the `__default` attribute from the + // resulting array. + foreach ( $supported_block_attrs[ $parsed_block['blockName'] ] as $attribute_name ) { + // Retain any non-pattern override bindings that might be present. + $updated_bindings[ $attribute_name ] = isset( $bindings[ $attribute_name ] ) + ? $bindings[ $attribute_name ] + : array( 'source' => 'core/pattern-overrides' ); + } + $parsed_block['attrs']['metadata']['bindings'] = $updated_bindings; + } + + return $parsed_block; +} + +add_filter( 'render_block_data', 'gutenberg_replace_pattern_override_default_binding', 10, 1 ); diff --git a/lib/experimental/script-modules.php b/lib/experimental/script-modules.php index e22b757c119501..a9d5540e56dc55 100644 --- a/lib/experimental/script-modules.php +++ b/lib/experimental/script-modules.php @@ -197,3 +197,96 @@ function gutenberg_dequeue_module( $module_identifier ) { _deprecated_function( __FUNCTION__, 'Gutenberg 17.6.0', 'wp_dequeue_script_module' ); wp_script_modules()->dequeue( $module_identifier ); } + + +/** + * Print data associated with Script Modules in Script tags. + * + * This embeds data in the page HTML so that it is available on page load. + * + * Data can be associated with a given Script Module by using the + * `scriptmoduledata_{$module_id}` filter. + * + * The data for a given Script Module will be JSON serialized in a script tag with an ID + * like `wp-scriptmodule-data_{$module_id}`. + */ +function gutenberg_print_script_module_data(): void { + $get_marked_for_enqueue = new ReflectionMethod( 'WP_Script_Modules', 'get_marked_for_enqueue' ); + $get_marked_for_enqueue->setAccessible( true ); + $get_import_map = new ReflectionMethod( 'WP_Script_Modules', 'get_import_map' ); + $get_import_map->setAccessible( true ); + + $modules = array(); + foreach ( array_keys( $get_marked_for_enqueue->invoke( wp_script_modules() ) ) as $id ) { + $modules[ $id ] = true; + } + foreach ( array_keys( $get_import_map->invoke( wp_script_modules() )['imports'] ) as $id ) { + $modules[ $id ] = true; + } + + foreach ( array_keys( $modules ) as $module_id ) { + /** + * Filters data associated with a given Script Module. + * + * Script Modules may require data that is required for initialization or is essential to + * have immediately available on page load. These are suitable use cases for this data. + * + * This is best suited to a minimal set of data and is not intended to replace the REST API. + * + * If the filter returns no data (an empty array), nothing will be embedded in the page. + * + * The data for a given Script Module, if provided, will be JSON serialized in a script tag + * with an ID like `wp-scriptmodule-data_{$module_id}`. + * + * The dynamic portion of the hook name, `$module_id`, refers to the Script Module ID that + * the data is associated with. + * + * @param array $data The data that should be associated with the array. + */ + $data = apply_filters( "scriptmoduledata_{$module_id}", array() ); + + if ( is_array( $data ) && ! empty( $data ) ) { + /* + * This data will be printed as JSON inside a script tag like this: + * + * + * A script tag must be closed by a sequence beginning with ``. It's impossible to + * close a script tag without using `<`. We ensure that `<` is escaped and `/` can + * remain unescaped, so `` will be printed as `\u003C/script\u00E3`. + * + * - JSON_HEX_TAG: All < and > are converted to \u003C and \u003E. + * - JSON_UNESCAPED_SLASHES: Don't escape /. + * + * If the page will use UTF-8 encoding, it's safe to print unescaped unicode: + * + * - JSON_UNESCAPED_UNICODE: Encode multibyte Unicode characters literally (instead of as `\uXXXX`). + * - JSON_UNESCAPED_LINE_TERMINATORS: The line terminators are kept unescaped when + * JSON_UNESCAPED_UNICODE is supplied. It uses the same behaviour as it was + * before PHP 7.1 without this constant. Available as of PHP 7.1.0. + * + * The JSON specification requires encoding in UTF-8, so if the generated HTML page + * is not encoded in UTF-8 then it's not safe to include those literals. They must + * be escaped to avoid encoding issues. + * + * @see https://www.rfc-editor.org/rfc/rfc8259.html for details on encoding requirements. + * @see https://www.php.net/manual/en/json.constants.php for details on these constants. + * @see https://html.spec.whatwg.org/#script-data-state for details on script tag parsing. + */ + $json_encode_flags = JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS; + if ( 'UTF-8' !== get_option( 'blog_charset' ) ) { + $json_encode_flags = JSON_HEX_TAG | JSON_UNESCAPED_SLASHES; + } + + wp_print_inline_script_tag( + wp_json_encode( $data, $json_encode_flags ), + array( + 'type' => 'application/json', + 'id' => "wp-scriptmodule-data_{$module_id}", + ) + ); + } + } +} + +add_action( 'wp_footer', 'gutenberg_print_script_module_data' ); +add_action( 'admin_print_footer_scripts', 'gutenberg_print_script_module_data' ); diff --git a/lib/load.php b/lib/load.php index 6179ade9a2288e..23985f9c8a92e9 100644 --- a/lib/load.php +++ b/lib/load.php @@ -131,6 +131,7 @@ function gutenberg_is_experiment_enabled( $name ) { // WordPress 6.6 compat. require __DIR__ . '/compat/wordpress-6.6/admin-bar.php'; +require __DIR__ . '/compat/wordpress-6.6/blocks.php'; require __DIR__ . '/compat/wordpress-6.6/compat.php'; require __DIR__ . '/compat/wordpress-6.6/resolve-patterns.php'; require __DIR__ . '/compat/wordpress-6.6/block-bindings/pattern-overrides.php'; diff --git a/lib/theme.json b/lib/theme.json index 2f1f723bf75f7f..f638d9722ef67c 100644 --- a/lib/theme.json +++ b/lib/theme.json @@ -265,6 +265,7 @@ "margin": false, "padding": false, "customSpacingSize": true, + "defaultSpacingSizes": true, "units": [ "px", "em", "rem", "vh", "vw", "%" ], "spacingScale": { "operator": "*", diff --git a/package-lock.json b/package-lock.json index ea29fafb92e230..2bc800fe4d81a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gutenberg", - "version": "18.4.1", + "version": "18.5.0-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gutenberg", - "version": "18.4.1", + "version": "18.5.0-rc.1", "hasInstallScript": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -53064,7 +53064,7 @@ }, "packages/a11y": { "name": "@wordpress/a11y", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53078,7 +53078,7 @@ }, "packages/annotations": { "name": "@wordpress/annotations", - "version": "2.58.0", + "version": "3.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53106,7 +53106,7 @@ }, "packages/api-fetch": { "name": "@wordpress/api-fetch", - "version": "6.55.0", + "version": "7.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53120,7 +53120,7 @@ }, "packages/autop": { "name": "@wordpress/autop", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -53132,7 +53132,7 @@ }, "packages/babel-plugin-import-jsx-pragma": { "name": "@wordpress/babel-plugin-import-jsx-pragma", - "version": "4.41.0", + "version": "5.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -53145,7 +53145,7 @@ }, "packages/babel-plugin-makepot": { "name": "@wordpress/babel-plugin-makepot", - "version": "5.42.0", + "version": "6.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -53163,7 +53163,7 @@ }, "packages/babel-preset-default": { "name": "@wordpress/babel-preset-default", - "version": "7.42.0", + "version": "8.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -53186,7 +53186,7 @@ }, "packages/base-styles": { "name": "@wordpress/base-styles", - "version": "4.49.0", + "version": "5.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -53196,7 +53196,7 @@ }, "packages/blob": { "name": "@wordpress/blob", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -53208,7 +53208,7 @@ }, "packages/block-directory": { "name": "@wordpress/block-directory", - "version": "4.35.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53244,7 +53244,7 @@ }, "packages/block-editor": { "name": "@wordpress/block-editor", - "version": "12.26.0", + "version": "13.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53358,7 +53358,7 @@ }, "packages/block-library": { "name": "@wordpress/block-library", - "version": "8.35.0", + "version": "9.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53424,7 +53424,7 @@ }, "packages/block-serialization-default-parser": { "name": "@wordpress/block-serialization-default-parser", - "version": "4.58.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -53436,7 +53436,7 @@ }, "packages/block-serialization-spec-parser": { "name": "@wordpress/block-serialization-spec-parser", - "version": "4.58.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "pegjs": "^0.10.0", @@ -53449,14 +53449,13 @@ }, "packages/blocks": { "name": "@wordpress/blocks", - "version": "12.35.0", + "version": "13.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", "@wordpress/autop": "file:../autop", "@wordpress/blob": "file:../blob", "@wordpress/block-serialization-default-parser": "file:../block-serialization-default-parser", - "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", @@ -53503,7 +53502,7 @@ }, "packages/browserslist-config": { "name": "@wordpress/browserslist-config", - "version": "5.41.0", + "version": "6.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -53513,7 +53512,7 @@ }, "packages/commands": { "name": "@wordpress/commands", - "version": "0.29.0", + "version": "1.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53538,7 +53537,7 @@ }, "packages/components": { "name": "@wordpress/components", - "version": "27.6.0", + "version": "28.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@ariakit/react": "^0.3.12", @@ -53630,7 +53629,7 @@ }, "packages/compose": { "name": "@wordpress/compose", - "version": "6.35.0", + "version": "7.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53667,7 +53666,7 @@ }, "packages/core-commands": { "name": "@wordpress/core-commands", - "version": "0.27.0", + "version": "1.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53694,7 +53693,7 @@ }, "packages/core-data": { "name": "@wordpress/core-data", - "version": "6.35.0", + "version": "7.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53738,7 +53737,7 @@ }, "packages/create-block": { "name": "@wordpress/create-block", - "version": "4.42.0", + "version": "4.43.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -53766,7 +53765,7 @@ }, "packages/create-block-tutorial-template": { "name": "@wordpress/create-block-tutorial-template", - "version": "3.12.0", + "version": "4.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -53776,7 +53775,7 @@ }, "packages/customize-widgets": { "name": "@wordpress/customize-widgets", - "version": "4.35.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53814,7 +53813,7 @@ }, "packages/data": { "name": "@wordpress/data", - "version": "9.28.0", + "version": "10.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53843,7 +53842,7 @@ }, "packages/data-controls": { "name": "@wordpress/data-controls", - "version": "3.27.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53861,7 +53860,7 @@ }, "packages/dataviews": { "name": "@wordpress/dataviews", - "version": "1.2.0", + "version": "2.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@ariakit/react": "^0.3.12", @@ -53921,7 +53920,7 @@ }, "packages/date": { "name": "@wordpress/date", - "version": "4.58.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53936,7 +53935,7 @@ }, "packages/dependency-extraction-webpack-plugin": { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "5.9.0", + "version": "6.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -53952,7 +53951,7 @@ }, "packages/deprecated": { "name": "@wordpress/deprecated", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -53965,7 +53964,7 @@ }, "packages/docgen": { "name": "@wordpress/docgen", - "version": "1.67.0", + "version": "2.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -53987,7 +53986,7 @@ }, "packages/dom": { "name": "@wordpress/dom", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54000,7 +53999,7 @@ }, "packages/dom-ready": { "name": "@wordpress/dom-ready", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -54012,7 +54011,7 @@ }, "packages/e2e-test-utils": { "name": "@wordpress/e2e-test-utils", - "version": "10.29.0", + "version": "11.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54035,7 +54034,7 @@ }, "packages/e2e-test-utils-playwright": { "name": "@wordpress/e2e-test-utils-playwright", - "version": "0.26.0", + "version": "1.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54059,7 +54058,7 @@ }, "packages/e2e-tests": { "name": "@wordpress/e2e-tests", - "version": "7.29.0", + "version": "8.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54099,7 +54098,7 @@ }, "packages/edit-post": { "name": "@wordpress/edit-post", - "version": "7.35.0", + "version": "8.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54145,7 +54144,7 @@ }, "packages/edit-site": { "name": "@wordpress/edit-site", - "version": "5.35.0", + "version": "6.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54207,7 +54206,7 @@ }, "packages/edit-widgets": { "name": "@wordpress/edit-widgets", - "version": "5.35.0", + "version": "6.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54250,7 +54249,7 @@ }, "packages/editor": { "name": "@wordpress/editor", - "version": "13.35.0", + "version": "14.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54292,6 +54291,7 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "deepmerge": "^4.3.0", + "fast-deep-equal": "^3.1.3", "is-plain-object": "^5.0.0", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0", @@ -54308,7 +54308,7 @@ }, "packages/element": { "name": "@wordpress/element", - "version": "5.35.0", + "version": "6.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54327,7 +54327,7 @@ }, "packages/env": { "name": "@wordpress/env", - "version": "9.10.0", + "version": "10.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54468,7 +54468,7 @@ }, "packages/escape-html": { "name": "@wordpress/escape-html", - "version": "2.58.0", + "version": "3.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -54480,7 +54480,7 @@ }, "packages/eslint-plugin": { "name": "@wordpress/eslint-plugin", - "version": "18.1.0", + "version": "19.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54523,7 +54523,7 @@ }, "packages/format-library": { "name": "@wordpress/format-library", - "version": "4.35.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54551,7 +54551,7 @@ }, "packages/hooks": { "name": "@wordpress/hooks", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -54563,7 +54563,7 @@ }, "packages/html-entities": { "name": "@wordpress/html-entities", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -54575,7 +54575,7 @@ }, "packages/i18n": { "name": "@wordpress/i18n", - "version": "4.58.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54595,7 +54595,7 @@ }, "packages/icons": { "name": "@wordpress/icons", - "version": "9.49.0", + "version": "10.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54609,7 +54609,7 @@ }, "packages/interactivity": { "name": "@wordpress/interactivity", - "version": "5.7.0", + "version": "6.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@preact/signals": "^1.2.2", @@ -54623,7 +54623,7 @@ }, "packages/interactivity-router": { "name": "@wordpress/interactivity-router", - "version": "1.8.0", + "version": "2.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@wordpress/interactivity": "file:../interactivity" @@ -54684,7 +54684,7 @@ }, "packages/interface": { "name": "@wordpress/interface", - "version": "5.35.0", + "version": "6.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54713,7 +54713,7 @@ }, "packages/is-shallow-equal": { "name": "@wordpress/is-shallow-equal", - "version": "4.58.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -54725,7 +54725,7 @@ }, "packages/jest-console": { "name": "@wordpress/jest-console", - "version": "7.29.0", + "version": "8.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54742,7 +54742,7 @@ }, "packages/jest-preset-default": { "name": "@wordpress/jest-preset-default", - "version": "11.29.0", + "version": "12.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54760,7 +54760,7 @@ }, "packages/jest-puppeteer-axe": { "name": "@wordpress/jest-puppeteer-axe", - "version": "6.29.0", + "version": "7.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54783,7 +54783,7 @@ }, "packages/keyboard-shortcuts": { "name": "@wordpress/keyboard-shortcuts", - "version": "4.35.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54801,7 +54801,7 @@ }, "packages/keycodes": { "name": "@wordpress/keycodes", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54814,7 +54814,7 @@ }, "packages/lazy-import": { "name": "@wordpress/lazy-import", - "version": "1.45.0", + "version": "2.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54829,7 +54829,7 @@ }, "packages/list-reusable-blocks": { "name": "@wordpress/list-reusable-blocks", - "version": "4.35.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54852,7 +54852,7 @@ }, "packages/media-utils": { "name": "@wordpress/media-utils", - "version": "4.49.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54868,7 +54868,7 @@ }, "packages/notices": { "name": "@wordpress/notices", - "version": "4.26.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54885,7 +54885,7 @@ }, "packages/npm-package-json-lint-config": { "name": "@wordpress/npm-package-json-lint-config", - "version": "4.43.0", + "version": "5.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -54898,7 +54898,7 @@ }, "packages/nux": { "name": "@wordpress/nux", - "version": "8.20.0", + "version": "9.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54921,7 +54921,7 @@ }, "packages/patterns": { "name": "@wordpress/patterns", - "version": "1.19.0", + "version": "2.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54951,7 +54951,7 @@ }, "packages/plugins": { "name": "@wordpress/plugins", - "version": "6.26.0", + "version": "7.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -54974,7 +54974,7 @@ }, "packages/postcss-plugins-preset": { "name": "@wordpress/postcss-plugins-preset", - "version": "4.42.0", + "version": "5.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -54991,7 +54991,7 @@ }, "packages/postcss-themes": { "name": "@wordpress/postcss-themes", - "version": "5.41.0", + "version": "6.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -55004,7 +55004,7 @@ }, "packages/preferences": { "name": "@wordpress/preferences", - "version": "3.35.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55030,7 +55030,7 @@ }, "packages/preferences-persistence": { "name": "@wordpress/preferences-persistence", - "version": "1.50.0", + "version": "2.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55043,7 +55043,7 @@ }, "packages/prettier-config": { "name": "@wordpress/prettier-config", - "version": "3.15.0", + "version": "4.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -55056,7 +55056,7 @@ }, "packages/primitives": { "name": "@wordpress/primitives", - "version": "3.56.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55070,7 +55070,7 @@ }, "packages/priority-queue": { "name": "@wordpress/priority-queue", - "version": "2.58.0", + "version": "3.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55083,7 +55083,7 @@ }, "packages/private-apis": { "name": "@wordpress/private-apis", - "version": "0.40.0", + "version": "1.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -55095,7 +55095,7 @@ }, "packages/project-management-automation": { "name": "@wordpress/project-management-automation", - "version": "1.57.0", + "version": "2.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -55112,7 +55112,7 @@ }, "packages/react-i18n": { "name": "@wordpress/react-i18n", - "version": "3.56.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55268,7 +55268,7 @@ }, "packages/readable-js-assets-webpack-plugin": { "name": "@wordpress/readable-js-assets-webpack-plugin", - "version": "2.41.0", + "version": "3.0.0", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -55281,7 +55281,7 @@ }, "packages/redux-routine": { "name": "@wordpress/redux-routine", - "version": "4.58.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55326,7 +55326,7 @@ }, "packages/reusable-blocks": { "name": "@wordpress/reusable-blocks", - "version": "4.35.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55353,7 +55353,7 @@ }, "packages/rich-text": { "name": "@wordpress/rich-text", - "version": "6.35.0", + "version": "7.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55377,7 +55377,7 @@ }, "packages/router": { "name": "@wordpress/router", - "version": "0.27.0", + "version": "1.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55396,7 +55396,7 @@ }, "packages/scripts": { "name": "@wordpress/scripts", - "version": "27.9.0", + "version": "28.0.0", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -55782,7 +55782,7 @@ }, "packages/server-side-render": { "name": "@wordpress/server-side-render", - "version": "4.35.0", + "version": "5.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55808,7 +55808,7 @@ }, "packages/shortcode": { "name": "@wordpress/shortcode", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55821,7 +55821,7 @@ }, "packages/style-engine": { "name": "@wordpress/style-engine", - "version": "1.41.0", + "version": "2.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55834,7 +55834,7 @@ }, "packages/stylelint-config": { "name": "@wordpress/stylelint-config", - "version": "21.41.0", + "version": "22.0.0", "dev": true, "license": "MIT", "dependencies": { @@ -55851,7 +55851,7 @@ }, "packages/sync": { "name": "@wordpress/sync", - "version": "0.20.0", + "version": "1.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55872,7 +55872,7 @@ }, "packages/token-list": { "name": "@wordpress/token-list", - "version": "2.58.0", + "version": "3.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -55884,7 +55884,7 @@ }, "packages/undo-manager": { "name": "@wordpress/undo-manager", - "version": "0.18.0", + "version": "1.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55897,7 +55897,7 @@ }, "packages/url": { "name": "@wordpress/url", - "version": "3.59.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55910,7 +55910,7 @@ }, "packages/viewport": { "name": "@wordpress/viewport", - "version": "5.35.0", + "version": "6.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55928,7 +55928,7 @@ }, "packages/warning": { "name": "@wordpress/warning", - "version": "2.58.0", + "version": "3.0.0", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -55937,7 +55937,7 @@ }, "packages/widgets": { "name": "@wordpress/widgets", - "version": "3.35.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", @@ -55965,7 +55965,7 @@ }, "packages/wordcount": { "name": "@wordpress/wordcount", - "version": "3.58.0", + "version": "4.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0" @@ -68824,7 +68824,6 @@ "@wordpress/autop": "file:../autop", "@wordpress/blob": "file:../blob", "@wordpress/block-serialization-default-parser": "file:../block-serialization-default-parser", - "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", @@ -69411,6 +69410,7 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "deepmerge": "^4.3.0", + "fast-deep-equal": "^3.1.3", "is-plain-object": "^5.0.0", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0", diff --git a/package.json b/package.json index f218f9ca808205..dc479f86b8f513 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "18.4.1", + "version": "18.5.0-rc.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", diff --git a/packages/a11y/CHANGELOG.md b/packages/a11y/CHANGELOG.md index eec8afdfb64264..6505d2b35579ab 100644 --- a/packages/a11y/CHANGELOG.md +++ b/packages/a11y/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 6417a952a1a4dd..7ab67fe4c16233 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/a11y", - "version": "3.58.0", + "version": "4.0.0", "description": "Accessibility (a11y) utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/annotations/CHANGELOG.md b/packages/annotations/CHANGELOG.md index def463f303f71c..eb85e84d604f5d 100644 --- a/packages/annotations/CHANGELOG.md +++ b/packages/annotations/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/annotations/package.json b/packages/annotations/package.json index f8a217f3c29379..cba00c1591e13a 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "2.58.0", + "version": "3.0.0", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md index 51d540327cbab8..6b2495368ee24f 100644 --- a/packages/api-fetch/CHANGELOG.md +++ b/packages/api-fetch/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index e6f1630514ddd6..d200edd4d81e41 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "6.55.0", + "version": "7.0.0", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/autop/CHANGELOG.md b/packages/autop/CHANGELOG.md index 7b9ffaa3059c3e..a1af20c63ba0b1 100644 --- a/packages/autop/CHANGELOG.md +++ b/packages/autop/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/autop/package.json b/packages/autop/package.json index db6aab5dd017f6..7946a728ea22db 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/autop", - "version": "3.58.0", + "version": "4.0.0", "description": "WordPress's automatic paragraph functions `autop` and `removep`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md index eeb17e2c6aa8b2..53ced4dd01d2bb 100644 --- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md +++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json index 736e003fc524ea..fc7c1294339083 100644 --- a/packages/babel-plugin-import-jsx-pragma/package.json +++ b/packages/babel-plugin-import-jsx-pragma/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-import-jsx-pragma", - "version": "4.41.0", + "version": "5.0.0", "description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-makepot/CHANGELOG.md b/packages/babel-plugin-makepot/CHANGELOG.md index 95f4e724c4fd2f..62a625ec2beeb7 100644 --- a/packages/babel-plugin-makepot/CHANGELOG.md +++ b/packages/babel-plugin-makepot/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index e873958ae19f5b..c6f74686e56af2 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-makepot", - "version": "5.42.0", + "version": "6.0.0", "description": "WordPress Babel internationalization (i18n) plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md index be6a8e7a8ff9b7..1e380b9f5a296d 100644 --- a/packages/babel-preset-default/CHANGELOG.md +++ b/packages/babel-preset-default/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.0.0 (2024-05-31) + ### Breaking Changes - Use React's automatic runtime to transform JSX ([#61692](https://github.com/WordPress/gutenberg/pull/61692)). diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index 8f9c9cf9ffc383..e6d8f163b253fe 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-preset-default", - "version": "7.42.0", + "version": "8.0.0", "description": "Default Babel preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/base-styles/CHANGELOG.md b/packages/base-styles/CHANGELOG.md index f4e197d1839896..51e71a7555b8b9 100644 --- a/packages/base-styles/CHANGELOG.md +++ b/packages/base-styles/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index c58976f0964e59..691fff09fb3b5e 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -125,7 +125,7 @@ $z-layers: ( // Should be above the popover (dropdown) ".reusable-blocks-menu-items__convert-modal": 1000001, ".patterns-menu-items__convert-modal": 1000001, - ".edit-site-create-template-part-modal": 1000001, + ".editor-create-template-part-modal": 1000001, ".block-editor-block-lock-modal": 1000001, ".block-editor-template-part__selection-modal": 1000001, ".block-editor-block-rename-modal": 1000001, diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json index 1aa6ce31907bc3..690cd2ea76551e 100644 --- a/packages/base-styles/package.json +++ b/packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/base-styles", - "version": "4.49.0", + "version": "5.0.0", "description": "Base SCSS utilities and variables for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blob/CHANGELOG.md b/packages/blob/CHANGELOG.md index 99e2fe11fd7493..50b6143dfb3e6c 100644 --- a/packages/blob/CHANGELOG.md +++ b/packages/blob/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/blob/package.json b/packages/blob/package.json index 0271affb58d963..a6ea4525b3d376 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blob", - "version": "3.58.0", + "version": "4.0.0", "description": "Blob utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-directory/CHANGELOG.md b/packages/block-directory/CHANGELOG.md index 7435a67f225916..eca5759aba1f1e 100644 --- a/packages/block-directory/CHANGELOG.md +++ b/packages/block-directory/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 46bc869c3c55e6..adefc0e827edad 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "4.35.0", + "version": "5.0.0", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 27d024a5e561f5..7127bddacb8289 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 13.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index cad39359f1c617..bcbe777cedaa9b 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-editor", - "version": "12.26.0", + "version": "13.0.0", "description": "Generic block editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-editor/src/components/alignment-control/test/index.js b/packages/block-editor/src/components/alignment-control/test/index.js index de72e92489be31..178ba294127c31 100644 --- a/packages/block-editor/src/components/alignment-control/test/index.js +++ b/packages/block-editor/src/components/alignment-control/test/index.js @@ -133,7 +133,7 @@ describe( 'AlignmentUI', () => { const { container } = render( { setPaused( false ); } } diff --git a/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js b/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js index 4b2d3df725a66b..bc04593f7ed2a6 100644 --- a/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js +++ b/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js @@ -1,19 +1,134 @@ /** * WordPress dependencies */ -import { ToolbarItem, ToolbarGroup, Icon } from '@wordpress/components'; -import { connection } from '@wordpress/icons'; -import { _x } from '@wordpress/i18n'; +import { useId } from '@wordpress/element'; +import { __, sprintf, _x } from '@wordpress/i18n'; +import { + DropdownMenu, + ToolbarGroup, + ToolbarItem, + __experimentalText as Text, +} from '@wordpress/components'; +import { store as blocksStore } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; +import { copy } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; +import BlockIcon from '../block-icon'; +import useBlockDisplayTitle from '../block-title/use-block-display-title'; + +export default function BlockBindingsToolbarIndicator( { clientIds } ) { + const isSingleBlockSelected = clientIds.length === 1; + const { icon, firstBlockName, isConnectedToPatternOverrides } = useSelect( + ( select ) => { + const { + getBlockAttributes, + getBlockNamesByClientId, + getBlocksByClientId, + } = select( blockEditorStore ); + const { getBlockType, getActiveBlockVariation } = + select( blocksStore ); + const blockTypeNames = getBlockNamesByClientId( clientIds ); + const _firstBlockTypeName = blockTypeNames[ 0 ]; + const firstBlockType = getBlockType( _firstBlockTypeName ); + let _icon; + if ( isSingleBlockSelected ) { + const match = getActiveBlockVariation( + _firstBlockTypeName, + getBlockAttributes( clientIds[ 0 ] ) + ); + // Take into account active block variations. + _icon = match?.icon || firstBlockType.icon; + } else { + const isSelectionOfSameType = + new Set( blockTypeNames ).size === 1; + // When selection consists of blocks of multiple types, display an + // appropriate icon to communicate the non-uniformity. + _icon = isSelectionOfSameType ? firstBlockType.icon : copy; + } + + return { + icon: _icon, + firstBlockName: getBlockAttributes( clientIds[ 0 ] ).metadata + .name, + isConnectedToPatternOverrides: getBlocksByClientId( + clientIds + ).some( ( block ) => + Object.values( block?.attributes.metadata?.bindings ).some( + ( binding ) => + binding.source === 'core/pattern-overrides' + ) + ), + }; + }, + [ clientIds, isSingleBlockSelected ] + ); + const firstBlockTitle = useBlockDisplayTitle( { + clientId: clientIds[ 0 ], + maximumLength: 35, + } ); + + let blockDescription = isSingleBlockSelected + ? _x( + 'This block is connected.', + 'block toolbar button label and description' + ) + : _x( + 'These blocks are connected.', + 'block toolbar button label and description' + ); + if ( isConnectedToPatternOverrides && firstBlockName ) { + blockDescription = isSingleBlockSelected + ? sprintf( + /* translators: %1s: The block type's name; %2s: The block's user-provided name (the same as the override name). */ + __( 'This %1$s is editable using the "%2$s" override.' ), + firstBlockTitle.toLowerCase(), + firstBlockName + ) + : __( 'These blocks are editable using overrides.' ); + } + const descriptionId = useId(); -export default function BlockBindingsToolbarIndicator() { return ( - - + + { ( toggleProps ) => ( + + + > + } + toggleProps={ { + describedBy: blockDescription, + ...toggleProps, + } } + menuProps={ { + orientation: 'both', + 'aria-describedby': descriptionId, + } } + > + { () => ( + + { blockDescription } + + ) } + + ) } ); diff --git a/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss b/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss index 4565473ec95eb4..f37276290ca713 100644 --- a/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss +++ b/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss @@ -1,11 +1,10 @@ -.block-editor-block-bindings-toolbar-indicator { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 6px; - svg { - fill: var(--wp-block-synced-color); - } +.block-editor-block-bindings-toolbar-indicator__popover .components-popover__content { + min-width: 260px; + padding: $grid-unit-20; +} + +.block-editor-block-bindings-toolbar-indicator .block-editor-block-bindings-toolbar-indicator-icon.has-colors svg { + fill: var(--wp-block-synced-color); } .editor-collapsible-block-toolbar .block-editor-block-bindings-toolbar-indicator { diff --git a/packages/block-editor/src/components/block-card/index.js b/packages/block-editor/src/components/block-card/index.js index 3122fb5cf0373d..8196f7688d4b50 100644 --- a/packages/block-editor/src/components/block-card/index.js +++ b/packages/block-editor/src/components/block-card/index.js @@ -7,7 +7,11 @@ import clsx from 'clsx'; * WordPress dependencies */ import deprecated from '@wordpress/deprecated'; -import { Button } from '@wordpress/components'; +import { + Button, + __experimentalText as Text, + __experimentalVStack as VStack, +} from '@wordpress/components'; import { chevronLeft, chevronRight } from '@wordpress/icons'; import { __, isRTL } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; @@ -60,14 +64,14 @@ function BlockCard( { title, icon, description, blockType, className } ) { /> ) } - + { title } { description && ( - + { description } - + ) } - + ); } diff --git a/packages/block-editor/src/components/block-card/style.scss b/packages/block-editor/src/components/block-card/style.scss index 9cd45c7e982965..42cf77aa4b0a84 100644 --- a/packages/block-editor/src/components/block-card/style.scss +++ b/packages/block-editor/src/components/block-card/style.scss @@ -5,10 +5,6 @@ padding: $grid-unit-20; } -.block-editor-block-card__content { - flex-grow: 1; -} - .block-editor-block-card__title { font-weight: 500; @@ -20,13 +16,6 @@ } } -.block-editor-block-card__description { - display: block; - font-size: $default-font-size; - line-height: $default-line-height; - margin-top: $grid-unit-05; -} - .block-editor-block-card .block-editor-block-icon { flex: 0 0 $button-size-small; margin-left: 0; diff --git a/packages/block-editor/src/components/block-edit/index.js b/packages/block-editor/src/components/block-edit/index.js index 57df36c7c74a0b..0c29c0e98b1bfd 100644 --- a/packages/block-editor/src/components/block-edit/index.js +++ b/packages/block-editor/src/components/block-edit/index.js @@ -1,9 +1,9 @@ /** * WordPress dependencies */ -import { useMemo } from '@wordpress/element'; - +import { useMemo, useContext } from '@wordpress/element'; import { hasBlockSupport } from '@wordpress/blocks'; + /** * Internal dependencies */ @@ -17,6 +17,8 @@ import { blockBindingsKey, isPreviewModeKey, } from './context'; +import { MultipleUsageWarning } from './multiple-usage-warning'; +import { PrivateBlockContext } from '../block-list/private-block-context'; /** * The `useBlockEditContext` hook provides information about the block this hook is being used in. @@ -49,6 +51,8 @@ export default function BlockEdit( { const layoutSupport = hasBlockSupport( name, 'layout', false ) || hasBlockSupport( name, '__experimentalLayout', false ); + const { originalBlockClientId } = useContext( PrivateBlockContext ); + return ( + { originalBlockClientId && ( + + ) } ); } diff --git a/packages/block-editor/src/components/block-edit/multiple-usage-warning.js b/packages/block-editor/src/components/block-edit/multiple-usage-warning.js new file mode 100644 index 00000000000000..4acd4d1f349dd0 --- /dev/null +++ b/packages/block-editor/src/components/block-edit/multiple-usage-warning.js @@ -0,0 +1,46 @@ +/** + * WordPress dependencies + */ +import { getBlockType } from '@wordpress/blocks'; +import { Button } from '@wordpress/components'; +import { useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; +import Warning from '../warning'; + +export function MultipleUsageWarning( { + originalBlockClientId, + name, + onReplace, +} ) { + const { selectBlock } = useDispatch( blockEditorStore ); + const blockType = getBlockType( name ); + + return ( + selectBlock( originalBlockClientId ) } + > + { __( 'Find original' ) } + , + onReplace( [] ) } + > + { __( 'Remove' ) } + , + ] } + > + { blockType?.title }: + { __( 'This block can only be used once.' ) } + + ); +} diff --git a/packages/block-editor/src/components/block-list/block-invalid-warning.native.js b/packages/block-editor/src/components/block-list/block-invalid-warning.native.js index d27b2e436f2961..8734f6f8804f03 100644 --- a/packages/block-editor/src/components/block-list/block-invalid-warning.native.js +++ b/packages/block-editor/src/components/block-list/block-invalid-warning.native.js @@ -54,7 +54,7 @@ export default function BlockInvalidWarning( { clientId } ) { { __unstableMarkLastChangeAsPersistent, moveBlocksToPosition, removeBlock, + selectBlock, } = dispatch( blockEditorStore ); // Do not add new properties here, use `useDispatch` instead to avoid @@ -305,6 +307,28 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { canInsertBlockType, } = registry.select( blockEditorStore ); + function switchToDefaultOrRemove() { + const block = getBlock( clientId ); + const defaultBlockName = getDefaultBlockName(); + if ( getBlockName( clientId ) !== defaultBlockName ) { + const replacement = switchToBlockType( + block, + defaultBlockName + ); + if ( replacement && replacement.length ) { + replaceBlocks( clientId, replacement ); + } + } else if ( isUnmodifiedDefaultBlock( block ) ) { + const nextBlockClientId = getNextBlockClientId( clientId ); + if ( nextBlockClientId ) { + registry.batch( () => { + removeBlock( clientId ); + selectBlock( nextBlockClientId ); + } ); + } + } + } + /** * Moves the block with clientId up one level. If the block type * cannot be inserted at the new location, it will be attempted to @@ -344,7 +368,16 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { getDefaultBlockName() ); - if ( replacement && replacement.length ) { + if ( + replacement && + replacement.length && + replacement.every( ( block ) => + canInsertBlockType( + block.name, + targetRootClientId + ) + ) + ) { insertBlocks( replacement, getBlockIndex( _clientId ), @@ -352,6 +385,8 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { changeSelection ); removeBlock( firstClientId, false ); + } else { + switchToDefaultOrRemove(); } } @@ -462,16 +497,8 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { } moveFirstItemUp( rootClientId ); - } else if ( - getBlockName( clientId ) !== getDefaultBlockName() - ) { - const replacement = switchToBlockType( - getBlock( clientId ), - getDefaultBlockName() - ); - if ( replacement && replacement.length ) { - replaceBlocks( clientId, replacement ); - } + } else { + switchToDefaultOrRemove(); } } }, @@ -534,6 +561,7 @@ function BlockListBlockProvider( props ) { isFirstMultiSelectedBlock, getMultiSelectedBlockClientIds, hasSelectedInnerBlock, + getBlocksByName, getBlockIndex, isBlockMultiSelected, @@ -607,6 +635,17 @@ function BlockListBlockProvider( props ) { const movingClientId = hasBlockMovingClientId(); const blockEditingMode = getBlockEditingMode( clientId ); + const multiple = hasBlockSupport( blockName, 'multiple', true ); + + // For block types with `multiple` support, there is no "original + // block" to be found in the content, as the block itself is valid. + const blocksWithSameName = multiple + ? [] + : getBlocksByName( blockName ); + const isInvalid = + blocksWithSameName.length && + blocksWithSameName[ 0 ] !== clientId; + return { ...previewContext, mode: getBlockMode( clientId ), @@ -664,6 +703,9 @@ function BlockListBlockProvider( props ) { hasEditableOutline: blockEditingMode !== 'disabled' && getBlockEditingMode( rootClientId ) === 'disabled', + originalBlockClientId: isInvalid + ? blocksWithSameName[ 0 ] + : false, }; }, [ clientId, rootClientId ] @@ -707,6 +749,7 @@ function BlockListBlockProvider( props ) { hasEditableOutline, className, defaultClassName, + originalBlockClientId, } = selectedProps; // Users of the editor.BlockListBlock filter used to be able to @@ -754,6 +797,7 @@ function BlockListBlockProvider( props ) { defaultClassName, mayDisplayControls, mayDisplayParentControls, + originalBlockClientId, themeSupportsLayout, }; diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index 03456c9fb77aed..f76021b50fc4b2 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -669,8 +669,16 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { } moveFirstItemUp( rootClientId ); - } else { - removeBlock( clientId ); + } else if ( + getBlockName( clientId ) !== getDefaultBlockName() + ) { + const replacement = switchToBlockType( + getBlock( clientId ), + getDefaultBlockName() + ); + if ( replacement && replacement.length ) { + replaceBlocks( clientId, replacement ); + } } } }, diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index 845bfc4f803a35..b2af3456be7b0c 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -147,7 +147,10 @@ function BlockPattern( { /> { showTitle && ( - + { pattern.type === INSERTER_PATTERN_TYPES.user && ! pattern.syncStatus && ( diff --git a/packages/block-editor/src/components/block-patterns-list/style.scss b/packages/block-editor/src/components/block-patterns-list/style.scss index 00dc650a3fbbdc..6b23c1e844dad9 100644 --- a/packages/block-editor/src/components/block-patterns-list/style.scss +++ b/packages/block-editor/src/components/block-patterns-list/style.scss @@ -27,8 +27,9 @@ scroll-margin-bottom: ($grid-unit-40 + $grid-unit-30); .block-editor-block-patterns-list__item-title { - text-align: left; flex-grow: 1; + font-size: $helptext-font-size; + text-align: left; } .block-editor-block-preview__container { @@ -59,7 +60,7 @@ .block-editor-patterns__pattern-details:not(:empty) { align-items: center; margin-top: $grid-unit-10; - margin-bottom: $grid-unit-05; // Add more space as there is a visual label on user-created patterns. + padding-bottom: $grid-unit-05; // Add more space for labels on user-created patterns. } .block-editor-patterns__pattern-icon-wrapper { diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index c3570c4a007f16..0e341d32163952 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -168,7 +168,7 @@ export function PrivateBlockToolbar( { isLargeViewport && isDefaultEditingMode && } { isUsingBindings && canBindBlock( blockName ) && ( - + ) } { ( shouldShowVisualToolbar || isMultiToolbar ) && ( isDefaultEditingMode || isSynced ) && ( diff --git a/packages/block-editor/src/components/border-radius-control/all-input-control.js b/packages/block-editor/src/components/border-radius-control/all-input-control.js index 27256f03547f03..14abf3c6c2bc94 100644 --- a/packages/block-editor/src/components/border-radius-control/all-input-control.js +++ b/packages/block-editor/src/components/border-radius-control/all-input-control.js @@ -61,7 +61,7 @@ export default function AllInputControl( { onChange={ handleOnChange } onUnitChange={ handleOnUnitChange } placeholder={ allPlaceholder } - size={ '__unstable-large' } + size="__unstable-large" /> ); } diff --git a/packages/block-editor/src/components/border-radius-control/input-controls.js b/packages/block-editor/src/components/border-radius-control/input-controls.js index c0c5c672d6364f..4529c00b997ac7 100644 --- a/packages/block-editor/src/components/border-radius-control/input-controls.js +++ b/packages/block-editor/src/components/border-radius-control/input-controls.js @@ -80,7 +80,7 @@ export default function BoxInputControls( { onUnitChange={ createHandleOnUnitChange( corner ) } - size={ '__unstable-large' } + size="__unstable-large" /> diff --git a/packages/block-editor/src/components/child-layout-control/index.js b/packages/block-editor/src/components/child-layout-control/index.js index eb2a02e5095d78..dfc4ee69437f67 100644 --- a/packages/block-editor/src/components/child-layout-control/index.js +++ b/packages/block-editor/src/components/child-layout-control/index.js @@ -111,7 +111,7 @@ export default function ChildLayoutControl( { > { selfStretch === 'fixed' && ( { onChange( { selfStretch, @@ -166,7 +166,7 @@ export default function ChildLayoutControl( { panelId={ panelId } > { @@ -181,7 +181,7 @@ export default function ChildLayoutControl( { min={ 1 } /> { @@ -210,7 +210,7 @@ export default function ChildLayoutControl( { > { @@ -234,7 +234,7 @@ export default function ChildLayoutControl( { { diff --git a/packages/block-editor/src/components/colors-gradients/control.js b/packages/block-editor/src/components/colors-gradients/control.js index 88b33379e04de3..d6b34876c8eaad 100644 --- a/packages/block-editor/src/components/colors-gradients/control.js +++ b/packages/block-editor/src/components/colors-gradients/control.js @@ -145,18 +145,14 @@ function ColorGradientControlInner( { { tabPanels.color } { tabPanels.gradient } diff --git a/packages/block-editor/src/components/contrast-checker/test/index.js b/packages/block-editor/src/components/contrast-checker/test/index.js index 26da2ac0ae8c1f..9abd7d7f7e2e6a 100644 --- a/packages/block-editor/src/components/contrast-checker/test/index.js +++ b/packages/block-editor/src/components/contrast-checker/test/index.js @@ -375,7 +375,7 @@ describe( 'ContrastChecker', () => { render( { render( { test( 'should render nothing when the colors meet AA WCAG guidelines but the background color only has alpha transparency with alpha checker enabled.', () => { const { container } = render( { { render( { test( 'should render component when the colors meet AA WCAG guidelines but all colors have alpha transparency with alpha checker enabled.', () => { render( diff --git a/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js b/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js index e38a01e199b792..d963e71289cb0f 100644 --- a/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js +++ b/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js @@ -92,7 +92,7 @@ export default function AspectRatioTool( { value={ displayValue } options={ options ?? aspectRatioOptions } onChange={ onChange } - size={ '__unstable-large' } + size="__unstable-large" __nextHasNoMarginBottom /> diff --git a/packages/block-editor/src/components/dimensions-tool/scale-tool.js b/packages/block-editor/src/components/dimensions-tool/scale-tool.js index e3ef91745aa53d..05a7d2f4d251a7 100644 --- a/packages/block-editor/src/components/dimensions-tool/scale-tool.js +++ b/packages/block-editor/src/components/dimensions-tool/scale-tool.js @@ -110,7 +110,7 @@ export default function ScaleTool( { help={ scaleHelp[ displayValue ] } value={ displayValue } onChange={ onChange } - size={ '__unstable-large' } + size="__unstable-large" > { options.map( ( option ) => ( > diff --git a/packages/block-editor/src/components/global-styles/background-panel.js b/packages/block-editor/src/components/global-styles/background-panel.js index b7cf7f2bbc493d..7f3c3ed27e667f 100644 --- a/packages/block-editor/src/components/global-styles/background-panel.js +++ b/packages/block-editor/src/components/global-styles/background-panel.js @@ -245,12 +245,22 @@ function BackgroundImageToolsPanelItem( { return; } + const sizeValue = style?.background?.backgroundSize; + const positionValue = style?.background?.backgroundPosition; + onChange( - setImmutably( style, [ 'background', 'backgroundImage' ], { - url: media.url, - id: media.id, - source: 'file', - title: media.title || undefined, + setImmutably( style, [ 'background' ], { + ...style?.background, + backgroundImage: { + url: media.url, + id: media.id, + source: 'file', + title: media.title || undefined, + }, + backgroundPosition: + ! positionValue && ( 'auto' === sizeValue || ! sizeValue ) + ? '50% 0' + : positionValue, } ) ); }; @@ -426,13 +436,16 @@ function BackgroundSizeToolsPanelItem( { const updateBackgroundSize = ( next ) => { // When switching to 'contain' toggle the repeat off. let nextRepeat = repeatValue; + let nextPosition = positionValue; if ( next === 'contain' ) { nextRepeat = 'no-repeat'; + nextPosition = undefined; } if ( next === 'cover' ) { nextRepeat = undefined; + nextPosition = undefined; } if ( @@ -441,6 +454,15 @@ function BackgroundSizeToolsPanelItem( { next === 'auto' ) { nextRepeat = undefined; + /* + * A background image uploaded and set in the editor (an image with a record id), + * receives a default background position of '50% 0', + * when the toggle switches to "Tile". This is to increase the chance that + * the image's focus point is visible. + */ + if ( !! style?.background?.backgroundImage?.id ) { + nextPosition = '50% 0'; + } } /* @@ -454,6 +476,7 @@ function BackgroundSizeToolsPanelItem( { onChange( setImmutably( style, [ 'background' ], { ...style?.background, + backgroundPosition: nextPosition, backgroundRepeat: nextRepeat, backgroundSize: next, } ) @@ -509,7 +532,7 @@ function BackgroundSizeToolsPanelItem( { onChange={ updateBackgroundPosition } /> @@ -542,9 +565,10 @@ function BackgroundSizeToolsPanelItem( { aria-label={ __( 'Background image width' ) } onChange={ updateBackgroundSize } value={ sizeValue } - size={ '__unstable-large' } + size="__unstable-large" __unstableInputWidth="100px" min={ 0 } + placeholder={ __( 'Auto' ) } /> ) : null } { currentValueForToggle !== 'cover' && ( diff --git a/packages/block-editor/src/components/global-styles/border-panel.js b/packages/block-editor/src/components/global-styles/border-panel.js index 964fd1b30100e4..a20bb15c044c51 100644 --- a/packages/block-editor/src/components/global-styles/border-panel.js +++ b/packages/block-editor/src/components/global-styles/border-panel.js @@ -261,7 +261,7 @@ export default function BorderPanel( { popoverPlacement="left-start" value={ border } __experimentalIsRenderedInSidebar - size={ '__unstable-large' } + size="__unstable-large" hideLabelFromVision={ ! hasShadowControl } label={ __( 'Border' ) } /> diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js index 26da27760aa5d0..9718545795f7c8 100644 --- a/packages/block-editor/src/components/global-styles/dimensions-panel.js +++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js @@ -100,14 +100,13 @@ function useHasChildLayout( settings ) { } function useHasSpacingPresets( settings ) { - const { - custom, - theme, - default: defaultPresets, - } = settings?.spacing?.spacingSizes || {}; - const presets = custom ?? theme ?? defaultPresets ?? []; - - return presets.length > 0; + const { defaultSpacingSizes, spacingSizes } = settings?.spacing || {}; + return ( + ( defaultSpacingSizes !== false && + spacingSizes?.default?.length > 0 ) || + spacingSizes?.theme?.length > 0 || + spacingSizes?.custom?.length > 0 + ); } function filterValuesBySides( values, sides ) { diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js index 5c1e87001ca84e..933d97b0c839e0 100644 --- a/packages/block-editor/src/components/global-styles/hooks.js +++ b/packages/block-editor/src/components/global-styles/hooks.js @@ -60,6 +60,7 @@ const VALID_SETTINGS = [ 'position.fixed', 'position.sticky', 'spacing.customSpacingSize', + 'spacing.defaultSpacingSizes', 'spacing.spacingSizes', 'spacing.spacingScale', 'spacing.blockGap', @@ -76,6 +77,7 @@ const VALID_SETTINGS = [ 'typography.fontWeight', 'typography.letterSpacing', 'typography.lineHeight', + 'typography.textAlign', 'typography.textColumns', 'typography.textDecoration', 'typography.textTransform', @@ -377,8 +379,13 @@ export function useSettingsForBlockElement( ? updatedSettings.shadow : false; + // Text alignment is only available for blocks. + if ( element ) { + updatedSettings.typography.textAlign = false; + } + return updatedSettings; - }, [ parentSettings, supportedStyles, supports ] ); + }, [ parentSettings, supportedStyles, supports, element ] ); } export function useColorsPerOrigin( settings ) { diff --git a/packages/block-editor/src/components/global-styles/typography-panel.js b/packages/block-editor/src/components/global-styles/typography-panel.js index 64a7be0443e1e9..3106723945fe86 100644 --- a/packages/block-editor/src/components/global-styles/typography-panel.js +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -17,6 +17,7 @@ import FontFamilyControl from '../font-family'; import FontAppearanceControl from '../font-appearance-control'; import LineHeightControl from '../line-height-control'; import LetterSpacingControl from '../letter-spacing-control'; +import TextAlignmentControl from '../text-alignment-control'; import TextTransformControl from '../text-transform-control'; import TextDecorationControl from '../text-decoration-control'; import WritingModeControl from '../writing-mode-control'; @@ -31,6 +32,7 @@ export function useHasTypographyPanel( settings ) { const hasLineHeight = useHasLineHeightControl( settings ); const hasFontAppearance = useHasAppearanceControl( settings ); const hasLetterSpacing = useHasLetterSpacingControl( settings ); + const hasTextAlign = useHasTextAlignmentControl( settings ); const hasTextTransform = useHasTextTransformControl( settings ); const hasTextDecoration = useHasTextDecorationControl( settings ); const hasWritingMode = useHasWritingModeControl( settings ); @@ -42,6 +44,7 @@ export function useHasTypographyPanel( settings ) { hasLineHeight || hasFontAppearance || hasLetterSpacing || + hasTextAlign || hasTextTransform || hasFontSize || hasTextDecoration || @@ -92,6 +95,10 @@ function useHasTextTransformControl( settings ) { return settings?.typography?.textTransform; } +function useHasTextAlignmentControl( settings ) { + return settings?.typography?.textAlign; +} + function useHasTextDecorationControl( settings ) { return settings?.typography?.textDecoration; } @@ -151,6 +158,7 @@ const DEFAULT_CONTROLS = { fontAppearance: true, lineHeight: true, letterSpacing: true, + textAlign: true, textTransform: true, textDecoration: true, writingMode: true, @@ -339,6 +347,22 @@ export default function TypographyPanel( { const hasWritingMode = () => !! value?.typography?.writingMode; const resetWritingMode = () => setWritingMode( undefined ); + // Text Alignment + const hasTextAlignmentControl = useHasTextAlignmentControl( settings ); + + const textAlign = decodeValue( inheritedValue?.typography?.textAlign ); + const setTextAlign = ( newValue ) => { + onChange( + setImmutably( + value, + [ 'typography', 'textAlign' ], + newValue || undefined + ) + ); + }; + const hasTextAlign = () => !! value?.typography?.textAlign; + const resetTextAlign = () => setTextAlign( undefined ); + const resetAllFilter = useCallback( ( previousValue ) => { return { ...previousValue, @@ -519,6 +543,22 @@ export default function TypographyPanel( { /> ) } + { hasTextAlignmentControl && ( + + + + ) } ); } diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js index 6ce2150b9d2031..28e1ddac9ab1d6 100644 --- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js @@ -916,7 +916,7 @@ export const toStyles = ( .has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); } .has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); } .has-global-padding :where(.has-global-padding:not(.wp-block-block, .alignfull, .alignwide)) { padding-right: 0; padding-left: 0; } - .has-global-padding :where(.has-global-padding:not(.wp-block-block, .alignfull, .alignwide)) > .alignfull { margin-left: 0; margin-right: 0; } + .has-global-padding :where(.has-global-padding:not(.wp-block-block, .alignfull, .alignwide)) > .alignfull { margin-left: 0; margin-right: 0; `; } diff --git a/packages/block-editor/src/components/height-control/index.js b/packages/block-editor/src/components/height-control/index.js index 23738378b69983..71753a67beb021 100644 --- a/packages/block-editor/src/components/height-control/index.js +++ b/packages/block-editor/src/components/height-control/index.js @@ -156,7 +156,7 @@ export default function HeightControl( { onChange={ onChange } onUnitChange={ handleUnitChange } min={ 0 } - size={ '__unstable-large' } + size="__unstable-large" label={ label } hideLabelFromVision /> diff --git a/packages/block-editor/src/components/inner-blocks/warning-max-depth-exceeded.native.js b/packages/block-editor/src/components/inner-blocks/warning-max-depth-exceeded.native.js index e363db4961c7c3..f759b0e519fd60 100644 --- a/packages/block-editor/src/components/inner-blocks/warning-max-depth-exceeded.native.js +++ b/packages/block-editor/src/components/inner-blocks/warning-max-depth-exceeded.native.js @@ -89,7 +89,7 @@ const WarningMaxDepthExceeded = ( { clientId } ) => { setShowDetails( true ) } > diff --git a/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js b/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js index d1021b639a5c57..709e005b587cea 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js +++ b/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js @@ -31,7 +31,7 @@ function PatternsListHeader( { filterValue, filteredBlockPatternsLength } ) { return ( { sprintf( diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js index 9ee57ed7950a83..3703381b23a140 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js @@ -136,7 +136,12 @@ export function PatternCategoryPreviews( { > - + { category.label } diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/patterns-filter.js b/packages/block-editor/src/components/inserter/block-patterns-tab/patterns-filter.js index 7f5abf0b8540ea..9e57f00b6b4661 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/patterns-filter.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/patterns-filter.js @@ -113,7 +113,8 @@ export function PatternsFilter( { popoverProps={ { placement: 'right-end', } } - label="Filter patterns" + label={ __( 'Filter patterns' ) } + toggleProps={ { size: 'compact' } } icon={ } diff --git a/packages/block-editor/src/components/inserter/block-types-tab.js b/packages/block-editor/src/components/inserter/block-types-tab.js index 57f66b6e4bb6ac..42c4a6a35e14bf 100644 --- a/packages/block-editor/src/components/inserter/block-types-tab.js +++ b/packages/block-editor/src/components/inserter/block-types-tab.js @@ -3,7 +3,7 @@ */ import { __, _x } from '@wordpress/i18n'; import { useMemo, useEffect, forwardRef } from '@wordpress/element'; -import { pipe, useAsyncList } from '@wordpress/compose'; +import { useAsyncList } from '@wordpress/compose'; /** * Internal dependencies @@ -27,15 +27,15 @@ const MAX_SUGGESTED_ITEMS = 6; */ const EMPTY_ARRAY = []; -export function BlockTypesTab( - { rootClientId, onInsert, onHover, showMostUsedBlocks }, - ref -) { - const [ items, categories, collections, onSelectItem ] = useBlockTypesState( - rootClientId, - onInsert - ); - +export function BlockTypesTabPanel( { + items, + collections, + categories, + onSelectItem, + onHover, + showMostUsedBlocks, + className, +} ) { const suggestedItems = useMemo( () => { return orderBy( items, 'frecency', 'desc' ).slice( 0, @@ -47,24 +47,6 @@ export function BlockTypesTab( return items.filter( ( item ) => ! item.category ); }, [ items ] ); - const itemsPerCategory = useMemo( () => { - return pipe( - ( itemList ) => - itemList.filter( - ( item ) => item.category && item.category !== 'reusable' - ), - ( itemList ) => - itemList.reduce( ( acc, item ) => { - const { category } = item; - if ( ! acc[ category ] ) { - acc[ category ] = []; - } - acc[ category ].push( item ); - return acc; - }, {} ) - )( items ); - }, [ items ] ); - const itemsPerCollection = useMemo( () => { // Create a new Object to avoid mutating collection. const result = { ...collections }; @@ -101,14 +83,13 @@ export function BlockTypesTab( didRenderAllCategories ? collectionEntries : EMPTY_ARRAY ); - if ( ! items.length ) { - return ; - } - return ( - - - { showMostUsedBlocks && !! suggestedItems.length && ( + + { showMostUsedBlocks && + // Only show the most used blocks if the total amount of block + // is larger than 1 row, otherwise it is not so useful. + items.length > 3 && + !! suggestedItems.length && ( ) } - { currentlyRenderedCategories.map( ( category ) => { - const categoryItems = itemsPerCategory[ category.slug ]; - if ( ! categoryItems || ! categoryItems.length ) { + { currentlyRenderedCategories.map( ( category ) => { + const categoryItems = items.filter( + ( item ) => item.category === category.slug + ); + if ( ! categoryItems || ! categoryItems.length ) { + return null; + } + return ( + + + + ); + } ) } + + { didRenderAllCategories && uncategorizedItems.length > 0 && ( + + + + ) } + + { currentlyRenderedCollections.map( + ( [ namespace, collection ] ) => { + const collectionItems = itemsPerCollection[ namespace ]; + if ( ! collectionItems || ! collectionItems.length ) { return null; } + return ( ); - } ) } + } + ) } + + ); +} - { didRenderAllCategories && uncategorizedItems.length > 0 && ( - - ; + } + + const itemsForCurrentRoot = []; + const itemsRemaining = []; + + for ( const item of items ) { + // Skip reusable blocks, they moved to the patterns tab. + if ( item.category === 'reusable' ) { + continue; + } + + if ( rootClientId && item.rootClientId === rootClientId ) { + itemsForCurrentRoot.push( item ); + } else { + itemsRemaining.push( item ); + } + } + + return ( + + + { !! itemsForCurrentRoot.length && ( + <> + - - ) } - - { currentlyRenderedCollections.map( - ( [ namespace, collection ] ) => { - const collectionItems = itemsPerCollection[ namespace ]; - if ( ! collectionItems || ! collectionItems.length ) { - return null; - } - - return ( - - - - ); - } + + > ) } + ); diff --git a/packages/block-editor/src/components/inserter/category-tabs/index.js b/packages/block-editor/src/components/inserter/category-tabs/index.js index 47c9f0e051a66e..a379f4719556b2 100644 --- a/packages/block-editor/src/components/inserter/category-tabs/index.js +++ b/packages/block-editor/src/components/inserter/category-tabs/index.js @@ -27,7 +27,7 @@ function CategoryTabs( { className="block-editor-inserter__category-tabs" selectOnMove={ false } selectedTabId={ selectedCategory ? selectedCategory.name : null } - orientation={ 'vertical' } + orientation="vertical" onSelect={ ( categoryId ) => { // Pass the full category object onSelectCategory( diff --git a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js index 566d0476fbd0f5..6b9e694c1cdf8f 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js +++ b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js @@ -14,20 +14,24 @@ import { useCallback } from '@wordpress/element'; * Internal dependencies */ import { store as blockEditorStore } from '../../../store'; +import { withRootClientIdOptionKey } from '../../../store/utils'; /** * Retrieves the block types inserter state. * * @param {string=} rootClientId Insertion's root client ID. * @param {Function} onInsert function called when inserter a list of blocks. + * @param {boolean} isQuick * @return {Array} Returns the block types state. (block types, categories, collections, onSelect handler) */ -const useBlockTypesState = ( rootClientId, onInsert ) => { +const useBlockTypesState = ( rootClientId, onInsert, isQuick ) => { const [ items ] = useSelect( ( select ) => [ - select( blockEditorStore ).getInserterItems( rootClientId ), + select( blockEditorStore ).getInserterItems( rootClientId, { + [ withRootClientIdOptionKey ]: ! isQuick, + } ), ], - [ rootClientId ] + [ rootClientId, isQuick ] ); const [ categories, collections ] = useSelect( ( select ) => { @@ -37,7 +41,14 @@ const useBlockTypesState = ( rootClientId, onInsert ) => { const onSelectItem = useCallback( ( - { name, initialAttributes, innerBlocks, syncStatus, content }, + { + name, + initialAttributes, + innerBlocks, + syncStatus, + content, + rootClientId: _rootClientId, + }, shouldFocusBlock ) => { const insertedBlock = @@ -51,7 +62,12 @@ const useBlockTypesState = ( rootClientId, onInsert ) => { createBlocksFromInnerBlocksTemplate( innerBlocks ) ); - onInsert( insertedBlock, undefined, shouldFocusBlock ); + onInsert( + insertedBlock, + undefined, + shouldFocusBlock, + _rootClientId + ); }, [ onInsert ] ); diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index 0dae090578ab4f..24074ec5004565 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useRegistry, useSelect } from '@wordpress/data'; import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; import { _n, sprintf } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; @@ -13,6 +13,34 @@ import { useCallback } from '@wordpress/element'; import { store as blockEditorStore } from '../../../store'; import { unlock } from '../../../lock-unlock'; +function getIndex( { + destinationRootClientId, + destinationIndex, + rootClientId, + registry, +} ) { + if ( rootClientId === destinationRootClientId ) { + return destinationIndex; + } + const parents = [ + '', + ...registry + .select( blockEditorStore ) + .getBlockParents( destinationRootClientId ), + destinationRootClientId, + ]; + const parentIndex = parents.indexOf( rootClientId ); + if ( parentIndex !== -1 ) { + return ( + registry + .select( blockEditorStore ) + .getBlockIndex( parents[ parentIndex + 1 ] ) + 1 + ); + } + return registry.select( blockEditorStore ).getBlockOrder( rootClientId ) + .length; +} + /** * @typedef WPInserterConfig * @@ -42,6 +70,7 @@ function useInsertionPoint( { shouldFocusBlock = true, selectBlockOnInsert = true, } ) { + const registry = useRegistry(); const { getSelectedBlock } = useSelect( blockEditorStore ); const { destinationRootClientId, destinationIndex } = useSelect( ( select ) => { @@ -91,7 +120,7 @@ function useInsertionPoint( { } = unlock( useDispatch( blockEditorStore ) ); const onInsertBlocks = useCallback( - ( blocks, meta, shouldForceFocusBlock = false ) => { + ( blocks, meta, shouldForceFocusBlock = false, _rootClientId ) => { // When we are trying to move focus or select a new block on insert, we also // need to clear the last focus to avoid the focus being set to the wrong block // when tabbing back into the canvas if the block was added from outside the @@ -121,8 +150,17 @@ function useInsertionPoint( { } else { insertBlocks( blocks, - destinationIndex, - destinationRootClientId, + isAppender || _rootClientId === undefined + ? destinationIndex + : getIndex( { + destinationRootClientId, + destinationIndex, + rootClientId: _rootClientId, + registry, + } ), + isAppender || _rootClientId === undefined + ? destinationRootClientId + : _rootClientId, selectBlockOnInsert, shouldFocusBlock || shouldForceFocusBlock ? 0 : null, meta @@ -154,9 +192,17 @@ function useInsertionPoint( { ); const onToggleInsertionPoint = useCallback( - ( show ) => { - if ( show ) { - showInsertionPoint( destinationRootClientId, destinationIndex ); + ( item ) => { + if ( item?.hasOwnProperty( 'rootClientId' ) ) { + showInsertionPoint( + item.rootClientId, + getIndex( { + destinationRootClientId, + destinationIndex, + rootClientId: item.rootClientId, + registry, + } ) + ); } else { hideInsertionPoint(); } diff --git a/packages/block-editor/src/components/inserter/library.js b/packages/block-editor/src/components/inserter/library.js index 6be31f49b5fbe1..4e10a051996a9f 100644 --- a/packages/block-editor/src/components/inserter/library.js +++ b/packages/block-editor/src/components/inserter/library.js @@ -7,7 +7,7 @@ import { forwardRef } from '@wordpress/element'; /** * Internal dependencies */ -import InserterMenu from './menu'; +import { PrivateInserterMenu } from './menu'; import { store as blockEditorStore } from '../../store'; const noop = () => {}; @@ -23,7 +23,7 @@ function InserterLibrary( __experimentalInitialTab, __experimentalInitialCategory, __experimentalFilterValue, - __experimentalOnPatternCategorySelection, + onPatternCategorySelection, onSelect = noop, shouldFocusBlock = false, onClose, @@ -43,7 +43,7 @@ function InserterLibrary( ); return ( - + ); +} + +export default forwardRef( PublicInserterLibrary ); diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index bda1584c22149f..6a4ac798b74900 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -45,7 +45,7 @@ function InserterMenu( showMostUsedBlocks, __experimentalFilterValue = '', shouldFocusBlock = true, - __experimentalOnPatternCategorySelection = NOOP, + onPatternCategorySelection, onClose, __experimentalInitialTab, __experimentalInitialCategory, @@ -81,8 +81,13 @@ function InserterMenu( const blockTypesTabRef = useRef(); const onInsert = useCallback( - ( blocks, meta, shouldForceFocusBlock ) => { - onInsertBlocks( blocks, meta, shouldForceFocusBlock ); + ( blocks, meta, shouldForceFocusBlock, _rootClientId ) => { + onInsertBlocks( + blocks, + meta, + shouldForceFocusBlock, + _rootClientId + ); onSelect(); // Check for focus loss due to filtering blocks by selected block type @@ -111,7 +116,7 @@ function InserterMenu( const onHover = useCallback( ( item ) => { - onToggleInsertionPoint( !! item ); + onToggleInsertionPoint( item ); setHoveredItem( item ); }, [ onToggleInsertionPoint, setHoveredItem ] @@ -128,9 +133,9 @@ function InserterMenu( ( patternCategory, filter ) => { setSelectedPatternCategory( patternCategory ); setPatternFilter( filter ); - __experimentalOnPatternCategorySelection(); + onPatternCategorySelection?.(); }, - [ setSelectedPatternCategory, __experimentalOnPatternCategorySelection ] + [ setSelectedPatternCategory, onPatternCategorySelection ] ); const showPatternPanel = @@ -341,4 +346,16 @@ function InserterMenu( ); } -export default forwardRef( InserterMenu ); +export const PrivateInserterMenu = forwardRef( InserterMenu ); + +function PublicInserterMenu( props, ref ) { + return ( + + ); +} + +export default forwardRef( PublicInserterMenu ); diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js index 3405ac98b881cc..022957df952cea 100644 --- a/packages/block-editor/src/components/inserter/quick-inserter.js +++ b/packages/block-editor/src/components/inserter/quick-inserter.js @@ -44,7 +44,8 @@ export default function QuickInserter( { } ); const [ blockTypes ] = useBlockTypesState( destinationRootClientId, - onInsertBlocks + onInsertBlocks, + true ); const [ patterns ] = usePatternsState( @@ -126,6 +127,7 @@ export default function QuickInserter( { isDraggable={ false } prioritizePatterns={ prioritizePatterns } selectBlockOnInsert={ selectBlockOnInsert } + isQuick /> diff --git a/packages/block-editor/src/components/inserter/search-results.js b/packages/block-editor/src/components/inserter/search-results.js index edd99609ea916c..9c001823745e6c 100644 --- a/packages/block-editor/src/components/inserter/search-results.js +++ b/packages/block-editor/src/components/inserter/search-results.js @@ -50,6 +50,7 @@ function InserterSearchResults( { shouldFocusBlock = true, prioritizePatterns, selectBlockOnInsert, + isQuick, } ) { const debouncedSpeak = useDebounce( speak, 500 ); @@ -80,7 +81,7 @@ function InserterSearchResults( { blockTypeCategories, blockTypeCollections, onSelectBlockType, - ] = useBlockTypesState( destinationRootClientId, onInsertBlocks ); + ] = useBlockTypesState( destinationRootClientId, onInsertBlocks, isQuick ); const [ patterns, , onClickPattern ] = usePatternsState( onInsertBlocks, destinationRootClientId diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss index 7618d9712ad535..4fac1d7ea1f308 100644 --- a/packages/block-editor/src/components/inserter/style.scss +++ b/packages/block-editor/src/components/inserter/style.scss @@ -329,18 +329,20 @@ $block-inserter-tabs-height: 44px; .block-editor-inserter__category-panel { background: $gray-100; - border-left: $border-width solid $gray-200; - border-right: $border-width solid $gray-200; + border-top: $border-width solid $gray-200; + box-shadow: $border-width $border-width 0 0 rgba($color: #000, $alpha: 0.133); // 0.133 = $gray-200 but with alpha. + outline: 1px solid transparent; // Shown for Windows 10 High Contrast mode. position: absolute; - top: 0; + top: -$border-width; left: 0; - height: 100%; + height: calc(100% + #{$border-width}); width: 100%; padding: 0 $grid-unit-20; display: flex; flex-direction: column; @include break-medium { + border-left: $border-width solid $gray-200; padding: 0; left: 100%; width: 300px; @@ -525,8 +527,8 @@ $block-inserter-tabs-height: 44px; } } -.block-editor-inserter__patterns-category-panel-title { - font-size: calc(1.25 * 13px); +.components-heading.block-editor-inserter__patterns-category-panel-title { + font-weight: 500; } .block-editor-inserter__patterns-explore-button, diff --git a/packages/block-editor/src/components/inspector-controls/fill.native.js b/packages/block-editor/src/components/inspector-controls/fill.native.js index 98b6698721e1ce..69da92dd42b378 100644 --- a/packages/block-editor/src/components/inspector-controls/fill.native.js +++ b/packages/block-editor/src/components/inspector-controls/fill.native.js @@ -52,11 +52,9 @@ export default function InspectorControlsFill( { return ( <> - { - - { () => { children } } - - } + + { () => { children } } + { Children.count( children ) > 0 && } > diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 053839c86996de..576556519fb2f4 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -220,7 +220,6 @@ function LinkControl( { // because otherwise using the keyboard to select text // *within* the link format is not possible. if ( isMounting.current ) { - isMounting.current = false; return; } @@ -238,6 +237,16 @@ function LinkControl( { isEndingEditWithFocus.current = false; }, [ isEditingLink, isCreatingPage ] ); + // The component mounting reference is maintained separately + // to correctly reset values in `StrictMode`. + useEffect( () => { + isMounting.current = false; + + return () => { + isMounting.current = true; + }; + }, [] ); + const hasLinkValue = value?.url?.trim()?.length > 0; /** diff --git a/packages/block-editor/src/components/list-view/block-contents.js b/packages/block-editor/src/components/list-view/block-contents.js index f6abaee9258d10..91bfbd7eddaa03 100644 --- a/packages/block-editor/src/components/list-view/block-contents.js +++ b/packages/block-editor/src/components/list-view/block-contents.js @@ -77,7 +77,7 @@ const ListViewBlockContents = forwardRef( { ( { draggable, onDragStart, onDragEnd } ) => ( id ) diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index e597b65d63b865..bbd9cbad73a4a1 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -164,7 +164,7 @@ function MediaPlaceholder( props ) { activeOpacity={ 0.5 } accessibilityLabel={ accessibilityLabel } style={ buttonStyles } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityHint={ accessibilityHint } hitSlop={ hitSlop } onPress={ onButtonPress( open ) } @@ -181,7 +181,7 @@ function MediaPlaceholder( props ) { activeOpacity={ 0.5 } accessibilityLabel={ accessibilityLabel } style={ styles[ 'media-placeholder__appender' ] } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityHint={ accessibilityHint } hitSlop={ hitSlop } onPress={ onButtonPress( open ) } diff --git a/packages/block-editor/src/components/publish-date-time-picker/index.js b/packages/block-editor/src/components/publish-date-time-picker/index.js index 418006cf854c14..eeaa5b2daad6fa 100644 --- a/packages/block-editor/src/components/publish-date-time-picker/index.js +++ b/packages/block-editor/src/components/publish-date-time-picker/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { DateTimePicker } from '@wordpress/components'; +import { DateTimePicker, TimePicker } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { forwardRef } from '@wordpress/element'; import { getSettings } from '@wordpress/date'; @@ -11,29 +11,57 @@ import { getSettings } from '@wordpress/date'; */ import InspectorPopoverHeader from '../inspector-popover-header'; -function PublishDateTimePicker( - { onClose, onChange, ...additionalProps }, +export function PublishDateTimePicker( + { + onClose, + onChange, + showPopoverHeaderActions, + isCompact, + currentDate, + ...additionalProps + }, ref ) { + const datePickerProps = { + startOfWeek: getSettings().l10n.startOfWeek, + onChange, + currentDate: isCompact ? undefined : currentDate, + currentTime: isCompact ? currentDate : undefined, + ...additionalProps, + }; + const DatePickerComponent = isCompact ? TimePicker : DateTimePicker; return ( onChange?.( null ), - }, - ] } + actions={ + showPopoverHeaderActions + ? [ + { + label: __( 'Now' ), + onClick: () => onChange?.( null ), + }, + ] + : undefined + } onClose={ onClose } /> - + ); } -export default forwardRef( PublishDateTimePicker ); +export const PrivatePublishDateTimePicker = forwardRef( PublishDateTimePicker ); + +function PublicPublishDateTimePicker( props, ref ) { + return ( + + ); +} + +export default forwardRef( PublicPublishDateTimePicker ); diff --git a/packages/block-editor/src/components/resolution-tool/index.js b/packages/block-editor/src/components/resolution-tool/index.js index 71c7e508ca3edb..42fea6e8655a8e 100644 --- a/packages/block-editor/src/components/resolution-tool/index.js +++ b/packages/block-editor/src/components/resolution-tool/index.js @@ -49,7 +49,7 @@ export default function ResolutionTool( { options={ options } onChange={ onChange } help={ __( 'Select the size of the source image.' ) } - size={ '__unstable-large' } + size="__unstable-large" /> ); diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 2a0afcf24f0ddc..cc22c9b804130a 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -11,6 +11,7 @@ import { useCallback, forwardRef, createContext, + useContext, } from '@wordpress/element'; import { useDispatch, useRegistry, useSelect } from '@wordpress/data'; import { useMergeRefs, useInstanceId } from '@wordpress/compose'; @@ -39,6 +40,7 @@ import { Content, valueToHTMLString } from './content'; import { withDeprecations } from './with-deprecations'; import { unlock } from '../../lock-unlock'; import { canBindBlock } from '../../hooks/use-bindings-attributes'; +import BlockContext from '../block-context'; export const keyboardShortcutContext = createContext(); export const inputEventContext = createContext(); @@ -121,6 +123,7 @@ export function RichTextWrapper( const context = useBlockEditContext(); const { clientId, isSelected: isBlockSelected, name: blockName } = context; const blockBindings = context[ blockBindingsKey ]; + const blockContext = useContext( BlockContext ); const selector = ( select ) => { // Avoid subscribing to the block editor store if the block is not // selected. @@ -170,7 +173,7 @@ export function RichTextWrapper( const { getBlockBindingsSource } = unlock( select( blocksStore ) ); - for ( const [ attribute, args ] of Object.entries( + for ( const [ attribute, binding ] of Object.entries( blockBindings ) ) { if ( @@ -180,13 +183,16 @@ export function RichTextWrapper( break; } - // If the source is not defined, or if its value of `lockAttributesEditing` is `true`, disable it. + // If the source is not defined, or if its value of `canUserEditValue` is `false`, disable it. const blockBindingsSource = getBlockBindingsSource( - args.source + binding.source ); if ( - ! blockBindingsSource || - blockBindingsSource.lockAttributesEditing() + ! blockBindingsSource?.canUserEditValue( { + select, + context: blockContext, + args: binding.args, + } ) ) { _disableBoundBlocks = true; break; diff --git a/packages/block-editor/src/components/rich-text/native/index.native.js b/packages/block-editor/src/components/rich-text/native/index.native.js index 26d39a0c6058b4..aa06c4dd1e8926 100644 --- a/packages/block-editor/src/components/rich-text/native/index.native.js +++ b/packages/block-editor/src/components/rich-text/native/index.native.js @@ -402,7 +402,8 @@ export class RichText extends Component { this.comesFromAztec = true; this.firedAfterTextChanged = event.nativeEvent.firedAfterTextChanged; const value = this.createRecord(); - const { start, end, text } = value; + const { start, end, text, activeFormats } = value; + const hasActiveFormats = activeFormats && !! activeFormats.length; let newValue; // Always handle full content deletion ourselves. @@ -415,8 +416,8 @@ export class RichText extends Component { // Only process delete if the key press occurs at an uncollapsed edge. if ( - ! onDelete || ! isCollapsed( value ) || + hasActiveFormats || ( isReverse && start !== 0 ) || ( ! isReverse && end !== text.length ) ) { diff --git a/packages/block-editor/src/components/rich-text/native/test/index.native.js b/packages/block-editor/src/components/rich-text/native/test/index.native.js index 8197952a116ddf..26b6d6c5358ec5 100644 --- a/packages/block-editor/src/components/rich-text/native/test/index.native.js +++ b/packages/block-editor/src/components/rich-text/native/test/index.native.js @@ -151,7 +151,7 @@ describe( '', () => { const expectedFontSize = 16; // Act. const { getByLabelText } = render( - + ); // Assert. const actualFontSize = getByLabelText( 'editor' ).props.fontSize; @@ -164,8 +164,8 @@ describe( '', () => { // Act. const { getByLabelText } = render( ); // Assert. @@ -179,7 +179,7 @@ describe( '', () => { // Act. const { getByLabelText } = render( ); @@ -195,7 +195,7 @@ describe( '', () => { mockGlobalSettings( { fontSize: 'min(2em, 3em)' } ); // Act. const { getByLabelText } = render( - + ); // Assert. const actualFontSize = getByLabelText( 'editor' ).props.fontSize; @@ -209,7 +209,7 @@ describe( '', () => { mockGlobalSettings( { fontSize: 'min(2em, 3em)' } ); // Act. const { getByLabelText } = render( - + ); // Assert. const actualFontSize = getByLabelText( 'editor' ).props.fontSize; @@ -224,7 +224,7 @@ describe( '', () => { mockGlobalSettings( { fontSize: unit } ); // Act. const { getByLabelText } = render( - + ); // Assert. const actualFontSize = @@ -241,9 +241,9 @@ describe( '', () => { // Act. const { getByLabelText } = render( ); @@ -260,7 +260,7 @@ describe( '', () => { // Act. const { getByLabelText } = render( @@ -276,7 +276,7 @@ describe( '', () => { Dimensions.set( { window: { ...window, width: 300 } } ); // Act. const { getByLabelText } = render( - + ); // Assert. const actualFontSize = getByLabelText( 'editor' ).props.fontSize; @@ -289,7 +289,7 @@ describe( '', () => { Dimensions.set( { window: { ...window, height: 300 } } ); // Act. const { getByLabelText } = render( - + ); // Assert. const actualFontSize = getByLabelText( 'editor' ).props.fontSize; @@ -335,7 +335,7 @@ describe( '', () => { const style = { lineHeight: 0.2 }; // Act. const { getByLabelText } = render( - + ); // Assert. const actualFontSize = getByLabelText( 'editor' ).props.lineHeight; diff --git a/packages/block-editor/src/components/spacing-sizes-control/hooks/use-spacing-sizes.js b/packages/block-editor/src/components/spacing-sizes-control/hooks/use-spacing-sizes.js index e75f3519a1ca6c..fcd4e3fb964a65 100644 --- a/packages/block-editor/src/components/spacing-sizes-control/hooks/use-spacing-sizes.js +++ b/packages/block-editor/src/components/spacing-sizes-control/hooks/use-spacing-sizes.js @@ -1,28 +1,60 @@ /** * WordPress dependencies */ +import { useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import { useSettings } from '../../use-settings'; +import { RANGE_CONTROL_MAX_SIZE } from '../utils'; + +const EMPTY_ARRAY = []; + +const compare = new Intl.Collator( 'und', { numeric: true } ).compare; export default function useSpacingSizes() { - const spacingSizes = [ { name: 0, slug: '0', size: 0 } ]; - - const [ settingsSizes ] = useSettings( 'spacing.spacingSizes' ); - if ( settingsSizes ) { - spacingSizes.push( ...settingsSizes ); - } - - if ( spacingSizes.length > 8 ) { - spacingSizes.unshift( { - name: __( 'Default' ), - slug: 'default', - size: undefined, - } ); - } - - return spacingSizes; + const [ + customSpacingSizes, + themeSpacingSizes, + defaultSpacingSizes, + defaultSpacingSizesEnabled, + ] = useSettings( + 'spacing.spacingSizes.custom', + 'spacing.spacingSizes.theme', + 'spacing.spacingSizes.default', + 'spacing.defaultSpacingSizes' + ); + + const customSizes = customSpacingSizes ?? EMPTY_ARRAY; + + const themeSizes = themeSpacingSizes ?? EMPTY_ARRAY; + + const defaultSizes = + defaultSpacingSizes && defaultSpacingSizesEnabled !== false + ? defaultSpacingSizes + : EMPTY_ARRAY; + + return useMemo( () => { + const sizes = [ + { name: __( 'None' ), slug: '0', size: 0 }, + ...customSizes, + ...themeSizes, + ...defaultSizes, + ].sort( ( a, b ) => compare( a.slug, b.slug ) ); + + return sizes.length > RANGE_CONTROL_MAX_SIZE + ? [ + { + name: __( 'Default' ), + slug: 'default', + size: undefined, + }, + ...sizes, + ] + : // See https://github.com/WordPress/gutenberg/pull/44247 for reasoning + // to use the index as the name in the range control. + sizes.map( ( { slug, size }, i ) => ( { name: i, slug, size } ) ); + }, [ customSizes, themeSizes, defaultSizes ] ); } diff --git a/packages/block-editor/src/components/spacing-sizes-control/input-controls/spacing-input-control.js b/packages/block-editor/src/components/spacing-sizes-control/input-controls/spacing-input-control.js index 6feb583c29cdb2..58a81d8b130a3e 100644 --- a/packages/block-editor/src/components/spacing-sizes-control/input-controls/spacing-input-control.js +++ b/packages/block-editor/src/components/spacing-sizes-control/input-controls/spacing-input-control.js @@ -23,6 +23,7 @@ import { settings } from '@wordpress/icons'; import { useSettings } from '../../use-settings'; import { store as blockEditorStore } from '../../../store'; import { + RANGE_CONTROL_MAX_SIZE, ALL_SIDES, LABELS, getSliderValueFromPreset, @@ -79,7 +80,7 @@ export default function SpacingInputControl( { value = getPresetValueFromCustomValue( value, spacingSizes ); let selectListSizes = spacingSizes; - const showRangeControl = spacingSizes.length <= 8; + const showRangeControl = spacingSizes.length <= RANGE_CONTROL_MAX_SIZE; const disableCustomSpacingSizes = useSelect( ( select ) => { const editorSettings = select( blockEditorStore ).getSettings(); @@ -230,7 +231,7 @@ export default function SpacingInputControl( { label={ ariaLabel } hideLabelFromVision className="spacing-sizes-control__custom-value-input" - size={ '__unstable-large' } + size="__unstable-large" onDragStart={ () => { if ( value?.charAt( 0 ) === '-' ) { setMinValue( 0 ); @@ -312,7 +313,7 @@ export default function SpacingInputControl( { options={ options } label={ ariaLabel } hideLabelFromVision - size={ '__unstable-large' } + size="__unstable-large" onMouseOver={ onMouseOver } onMouseOut={ onMouseOut } onFocus={ onMouseOver } diff --git a/packages/block-editor/src/components/spacing-sizes-control/utils.js b/packages/block-editor/src/components/spacing-sizes-control/utils.js index 32f0dbc59ac466..91c5a91934f6e3 100644 --- a/packages/block-editor/src/components/spacing-sizes-control/utils.js +++ b/packages/block-editor/src/components/spacing-sizes-control/utils.js @@ -12,6 +12,8 @@ import { sidesVertical, } from '@wordpress/icons'; +export const RANGE_CONTROL_MAX_SIZE = 8; + export const ALL_SIDES = [ 'top', 'right', 'bottom', 'left' ]; export const DEFAULT_VALUES = { diff --git a/packages/block-editor/src/components/video-player/index.native.js b/packages/block-editor/src/components/video-player/index.native.js index a409c1d4ad99fc..3c9de8758579b6 100644 --- a/packages/block-editor/src/components/video-player/index.native.js +++ b/packages/block-editor/src/components/video-player/index.native.js @@ -99,7 +99,7 @@ class Video extends Component { // So we are setting controls=false and adding a play button that // will trigger presentFullscreenPlayer() controls={ false } - ignoreSilentSwitch={ 'ignore' } + ignoreSilentSwitch="ignore" paused={ ! isFullScreen } onLayout={ this.onVideoLayout } onFullscreenPlayerWillPresent={ () => { diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 5e6a9486fcd66d..ab12d5d73f0b6d 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -14,10 +14,11 @@ import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ +import { canBindAttribute } from '../hooks/use-bindings-attributes'; import { unlock } from '../lock-unlock'; import InspectorControls from '../components/inspector-controls'; -export const BlockBindingsPanel = ( { metadata } ) => { +export const BlockBindingsPanel = ( { name, metadata } ) => { const { bindings } = metadata || {}; const { sources } = useSelect( ( select ) => { const _sources = unlock( @@ -33,11 +34,15 @@ export const BlockBindingsPanel = ( { metadata } ) => { return null; } + // Don't show not allowed attributes. // Don't show the bindings connected to pattern overrides in the inspectors panel. // TODO: Explore if this should be abstracted to let other sources decide. const filteredBindings = { ...bindings }; Object.keys( filteredBindings ).forEach( ( key ) => { - if ( filteredBindings[ key ].source === 'core/pattern-overrides' ) { + if ( + ! canBindAttribute( name, key ) || + filteredBindings[ key ].source === 'core/pattern-overrides' + ) { delete filteredBindings[ key ]; } } ); diff --git a/packages/block-editor/src/hooks/position.js b/packages/block-editor/src/hooks/position.js index 3c9cde478cdd04..a5d4a0cb43bf3d 100644 --- a/packages/block-editor/src/hooks/position.js +++ b/packages/block-editor/src/hooks/position.js @@ -303,7 +303,7 @@ export function PositionPanelPure( { onChange={ ( { selectedItem } ) => { onChangeType( selectedItem.value ); } } - size={ '__unstable-large' } + size="__unstable-large" /> diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index dad62bc0594a75..4cd33350224541 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -332,7 +332,15 @@ function BlockStyleControls( { clientId, name, setAttributes, - settings, + settings: { + ...settings, + typography: { + ...settings.typography, + // The text alignment UI for individual blocks is rendered in + // the block toolbar, so disable it here. + textAlign: false, + }, + }, }; if ( blockEditingMode !== 'default' ) { return null; diff --git a/packages/block-editor/src/hooks/use-bindings-attributes.js b/packages/block-editor/src/hooks/use-bindings-attributes.js index b7a4ca0379dd1b..334c751bc01b0b 100644 --- a/packages/block-editor/src/hooks/use-bindings-attributes.js +++ b/packages/block-editor/src/hooks/use-bindings-attributes.js @@ -4,7 +4,7 @@ import { store as blocksStore } from '@wordpress/blocks'; import { createHigherOrderComponent } from '@wordpress/compose'; import { useRegistry, useSelect } from '@wordpress/data'; -import { useCallback } from '@wordpress/element'; +import { useCallback, useMemo } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; /** @@ -29,6 +29,41 @@ const BLOCK_BINDINGS_ALLOWED_BLOCKS = { 'core/button': [ 'url', 'text', 'linkTarget', 'rel' ], }; +const DEFAULT_ATTRIBUTE = '__default'; + +/** + * Returns the bindings with the `__default` binding for pattern overrides + * replaced with the full-set of supported attributes. e.g.: + * + * bindings passed in: `{ __default: { source: 'core/pattern-overrides' } }` + * bindings returned: `{ content: { source: 'core/pattern-overrides' } }` + * + * @param {string} blockName The block name (e.g. 'core/paragraph'). + * @param {Object} bindings A block's bindings from the metadata attribute. + * + * @return {Object} The bindings with default replaced for pattern overrides. + */ +function replacePatternOverrideDefaultBindings( blockName, bindings ) { + // The `__default` binding currently only works for pattern overrides. + if ( + bindings?.[ DEFAULT_ATTRIBUTE ]?.source === 'core/pattern-overrides' + ) { + const supportedAttributes = BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ]; + const bindingsWithDefaults = {}; + for ( const attributeName of supportedAttributes ) { + // If the block has mixed binding sources, retain any non pattern override bindings. + const bindingSource = bindings[ attributeName ] + ? bindings[ attributeName ] + : { source: 'core/pattern-overrides' }; + bindingsWithDefaults[ attributeName ] = bindingSource; + } + + return bindingsWithDefaults; + } + + return bindings; +} + /** * Based on the given block name, * check if it is possible to bind the block. @@ -61,8 +96,15 @@ export const withBlockBindingSupport = createHigherOrderComponent( const sources = useSelect( ( select ) => unlock( select( blocksStore ) ).getAllBlockBindingsSources() ); - const bindings = props.attributes.metadata?.bindings; const { name, clientId, context } = props; + const bindings = useMemo( + () => + replacePatternOverrideDefaultBindings( + name, + props.attributes.metadata?.bindings + ), + [ props.attributes.metadata?.bindings, name ] + ); const boundAttributes = useSelect( () => { if ( ! bindings ) { return; @@ -128,8 +170,8 @@ export const withBlockBindingSupport = createHigherOrderComponent( continue; } - const source = - sources[ bindings[ attributeName ].source ]; + const binding = bindings[ attributeName ]; + const source = sources[ binding?.source ]; if ( ! source?.setValue && ! source?.setValues ) { continue; } @@ -157,12 +199,13 @@ export const withBlockBindingSupport = createHigherOrderComponent( attributeName, value, ] of Object.entries( attributes ) ) { + const binding = bindings[ attributeName ]; source.setValue( { registry, context, clientId, attributeName, - args: bindings[ attributeName ].args, + args: binding.args, value, } ); } diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index c14b6329cf2ec3..4338262300c618 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -240,7 +240,11 @@ export function useBlockSettings( name, parentLayout ) { padding, margin, blockGap, - spacingSizes, + defaultSpacingSizesEnabled, + customSpacingSize, + userSpacingSizes, + defaultSpacingSizes, + themeSpacingSizes, units, aspectRatio, minHeight, @@ -293,7 +297,11 @@ export function useBlockSettings( name, parentLayout ) { 'spacing.padding', 'spacing.margin', 'spacing.blockGap', - 'spacing.spacingSizes', + 'spacing.defaultSpacingSizes', + 'spacing.customSpacingSize', + 'spacing.spacingSizes.custom', + 'spacing.spacingSizes.default', + 'spacing.spacingSizes.theme', 'spacing.units', 'dimensions.aspectRatio', 'dimensions.minHeight', @@ -384,8 +392,12 @@ export function useBlockSettings( name, parentLayout ) { }, spacing: { spacingSizes: { - custom: spacingSizes, + custom: userSpacingSizes, + default: defaultSpacingSizes, + theme: themeSpacingSizes, }, + customSpacingSize, + defaultSpacingSizes: defaultSpacingSizesEnabled, padding, margin, blockGap, @@ -428,7 +440,11 @@ export function useBlockSettings( name, parentLayout ) { padding, margin, blockGap, - spacingSizes, + defaultSpacingSizesEnabled, + customSpacingSize, + userSpacingSizes, + defaultSpacingSizes, + themeSpacingSizes, units, aspectRatio, minHeight, diff --git a/packages/block-editor/src/layouts/flex.js b/packages/block-editor/src/layouts/flex.js index f628a9bf3c3f66..72beca11b3f0c9 100644 --- a/packages/block-editor/src/layouts/flex.js +++ b/packages/block-editor/src/layouts/flex.js @@ -403,12 +403,12 @@ function OrientationControl( { layout, onChange } ) { > diff --git a/packages/block-editor/src/layouts/grid.js b/packages/block-editor/src/layouts/grid.js index e3f956f6dc13d7..4528de117c45b0 100644 --- a/packages/block-editor/src/layouts/grid.js +++ b/packages/block-editor/src/layouts/grid.js @@ -203,7 +203,7 @@ function GridLayoutMinimumWidthControl( { layout, onChange } ) { { onChange( { ...layout, @@ -251,7 +251,7 @@ function GridLayoutColumnsAndRowsControl( { { /** * If the input is cleared, avoid switching @@ -296,7 +296,7 @@ function GridLayoutColumnsAndRowsControl( { { onChange( { ...layout, @@ -372,12 +372,12 @@ function GridLayoutTypeControl( { layout, onChange } ) { isBlock > diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index a064d7a389ecf8..848a3ee49251bd 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -40,6 +40,9 @@ import { import { requiresWrapperOnCopy } from './components/writing-flow/utils'; import { PrivateRichText } from './components/rich-text/'; import { PrivateBlockPopover } from './components/block-popover'; +import { PrivateInserterLibrary } from './components/inserter/library'; +import { PrivatePublishDateTimePicker } from './components/publish-date-time-picker'; +import useSpacingSizes from './components/spacing-sizes-control/hooks/use-spacing-sizes'; /** * Private @wordpress/block-editor APIs. @@ -78,6 +81,9 @@ lock( privateApis, { selectBlockPatternsKey, requiresWrapperOnCopy, PrivateRichText, + PrivateInserterLibrary, reusableBlocksSelectKey, PrivateBlockPopover, + PrivatePublishDateTimePicker, + useSpacingSizes, } ); diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index c9a1430a078fa5..db2c615dd5d6ca 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -18,6 +18,7 @@ import { } from '@wordpress/blocks'; import { speak } from '@wordpress/a11y'; import { __, _n, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; import { create, insert, remove, toHTMLString } from '@wordpress/rich-text'; import deprecated from '@wordpress/deprecated'; @@ -872,6 +873,30 @@ export const __unstableSplitSelection = typeof selectionB.attributeKey === 'string' ? selectionB.attributeKey : findRichTextAttributeKey( blockBType ); + const blockAttributes = select.getBlockAttributes( + selectionA.clientId + ); + const bindings = blockAttributes?.metadata?.bindings; + + // If the attribute is bound, don't split the selection and insert a new block instead. + if ( bindings?.[ attributeKeyA ] ) { + // Show warning if user tries to insert a block into another block with bindings. + if ( blocks.length ) { + const { createWarningNotice } = + registry.dispatch( noticesStore ); + createWarningNotice( + __( + "Blocks can't be inserted into other blocks with bindings" + ), + { + type: 'snackbar', + } + ); + return; + } + dispatch.insertAfterBlock( selectionA.clientId ); + return; + } // Can't split if the selection is not set. if ( @@ -918,9 +943,7 @@ export const __unstableSplitSelection = ); } - const length = select.getBlockAttributes( selectionA.clientId )[ - attributeKeyA - ].length; + const length = blockAttributes[ attributeKeyA ].length; if ( selectionA.offset === 0 && length ) { dispatch.insertBlocks( @@ -1000,7 +1023,10 @@ export const __unstableSplitSelection = const first = firstBlocks.shift(); head = { ...head, - attributes: headType.merge( head.attributes, first.attributes ), + attributes: { + ...head.attributes, + ...headType.merge( head.attributes, first.attributes ), + }, }; output.push( head ); selection = { @@ -1034,10 +1060,10 @@ export const __unstableSplitSelection = const last = lastBlocks.pop(); output.push( { ...tail, - attributes: tailType.merge( - last.attributes, - tail.attributes - ), + attributes: { + ...tail.attributes, + ...tailType.merge( last.attributes, tail.attributes ), + }, } ); output.push( ...lastBlocks ); selection = { diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 1c685eb4230ba4..bf7b5125a770e6 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -22,6 +22,7 @@ import { createSelector, createRegistrySelector } from '@wordpress/data'; * Internal dependencies */ import { + withRootClientIdOptionKey, checkAllowListRecursive, checkAllowList, getAllPatternsDependants, @@ -1995,7 +1996,7 @@ const buildBlockTypeItem = */ export const getInserterItems = createRegistrySelector( ( select ) => createSelector( - ( state, rootClientId = null ) => { + ( state, rootClientId = null, options = {} ) => { const buildReusableBlockInserterItem = ( reusableBlock ) => { const icon = ! reusableBlock.wp_pattern_sync_status ? { @@ -2037,16 +2038,73 @@ export const getInserterItems = createRegistrySelector( ( select ) => buildScope: 'inserter', } ); - const blockTypeInserterItems = getBlockTypes() + let blockTypeInserterItems = getBlockTypes() .filter( ( blockType ) => - canIncludeBlockTypeInInserter( - state, - blockType, - rootClientId - ) + hasBlockSupport( blockType, 'inserter', true ) ) .map( buildBlockTypeInserterItem ); + if ( options[ withRootClientIdOptionKey ] ) { + blockTypeInserterItems = blockTypeInserterItems.reduce( + ( accumulator, item ) => { + item.rootClientId = rootClientId ?? ''; + + while ( + ! canInsertBlockTypeUnmemoized( + state, + item.name, + item.rootClientId + ) + ) { + if ( ! item.rootClientId ) { + let sectionRootClientId; + try { + sectionRootClientId = unlock( + getSettings( state ) + ).sectionRootClientId; + } catch ( e ) {} + if ( + sectionRootClientId && + canInsertBlockTypeUnmemoized( + state, + item.name, + sectionRootClientId + ) + ) { + item.rootClientId = sectionRootClientId; + } else { + delete item.rootClientId; + } + break; + } else { + const parentClientId = getBlockRootClientId( + state, + item.rootClientId + ); + item.rootClientId = parentClientId; + } + } + + // We could also add non insertable items and gray them out. + if ( item.hasOwnProperty( 'rootClientId' ) ) { + accumulator.push( item ); + } + + return accumulator; + }, + [] + ); + } else { + blockTypeInserterItems = blockTypeInserterItems.filter( + ( blockType ) => + canIncludeBlockTypeInInserter( + state, + blockType, + rootClientId + ) + ); + } + const items = blockTypeInserterItems.reduce( ( accumulator, item ) => { const { variations = [] } = item; diff --git a/packages/block-editor/src/store/utils.js b/packages/block-editor/src/store/utils.js index f236c4a7e56eb8..c94453e99c60a4 100644 --- a/packages/block-editor/src/store/utils.js +++ b/packages/block-editor/src/store/utils.js @@ -5,6 +5,8 @@ import { selectBlockPatternsKey } from './private-keys'; import { unlock } from '../lock-unlock'; import { STORE_NAME } from './constants'; +export const withRootClientIdOptionKey = Symbol( 'withRootClientId' ); + export const checkAllowList = ( list, item, defaultResult = null ) => { if ( typeof list === 'boolean' ) { return list; diff --git a/packages/block-editor/src/utils/test/__snapshots__/transform-styles.js.snap b/packages/block-editor/src/utils/test/__snapshots__/transform-styles.js.snap index 35b2adfae17249..9da07667d0a3e0 100644 --- a/packages/block-editor/src/utils/test/__snapshots__/transform-styles.js.snap +++ b/packages/block-editor/src/utils/test/__snapshots__/transform-styles.js.snap @@ -24,7 +24,7 @@ exports[`transformStyles URL rewrite should rewrite relative paths 1`] = ` ] `; -exports[`transformStyles error handling should handle multiple instances of \`:where(body)\` 1`] = ` +exports[`transformStyles error handling should handle multiple instances of \`:root :where(body)\` 1`] = ` [ ".my-namespace { color: pink; } .my-namespace { color: orange; }", ] diff --git a/packages/block-editor/src/utils/test/transform-styles.js b/packages/block-editor/src/utils/test/transform-styles.js index 105c92348113a9..8245ce62831078 100644 --- a/packages/block-editor/src/utils/test/transform-styles.js +++ b/packages/block-editor/src/utils/test/transform-styles.js @@ -52,8 +52,8 @@ describe( 'transformStyles', () => { ); } ); - it( 'should handle multiple instances of `:where(body)`', () => { - const input = `:where(body) { color: pink; } :where(body) { color: orange; }`; + it( 'should handle multiple instances of `:root :where(body)`', () => { + const input = `:root :where(body) { color: pink; } :root :where(body) { color: orange; }`; const output = transformStyles( [ { diff --git a/packages/block-editor/src/utils/transform-styles/index.js b/packages/block-editor/src/utils/transform-styles/index.js index 95eb43e874fd28..9d57de3fa3833c 100644 --- a/packages/block-editor/src/utils/transform-styles/index.js +++ b/packages/block-editor/src/utils/transform-styles/index.js @@ -18,7 +18,9 @@ function transformStyle( if ( ! wrapperSelector && ! baseURL ) { return css; } - const postcssFriendlyCSS = css.replace( /:where\(body\)/g, 'body' ); + const postcssFriendlyCSS = css + .replace( /:root :where\(body\)/g, 'body' ) + .replace( /:where\(body\)/g, 'body' ); try { return postcss( [ diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index efdcdce83503d1..1ef1ad977e9770 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 9.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 68372ad2a98f79..3615ec396d976c 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "8.35.0", + "version": "9.0.0", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index a4f054db46665a..8d4147cb318555 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -40,7 +40,8 @@ import { name as patternBlockName } from './index'; import { unlock } from '../lock-unlock'; const { useLayoutClasses } = unlock( blockEditorPrivateApis ); -const { isOverridableBlock } = unlock( patternsPrivateApis ); +const { isOverridableBlock, hasOverridableBlocks } = + unlock( patternsPrivateApis ); const fullAlignments = [ 'full', 'wide', 'left', 'right' ]; @@ -73,15 +74,6 @@ const useInferredLayout = ( blocks, parentLayout ) => { }, [ blocks, parentLayout ] ); }; -function hasOverridableBlocks( blocks ) { - return blocks.some( ( block ) => { - if ( isOverridableBlock( block ) ) { - return true; - } - return hasOverridableBlocks( block.innerBlocks ); - } ); -} - function setBlockEditMode( setEditMode, blocks, mode ) { blocks.forEach( ( block ) => { const editMode = diff --git a/packages/block-library/src/block/edit.native.js b/packages/block-library/src/block/edit.native.js index ae8c8315aa2e88..6578c12eefc4e0 100644 --- a/packages/block-library/src/block/edit.native.js +++ b/packages/block-library/src/block/edit.native.js @@ -232,7 +232,7 @@ export default function ReusableBlockEdit( { diff --git a/packages/block-library/src/block/index.php b/packages/block-library/src/block/index.php index 9403205c186596..8beef975fad6f3 100644 --- a/packages/block-library/src/block/index.php +++ b/packages/block-library/src/block/index.php @@ -78,7 +78,7 @@ function render_block_core_block( $attributes ) { * filter so that it is available when a pattern's inner blocks are * rendering via do_blocks given it only receives the inner content. */ - $has_pattern_overrides = isset( $attributes['content'] ); + $has_pattern_overrides = isset( $attributes['content'] ) && null !== get_block_bindings_source( 'core/pattern-overrides' ); if ( $has_pattern_overrides ) { $filter_block_context = static function ( $context ) use ( $attributes ) { $context['pattern/overrides'] = $attributes['content']; diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index 155fe797e3147d..e5bd5e6b5f0643 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -156,6 +156,7 @@ function ButtonEdit( props ) { onReplace, mergeBlocks, clientId, + context, } = props; const { tagName, @@ -246,11 +247,14 @@ function ButtonEdit( props ) { return { lockUrlControls: !! metadata?.bindings?.url && - ( ! blockBindingsSource || - blockBindingsSource?.lockAttributesEditing() ), + ! blockBindingsSource?.canUserEditValue( { + select, + context, + args: metadata?.bindings?.url?.args, + } ), }; }, - [ isSelected ] + [ isSelected, metadata?.bindings?.url ] ); return ( @@ -339,7 +343,7 @@ function ButtonEdit( props ) { } } anchor={ popoverAnchor } focusOnMount={ isEditingURL ? 'firstElement' : false } - __unstableSlotName={ '__unstable-block-tools-after' } + __unstableSlotName="__unstable-block-tools-after" shift > @@ -281,7 +281,7 @@ function Controls( { value={ dimRatio } onChange={ onOpacityChange } style={ styles.rangeCellContainer } - separatorType={ 'topFullWidth' } + separatorType="topFullWidth" /> ) : null } diff --git a/packages/block-library/src/cover/edit.native.js b/packages/block-library/src/cover/edit.native.js index 8e3a37a50c7c28..99324545bf798e 100644 --- a/packages/block-library/src/cover/edit.native.js +++ b/packages/block-library/src/cover/edit.native.js @@ -512,7 +512,7 @@ const Cover = ( { muted disableFocus repeat - resizeMode={ 'cover' } + resizeMode="cover" source={ { uri: url } } onLoad={ onVideoLoad } onLoadStart={ onVideoLoadStart } diff --git a/packages/block-library/src/cover/edit/inspector-controls.js b/packages/block-library/src/cover/edit/inspector-controls.js index a978b2ff184a7f..0b05a004cd06e0 100644 --- a/packages/block-library/src/cover/edit/inspector-controls.js +++ b/packages/block-library/src/cover/edit/inspector-controls.js @@ -79,7 +79,7 @@ function CoverHeightInput( { min={ min } onChange={ handleOnChange } onUnitChange={ onUnitChange } - __unstableInputWidth={ '80px' } + __unstableInputWidth="80px" units={ units } value={ computedValue } /> @@ -211,7 +211,14 @@ export default function CoverInspectorControls( { } help={ <> - + { __( 'Describe the purpose of the image.' ) } diff --git a/packages/block-library/src/embed/embed-no-preview.native.js b/packages/block-library/src/embed/embed-no-preview.native.js index cac8c5d1a4c10d..de97ca73a1618a 100644 --- a/packages/block-library/src/embed/embed-no-preview.native.js +++ b/packages/block-library/src/embed/embed-no-preview.native.js @@ -124,7 +124,7 @@ const EmbedNoPreview = ( { const embedNoProviderPreview = ( <> ) } - + { showDownloadButton && ( - + { /* Using RichText here instead of PlainText so that it can be styled like a button. */ } ) } { Platform.isWeb && ! imageSizeOptions && hasImageIds && ( - + { __( 'Resolution' ) } - + { __( 'Loading options…' ) } diff --git a/packages/block-library/src/gallery/v1/gallery-button.native.js b/packages/block-library/src/gallery/v1/gallery-button.native.js index 8804e99cf2e7e6..4dc9abd9753ee4 100644 --- a/packages/block-library/src/gallery/v1/gallery-button.native.js +++ b/packages/block-library/src/gallery/v1/gallery-button.native.js @@ -35,7 +35,7 @@ export function Button( props ) { style={ buttonStyle } activeOpacity={ 0.7 } accessibilityLabel={ accessibilityLabel } - accessibilityRole={ 'button' } + accessibilityRole="button" onPress={ onClick } disabled={ isDisabled } > diff --git a/packages/block-library/src/gallery/v1/gallery-image.native.js b/packages/block-library/src/gallery/v1/gallery-image.native.js index b887ca0bbfe04f..fc07a209d8c6a5 100644 --- a/packages/block-library/src/gallery/v1/gallery-image.native.js +++ b/packages/block-library/src/gallery/v1/gallery-image.native.js @@ -310,7 +310,7 @@ class GalleryImage extends Component { onPress={ this.onMediaPressed } accessible={ ! isSelected } // We need only child views to be accessible after the selection. accessibilityLabel={ this.accessibilityLabelImageContainer() } // if we don't set this explicitly it reads system provided accessibilityLabels of all child components and those include pretty technical words which don't make sense - accessibilityRole={ 'imagebutton' } // this makes VoiceOver to read a description of image provided by system on iOS and lets user know this is a button which conveys the message of tappablity + accessibilityRole="imagebutton" // this makes VoiceOver to read a description of image provided by system on iOS and lets user know this is a button which conveys the message of tappablity > " `; +exports[`Heading block should keep the heading when there is an empty paragraph block before and backspace is pressed at the start 1`] = ` +" +A quick brown fox jumps over the lazy dog. +" +`; + exports[`Heading block should merge with an empty Paragraph block and keep being the Heading block 1`] = ` " A quick brown fox jumps over the lazy dog. @@ -29,3 +35,9 @@ exports[`Heading block should set a text color 1`] = ` A quick brown fox jumps over the lazy dog. " `; + +exports[`Heading block should transform to a paragraph block when pressing backspace at the beginning of the first heading block 1`] = ` +" +A quick brown fox jumps over the lazy dog. +" +`; diff --git a/packages/block-library/src/heading/test/index.native.js b/packages/block-library/src/heading/test/index.native.js index 498ae6b3ea1d72..b8d93c32f5b4f6 100644 --- a/packages/block-library/src/heading/test/index.native.js +++ b/packages/block-library/src/heading/test/index.native.js @@ -189,4 +189,69 @@ describe( 'Heading block', () => { // Assert expect( getEditorHtml() ).toMatchSnapshot(); } ); + + it( 'should transform to a paragraph block when pressing backspace at the beginning of the first heading block', async () => { + // Arrange + await initializeEditor(); + + // Act + await addBlock( screen, 'Heading' ); + const headingBlock = getBlock( screen, 'Heading' ); + fireEvent.press( headingBlock ); + + const headingTextInput = + within( headingBlock ).getByPlaceholderText( 'Heading' ); + typeInRichText( + headingTextInput, + 'A quick brown fox jumps over the lazy dog.', + { finalSelectionStart: 0, finalSelectionEnd: 0 } + ); + + fireEvent( headingTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: BACKSPACE, + } ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'should keep the heading when there is an empty paragraph block before and backspace is pressed at the start', async () => { + // Arrange + await initializeEditor(); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + const paragraphTextInput = + within( paragraphBlock ).getByPlaceholderText( 'Start writing…' ); + fireEvent( paragraphTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + + await addBlock( screen, 'Heading' ); + const headingBlock = getBlock( screen, 'Heading', { rowIndex: 2 } ); + fireEvent.press( headingBlock ); + + const headingTextInput = + within( headingBlock ).getByPlaceholderText( 'Heading' ); + typeInRichText( + headingTextInput, + 'A quick brown fox jumps over the lazy dog.', + { finalSelectionStart: 0, finalSelectionEnd: 0 } + ); + + fireEvent( headingTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: BACKSPACE, + } ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index b5e7e6be095170..673ab44a8c28aa 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -145,7 +145,7 @@ export function ImageEdit( { scale: undefined, } ); } - }, [ align ] ); + }, [ __unstableMarkNextChangeAsNotPersistent, align, setAttributes ] ); const { getSettings } = useSelect( blockEditorStore ); const blockEditingMode = useBlockEditingMode(); @@ -283,7 +283,7 @@ export function ImageEdit( { ); @@ -317,8 +317,11 @@ export function ImageEdit( { return { lockUrlControls: !! metadata?.bindings?.url && - ( ! blockBindingsSource || - blockBindingsSource?.lockAttributesEditing() ), + ! blockBindingsSource?.canUserEditValue( { + select, + context, + args: metadata?.bindings?.url?.args, + } ), lockUrlControlsMessage: blockBindingsSource?.label ? sprintf( /* translators: %s: Label of the bindings source. */ @@ -328,7 +331,7 @@ export function ImageEdit( { : __( 'Connected to dynamic data' ), }; }, - [ isSingleSelected ] + [ isSingleSelected, metadata?.bindings?.url ] ); const placeholder = ( content ) => { return ( @@ -359,9 +362,7 @@ export function ImageEdit( { } } > { lockUrlControls ? ( - + { lockUrlControlsMessage } ) : ( diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 010ec4cd596949..efa526ec0afd5d 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -581,7 +581,10 @@ export class ImageEdit extends Component { ) }{ ' ' } @@ -617,7 +620,7 @@ export class ImageEdit extends Component { styles.removeFeaturedButton, ] } cellContainerStyle={ styles.setFeaturedButtonCellContainer } - separatorType={ 'none' } + separatorType="none" onPress={ () => this.onSetFeatured( MEDIA_ID_NO_FEATURED_IMAGE_SET ) } @@ -629,7 +632,7 @@ export class ImageEdit extends Component { label={ __( 'Set as Featured Image' ) } labelStyle={ setFeaturedButtonStyle } cellContainerStyle={ styles.setFeaturedButtonCellContainer } - separatorType={ 'none' } + separatorType="none" onPress={ () => this.onSetFeatured( attributes.id ) } /> ); diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index fc1199bb40fb01..6095ef2fee24d1 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -462,8 +462,11 @@ export default function Image( { return { lockUrlControls: !! urlBinding && - ( ! urlBindingSource || - urlBindingSource?.lockAttributesEditing() ), + ! urlBindingSource?.canUserEditValue( { + select, + context, + args: urlBinding?.args, + } ), lockHrefControls: // Disable editing the link of the URL if the image is inside a pattern instance. // This is a temporary solution until we support overriding the link on the frontend. @@ -474,8 +477,11 @@ export default function Image( { hasParentPattern, lockAltControls: !! altBinding && - ( ! altBindingSource || - altBindingSource?.lockAttributesEditing() ), + ! altBindingSource?.canUserEditValue( { + select, + context, + args: altBinding?.args, + } ), lockAltControlsMessage: altBindingSource?.label ? sprintf( /* translators: %s: Label of the bindings source. */ @@ -485,8 +491,11 @@ export default function Image( { : __( 'Connected to dynamic data' ), lockTitleControls: !! titleBinding && - ( ! titleBindingSource || - titleBindingSource?.lockAttributesEditing() ), + ! titleBindingSource?.canUserEditValue( { + select, + context, + args: titleBinding?.args, + } ), lockTitleControlsMessage: titleBindingSource?.label ? sprintf( /* translators: %s: Label of the bindings source. */ @@ -499,13 +508,21 @@ export default function Image( { [ clientId, isSingleSelected, metadata?.bindings ] ); + const showUrlInput = + isSingleSelected && + ! isEditingImage && + ! lockHrefControls && + ! lockUrlControls; + + const showCoverControls = isSingleSelected && canInsertCover; + + const showBlockControls = showUrlInput || allowCrop || showCoverControls; + const controls = ( <> - - { isSingleSelected && - ! isEditingImage && - ! lockHrefControls && - ! lockUrlControls && ( + { showBlockControls && ( + + { showUrlInput && ( ) } - { allowCrop && ( - setIsEditingImage( true ) } - icon={ crop } - label={ __( 'Crop' ) } - /> - ) } - { isSingleSelected && canInsertCover && ( - - ) } - + { allowCrop && ( + setIsEditingImage( true ) } + icon={ crop } + label={ __( 'Crop' ) } + /> + ) } + { showCoverControls && ( + + ) } + + ) } { isSingleSelected && ! isEditingImage && ! lockUrlControls && ( { lockAltControlsMessage }> ) : ( <> - + { __( 'Describe the purpose of the image.' ) } @@ -684,7 +709,14 @@ export default function Image( { <>{ lockAltControlsMessage }> ) : ( <> - + { __( 'Describe the purpose of the image.' ) } diff --git a/packages/block-library/src/latest-posts/edit.native.js b/packages/block-library/src/latest-posts/edit.native.js index 90edce4f84b5b9..632efa149313f6 100644 --- a/packages/block-library/src/latest-posts/edit.native.js +++ b/packages/block-library/src/latest-posts/edit.native.js @@ -207,7 +207,7 @@ class LatestPostsEdit extends Component { label={ __( 'Add link to featured image' ) } checked={ addLinkToFeaturedImage } onChange={ this.onSetAddLinkToFeaturedImage } - separatorType={ 'topFullWidth' } + separatorType="topFullWidth" /> > ) } diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js index 5901cd780c1309..bcdbc658507fce 100644 --- a/packages/block-library/src/media-text/edit.js +++ b/packages/block-library/src/media-text/edit.js @@ -187,6 +187,12 @@ function MediaTextEdit( { mediaId: undefined, mediaUrl: undefined, mediaAlt: undefined, + mediaLink: undefined, + linkDestination: undefined, + linkTarget: undefined, + linkClass: undefined, + rel: undefined, + href: undefined, useFeaturedImage: ! useFeaturedImage, } ); }; @@ -383,7 +389,14 @@ function MediaTextEdit( { onChange={ onMediaAltChange } help={ <> - + { __( 'Describe the purpose of the image.' ) } @@ -446,17 +459,13 @@ function MediaTextEdit( { > ) } - { mediaType === 'image' && ( + { mediaType === 'image' && ! useFeaturedImage && ( - + ); 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 a06bba719db5e9..eaee027c061856 100644 --- a/packages/block-library/src/media-text/media-container.native.js +++ b/packages/block-library/src/media-text/media-container.native.js @@ -330,7 +330,7 @@ class MediaContainer extends Component { onSelect={ this.onSelectMediaUploadOption } allowedTypes={ ALLOWED_MEDIA_TYPES } onFocus={ this.props.onFocus } - className={ 'no-block-outline' } + className="no-block-outline" /> ); } diff --git a/packages/block-library/src/media-text/style.scss b/packages/block-library/src/media-text/style.scss index e43961ac890973..d24ef46409323e 100644 --- a/packages/block-library/src/media-text/style.scss +++ b/packages/block-library/src/media-text/style.scss @@ -15,27 +15,28 @@ } .wp-block-media-text.is-vertically-aligned-top { - .wp-block-media-text__content, - .wp-block-media-text__media { + > .wp-block-media-text__content, + > .wp-block-media-text__media { align-self: start; } } + .wp-block-media-text, .wp-block-media-text.is-vertically-aligned-center { - .wp-block-media-text__content, - .wp-block-media-text__media { + > .wp-block-media-text__content, + > .wp-block-media-text__media { align-self: center; } } .wp-block-media-text.is-vertically-aligned-bottom { - .wp-block-media-text__content, - .wp-block-media-text__media { + > .wp-block-media-text__content, + > .wp-block-media-text__media { align-self: end; } } -.wp-block-media-text .wp-block-media-text__media { +.wp-block-media-text > .wp-block-media-text__media { /*!rtl:begin:ignore*/ grid-column: 1; grid-row: 1; @@ -43,7 +44,7 @@ margin: 0; } -.wp-block-media-text .wp-block-media-text__content { +.wp-block-media-text > .wp-block-media-text__content { direction: ltr; /*!rtl:begin:ignore*/ grid-column: 2; @@ -53,14 +54,14 @@ word-break: break-word; } -.wp-block-media-text.has-media-on-the-right .wp-block-media-text__media { +.wp-block-media-text.has-media-on-the-right > .wp-block-media-text__media { /*!rtl:begin:ignore*/ grid-column: 2; grid-row: 1; /*!rtl:end:ignore*/ } -.wp-block-media-text.has-media-on-the-right .wp-block-media-text__content { +.wp-block-media-text.has-media-on-the-right > .wp-block-media-text__content { /*!rtl:begin:ignore*/ grid-column: 1; grid-row: 1; @@ -75,18 +76,18 @@ vertical-align: middle; } -.wp-block-media-text.is-image-fill .wp-block-media-text__media { +.wp-block-media-text.is-image-fill > .wp-block-media-text__media { height: 100%; min-height: 250px; background-size: cover; } -.wp-block-media-text.is-image-fill .wp-block-media-text__media > a { +.wp-block-media-text.is-image-fill > .wp-block-media-text__media > a { display: block; height: 100%; } -.wp-block-media-text.is-image-fill .wp-block-media-text__media img { +.wp-block-media-text.is-image-fill > .wp-block-media-text__media img { // The image is visually hidden but accessible to assistive technologies. position: absolute; width: 1px; @@ -107,11 +108,11 @@ @media (max-width: #{ ($break-small) }) { .wp-block-media-text.is-stacked-on-mobile { grid-template-columns: 100% !important; - .wp-block-media-text__media { + > .wp-block-media-text__media { grid-column: 1; grid-row: 1; } - .wp-block-media-text__content { + > .wp-block-media-text__content { grid-column: 1; grid-row: 2; } diff --git a/packages/block-library/src/missing/edit.native.js b/packages/block-library/src/missing/edit.native.js index a6164f590ca21d..7432f5fae0f12f 100644 --- a/packages/block-library/src/missing/edit.native.js +++ b/packages/block-library/src/missing/edit.native.js @@ -88,7 +88,7 @@ export class UnsupportedBlockEdit extends Component { onPress={ this.onHelpButtonPressed } style={ styles.helpIconContainer } accessibilityLabel={ __( 'Help button' ) } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityHint={ __( 'Tap here to show help' ) } > diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 125dd90ee87bec..9e2e171975570f 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -261,6 +261,7 @@ export default function NavigationLinkEdit( { const isDraggingWithin = useIsDraggingWithin( listItemRef ); const itemLabelPlaceholder = __( 'Add label…' ); const ref = useRef(); + const linkUIref = useRef(); const prevUrl = usePrevious( url ); // Change the label using inspector causes rich text to change focus on firefox. @@ -570,14 +571,27 @@ export default function NavigationLinkEdit( { ) } { isLinkOpen && ( { // If there is no link then remove the auto-inserted block. // This avoids empty blocks which can provided a poor UX. if ( ! url ) { - // Select the previous block to keep focus nearby - selectPreviousBlock( clientId, true ); + // Fixes https://github.com/WordPress/gutenberg/issues/61361 + // There's a chance we're closing due to the user selecting the browse all button. + // Only move focus if the focus is still within the popover ui. If it's not within + // the popover, it's because something has taken the focus from the popover, and + // we don't want to steal it back. + if ( + linkUIref.current.contains( + window.document.activeElement + ) + ) { + // Select the previous block to keep focus nearby + selectPreviousBlock( clientId, true ); + } + // Remove the link. onReplace( [] ); return; diff --git a/packages/block-library/src/navigation-link/link-ui.js b/packages/block-library/src/navigation-link/link-ui.js index ce79af40e4708c..6619c46253546e 100644 --- a/packages/block-library/src/navigation-link/link-ui.js +++ b/packages/block-library/src/navigation-link/link-ui.js @@ -20,6 +20,7 @@ import { useState, useRef, useEffect, + forwardRef, } from '@wordpress/element'; import { store as coreStore, @@ -145,7 +146,7 @@ function LinkUIBlockInserter( { clientId, onBack, onSelectBlock } ) { ); } -export function LinkUI( props ) { +function UnforwardedLinkUI( props, ref ) { const [ addingBlock, setAddingBlock ] = useState( false ); const [ focusAddBlockButton, setFocusAddBlockButton ] = useState( false ); const { saveEntityRecord } = useDispatch( coreStore ); @@ -214,6 +215,7 @@ export function LinkUI( props ) { return ( { const blockInserterAriaRole = 'listbox'; const addBlockButtonRef = useRef(); diff --git a/packages/block-library/src/navigation-submenu/edit.js b/packages/block-library/src/navigation-submenu/edit.js index f435b5e5a8a953..bbba77707e8159 100644 --- a/packages/block-library/src/navigation-submenu/edit.js +++ b/packages/block-library/src/navigation-submenu/edit.js @@ -459,34 +459,32 @@ export default function NavigationSubmenuEdit( { { /* eslint-disable jsx-a11y/anchor-is-valid */ } { /* eslint-enable */ } - { - - setAttributes( { label: labelValue } ) + + setAttributes( { label: labelValue } ) + } + onMerge={ mergeBlocks } + onReplace={ onReplace } + aria-label={ __( 'Navigation link text' ) } + placeholder={ itemLabelPlaceholder } + withoutInteractiveFormatting + allowedFormats={ [ + 'core/bold', + 'core/italic', + 'core/image', + 'core/strikethrough', + ] } + onClick={ () => { + if ( ! openSubmenusOnClick && ! url ) { + setIsLinkOpen( true ); + setOpenedBy( ref.current ); } - onMerge={ mergeBlocks } - onReplace={ onReplace } - aria-label={ __( 'Navigation link text' ) } - placeholder={ itemLabelPlaceholder } - withoutInteractiveFormatting - allowedFormats={ [ - 'core/bold', - 'core/italic', - 'core/image', - 'core/strikethrough', - ] } - onClick={ () => { - if ( ! openSubmenusOnClick && ! url ) { - setIsLinkOpen( true ); - setOpenedBy( ref.current ); - } - } } - /> - } + } } + /> { ! openSubmenusOnClick && isLinkOpen && ( = 6.5 - * that are not present in Gutenberg's WP 6.5 compatibility layer. + * Do not add the `block_core_navigation_insert_hooked_blocks_into_rest_response` filter in the following cases: + * - If Core has added the `insert_hooked_blocks_into_rest_response` filter already (WP >= 6.6); + * - or if the `set_ignored_hooked_blocks_metadata` function is unavailable (which is required for the filter to work. It was introduced by WP 6.5 but is not present in Gutenberg's WP 6.5 compatibility layer); + * - or if the `$rest_prepare_wp_navigation_core_callback` filter has already been added. */ -if ( function_exists( 'set_ignored_hooked_blocks_metadata' ) && ! has_filter( 'rest_prepare_wp_navigation', $rest_prepare_wp_navigation_core_callback ) ) { +if ( + ! has_filter( 'rest_prepare_wp_navigation', 'insert_hooked_blocks_into_rest_response' ) && + function_exists( 'set_ignored_hooked_blocks_metadata' ) && + ! has_filter( 'rest_prepare_wp_navigation', $rest_prepare_wp_navigation_core_callback ) +) { add_filter( 'rest_prepare_wp_navigation', 'block_core_navigation_insert_hooked_blocks_into_rest_response', 10, 3 ); } diff --git a/packages/block-library/src/page-list/convert-to-links-modal.js b/packages/block-library/src/page-list/convert-to-links-modal.js index 8f8c75f6dea1c1..33ee22045b8241 100644 --- a/packages/block-library/src/page-list/convert-to-links-modal.js +++ b/packages/block-library/src/page-list/convert-to-links-modal.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { Button, Modal } from '@wordpress/components'; +import { useInstanceId } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; export const convertDescription = __( @@ -13,12 +14,20 @@ export function ConvertToLinksModal( { onClick, onClose, disabled } ) { - + { convertDescription } diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js index 0e81336dfde287..2106aa7c39e9f7 100644 --- a/packages/block-library/src/page-list/edit.js +++ b/packages/block-library/src/page-list/edit.js @@ -63,7 +63,7 @@ function BlockContent( { if ( pages === null ) { return ( - + { __( 'Page List: Cannot retrieve Pages.' ) } @@ -73,7 +73,7 @@ function BlockContent( { if ( pages.length === 0 ) { return ( - + { __( 'Page List: Cannot retrieve Pages.' ) } @@ -101,7 +101,7 @@ function BlockContent( { return ( - + { __( 'Page List: Cannot retrieve Pages.' ) } 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 2910d1551ca28b..10e599372dfef9 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 @@ -1,5 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Paragraph block should prevent deleting the first Paragraph block when pressing backspace at the start 1`] = ` +" +A quick brown fox jumps over the lazy dog. +" +`; + exports[`Paragraph block should render without crashing and match snapshot 1`] = ` { expect( screen.toJSON() ).toMatchSnapshot(); } ); + it( 'should prevent deleting the first Paragraph block when pressing backspace at the start', async () => { + // Arrange + const screen = await initializeEditor(); + 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: 0, finalSelectionEnd: 0 } + ); + + fireEvent( paragraphTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: BACKSPACE, + } ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + it( 'should bold text', async () => { // Arrange const screen = await initializeEditor(); diff --git a/packages/block-library/src/query/variations.js b/packages/block-library/src/query/variations.js index 65a73d134981f3..1970d9947916b5 100644 --- a/packages/block-library/src/query/variations.js +++ b/packages/block-library/src/query/variations.js @@ -54,9 +54,7 @@ const variations = [ }, }, scope: [ 'inserter' ], - isActive: ( { namespace, query } ) => { - return namespace === 'core/posts-list' && query.postType === 'post'; - }, + isActive: [ 'namespace', 'query.postType' ], }, { name: 'title-date', diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index 1a88576965002f..cfe7b29caf5de0 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -441,7 +441,7 @@ export default function SearchEdit( { widthUnit: newUnit, } ); } } - __unstableInputWidth={ '80px' } + __unstableInputWidth="80px" value={ `${ width }${ widthUnit }` } units={ units } /> diff --git a/packages/block-library/src/social-link/edit.native.js b/packages/block-library/src/social-link/edit.native.js index 3ea794a9b8c20b..0b4b6b0ac7d4bd 100644 --- a/packages/block-library/src/social-link/edit.native.js +++ b/packages/block-library/src/social-link/edit.native.js @@ -171,7 +171,7 @@ const SocialLinkEdit = ( { { /> { if ( - newAttributes.metadata.bindings[ binding ]?.source?.name === - 'pattern_attributes' + newAttributes.metadata.bindings[ binding ]?.source === + 'core/pattern-overrides' ) { - newAttributes.metadata.bindings[ binding ].source = - 'core/pattern-overrides'; + hasPatternOverrides = true; + newAttributes.metadata = { + ...newAttributes.metadata, + bindings: { ...newAttributes.metadata.bindings }, + }; + delete newAttributes.metadata.bindings[ binding ]; } } ); + if ( hasPatternOverrides ) { + newAttributes.metadata.bindings.__default = { + source: 'core/pattern-overrides', + }; + } } } return [ name, newAttributes ]; diff --git a/packages/blocks/src/api/parser/get-block-attributes.js b/packages/blocks/src/api/parser/get-block-attributes.js index 24faae73704636..36ea86399329e1 100644 --- a/packages/blocks/src/api/parser/get-block-attributes.js +++ b/packages/blocks/src/api/parser/get-block-attributes.js @@ -7,7 +7,6 @@ import memoize from 'memize'; /** * WordPress dependencies */ -import { pipe } from '@wordpress/compose'; import { applyFilters } from '@wordpress/hooks'; import { RichTextData } from '@wordpress/rich-text'; @@ -37,24 +36,8 @@ import { normalizeBlockType, getDefault } from '../utils'; * * @return {Function} Enhanced hpq matcher. */ -export const toBooleanAttributeMatcher = ( matcher ) => - pipe( [ - matcher, - // Expected values from `attr( 'disabled' )`: - // - // - // - Value: `undefined` - // - Transformed: `false` - // - // - // - Value: `''` - // - Transformed: `true` - // - // - // - Value: `'disabled'` - // - Transformed: `true` - ( value ) => value !== undefined, - ] ); +export const toBooleanAttributeMatcher = ( matcher ) => ( value ) => + matcher( value ) !== undefined; /** * Returns true if value is of the given JSON schema type, or false otherwise. @@ -214,13 +197,13 @@ export function isValidByEnum( value, enumSet ) { */ export const matcherFromSource = memoize( ( sourceConfig ) => { switch ( sourceConfig.source ) { - case 'attribute': + case 'attribute': { let matcher = attr( sourceConfig.selector, sourceConfig.attribute ); if ( sourceConfig.type === 'boolean' ) { matcher = toBooleanAttributeMatcher( matcher ); } - return matcher; + } case 'html': return html( sourceConfig.selector, sourceConfig.multiline ); case 'text': @@ -244,12 +227,10 @@ export const matcherFromSource = memoize( ( sourceConfig ) => { ) ); return query( sourceConfig.selector, subMatchers ); - case 'tag': - return pipe( [ - prop( sourceConfig.selector, 'nodeName' ), - ( nodeName ) => - nodeName ? nodeName.toLowerCase() : undefined, - ] ); + case 'tag': { + const matcher = prop( sourceConfig.selector, 'nodeName' ); + return ( domNode ) => matcher( domNode )?.toLowerCase(); + } default: // eslint-disable-next-line no-console console.error( `Unknown source type "${ sourceConfig.source }"` ); diff --git a/packages/blocks/src/store/private-actions.js b/packages/blocks/src/store/private-actions.js index a47d9aacab37ae..dd6650338d9d1a 100644 --- a/packages/blocks/src/store/private-actions.js +++ b/packages/blocks/src/store/private-actions.js @@ -55,6 +55,6 @@ export function registerBlockBindingsSource( source ) { setValue: source.setValue, setValues: source.setValues, getPlaceholder: source.getPlaceholder, - lockAttributesEditing: source.lockAttributesEditing, + canUserEditValue: source.canUserEditValue, }; } diff --git a/packages/blocks/src/store/reducer.js b/packages/blocks/src/store/reducer.js index 1d0d8cb2e968fc..c00810c534d55d 100644 --- a/packages/blocks/src/store/reducer.js +++ b/packages/blocks/src/store/reducer.js @@ -381,10 +381,7 @@ export function blockBindingsSources( state = {}, action ) { setValue: action.setValue, setValues: action.setValues, getPlaceholder: action.getPlaceholder, - lockAttributesEditing: () => - action.lockAttributesEditing - ? action.lockAttributesEditing() - : true, + canUserEditValue: action.canUserEditValue || ( () => false ), }, }; } diff --git a/packages/blocks/src/store/selectors.js b/packages/blocks/src/store/selectors.js index a69879809ab1a6..ecdc82e7633043 100644 --- a/packages/blocks/src/store/selectors.js +++ b/packages/blocks/src/store/selectors.js @@ -6,7 +6,6 @@ import removeAccents from 'remove-accents'; /** * WordPress dependencies */ -import { pipe } from '@wordpress/compose'; import { createSelector } from '@wordpress/data'; /** @@ -238,26 +237,54 @@ export const getBlockVariations = createSelector( export function getActiveBlockVariation( state, blockName, attributes, scope ) { const variations = getBlockVariations( state, blockName, scope ); - const match = variations?.find( ( variation ) => { + if ( ! variations ) { + return variations; + } + + const blockType = getBlockType( state, blockName ); + const attributeKeys = Object.keys( blockType?.attributes || {} ); + let match; + let maxMatchedAttributes = 0; + + for ( const variation of variations ) { if ( Array.isArray( variation.isActive ) ) { - const blockType = getBlockType( state, blockName ); - const attributeKeys = Object.keys( blockType?.attributes || {} ); const definedAttributes = variation.isActive.filter( - ( attribute ) => attributeKeys.includes( attribute ) + ( attribute ) => { + // We support nested attribute paths, e.g. `layout.type`. + // In this case, we need to check if the part before the + // first dot is a known attribute. + const topLevelAttribute = attribute.split( '.' )[ 0 ]; + return attributeKeys.includes( topLevelAttribute ); + } ); - if ( definedAttributes.length === 0 ) { - return false; + const definedAttributesLength = definedAttributes.length; + if ( definedAttributesLength === 0 ) { + continue; } - return definedAttributes.every( - ( attribute ) => - attributes[ attribute ] === - variation.attributes[ attribute ] - ); + const isMatch = definedAttributes.every( ( attribute ) => { + const attributeValue = getValueFromObjectPath( + attributes, + attribute + ); + if ( attributeValue === undefined ) { + return false; + } + return ( + attributeValue === + getValueFromObjectPath( variation.attributes, attribute ) + ); + } ); + if ( isMatch && definedAttributesLength > maxMatchedAttributes ) { + match = variation; + maxMatchedAttributes = definedAttributesLength; + } + } else if ( variation.isActive?.( attributes, variation.attributes ) ) { + // If isActive is a function, we cannot know how many attributes it matches. + // This means that we cannot compare the specificity of our matches, + // and simply return the best match we have found. + return match || variation; } - - return variation.isActive?.( attributes, variation.attributes ); - } ); - + } return match; } @@ -645,6 +672,18 @@ export function hasBlockSupport( state, nameOrType, feature, defaultSupports ) { return !! getBlockSupport( state, nameOrType, feature, defaultSupports ); } +/** + * Normalizes a search term string: removes accents, converts to lowercase, removes extra whitespace. + * + * @param {string|null|undefined} term Search term to normalize. + * @return {string} Normalized search term. + */ +function getNormalizedSearchTerm( term ) { + return removeAccents( term ?? '' ) + .toLowerCase() + .trim(); +} + /** * Returns true if the block type by the given name or object value matches a * search term, or false otherwise. @@ -684,30 +723,12 @@ export function hasBlockSupport( state, nameOrType, feature, defaultSupports ) { * * @return {Object[]} Whether block type matches search term. */ -export function isMatchingSearchTerm( state, nameOrType, searchTerm ) { +export function isMatchingSearchTerm( state, nameOrType, searchTerm = '' ) { const blockType = getNormalizedBlockType( state, nameOrType ); - - const getNormalizedSearchTerm = pipe( [ - // Disregard diacritics. - // Input: "média" - ( term ) => removeAccents( term ?? '' ), - - // Lowercase. - // Input: "MEDIA" - ( term ) => term.toLowerCase(), - - // Strip leading and trailing whitespace. - // Input: " media " - ( term ) => term.trim(), - ] ); - const normalizedSearchTerm = getNormalizedSearchTerm( searchTerm ); - const isSearchMatch = pipe( [ - getNormalizedSearchTerm, - ( normalizedCandidate ) => - normalizedCandidate.includes( normalizedSearchTerm ), - ] ); + const isSearchMatch = ( candidate ) => + getNormalizedSearchTerm( candidate ).includes( normalizedSearchTerm ); return ( isSearchMatch( blockType.title ) || diff --git a/packages/blocks/src/store/test/selectors.js b/packages/blocks/src/store/test/selectors.js index 1fda11d72311a3..1a6e768724acc7 100644 --- a/packages/blocks/src/store/test/selectors.js +++ b/packages/blocks/src/store/test/selectors.js @@ -291,6 +291,7 @@ describe( 'selectors', () => { testAttribute: {}, firstTestAttribute: {}, secondTestAttribute: {}, + thirdTestAttribute: {}, }, }; const FIRST_VARIATION_TEST_ATTRIBUTE_VALUE = 1; @@ -410,6 +411,47 @@ describe( 'selectors', () => { expect( result ).toEqual( variation ); } ); } ); + it( 'should support nested attribute paths in the isActive array', () => { + const variations = [ + { + name: 'variation-1', + attributes: { + firstTestAttribute: { + nestedProperty: 1, + otherNestedProperty: 5555, + }, + }, + isActive: [ 'firstTestAttribute.nestedProperty' ], + }, + { + name: 'variation-2', + attributes: { + firstTestAttribute: { + nestedProperty: 2, + otherNestedProperty: 5555, + }, + }, + isActive: [ 'firstTestAttribute.nestedProperty' ], + }, + ]; + const state = + createBlockVariationsStateWithTestBlockType( variations ); + + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: { + nestedProperty: 1, + }, + } ) + ).toEqual( variations[ 0 ] ); + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: { + nestedProperty: 2, + }, + } ) + ).toEqual( variations[ 1 ] ); + } ); it( 'should return the active variation based on the given isActive array (multiple values)', () => { const variations = [ { @@ -469,6 +511,136 @@ describe( 'selectors', () => { } ) ).toEqual( variations[ 2 ] ); } ); + it( 'should return the active variation using the match with the highest specificity for the given isActive array (multiple values)', () => { + const variations = [ + { + name: 'variation-1', + attributes: { + firstTestAttribute: 1, + secondTestAttribute: 2, + }, + isActive: [ + 'firstTestAttribute', + 'secondTestAttribute', + ], + }, + { + name: 'variation-2', + attributes: { + firstTestAttribute: 1, + secondTestAttribute: 2, + thirdTestAttribute: 3, + }, + isActive: [ + 'firstTestAttribute', + 'secondTestAttribute', + 'thirdTestAttribute', + ], + }, + { + name: 'variation-3', + attributes: { + firstTestAttribute: 1, + thirdTestAttribute: 3, + }, + isActive: [ + 'firstTestAttribute', + 'thirdTestAttribute', + ], + }, + ]; + + const state = + createBlockVariationsStateWithTestBlockType( variations ); + + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: 1, + secondTestAttribute: 2, + } ) + ).toEqual( variations[ 0 ] ); + // All variations match the following attributes. Since all matches have an array for their isActive + // fields, we can compare the specificity of each match and return the most specific match. + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: 1, + secondTestAttribute: 2, + thirdTestAttribute: 3, + } ) + ).toEqual( variations[ 1 ] ); + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: 1, + thirdTestAttribute: 3, + } ) + ).toEqual( variations[ 2 ] ); + } ); + it( 'should return the active variation using the first match given the isActive array (multiple values) and function', () => { + const variations = [ + { + name: 'variation-1', + attributes: { + firstTestAttribute: 1, + secondTestAttribute: 2, + }, + isActive: [ + 'firstTestAttribute', + 'secondTestAttribute', + ], + }, + { + name: 'variation-2', + attributes: { + firstTestAttribute: 1, + secondTestAttribute: 2, + thirdTestAttribute: 3, + }, + isActive: [ + 'firstTestAttribute', + 'secondTestAttribute', + 'thirdTestAttribute', + ], + }, + { + name: 'variation-3', + attributes: { + firstTestAttribute: 1, + thirdTestAttribute: 3, + }, + isActive: ( blockAttributes, variationAttributes ) => + blockAttributes.firstTestAttribute === + variationAttributes.firstTestAttribute && + blockAttributes.thirdTestAttribute === + variationAttributes.thirdTestAttribute, + }, + ]; + + const state = + createBlockVariationsStateWithTestBlockType( variations ); + + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: 1, + secondTestAttribute: 2, + } ) + ).toEqual( variations[ 0 ] ); + // All variations match the following attributes. However, since the third variation has a function + // for its isActive field, we cannot compare the specificity of each match, so instead we return the + // best match we've found. + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: 1, + secondTestAttribute: 2, + thirdTestAttribute: 3, + } ) + ).toEqual( variations[ 1 ] ); + expect( + getActiveBlockVariation( state, blockName, { + firstTestAttribute: 1, + thirdTestAttribute: 3, + } ) + ).toEqual( variations[ 2 ] ); + } ); it( 'should ignore attributes that are not defined in the block type', () => { const variations = [ { diff --git a/packages/browserslist-config/CHANGELOG.md b/packages/browserslist-config/CHANGELOG.md index 51aa52e81c2b44..6737bb003c1936 100644 --- a/packages/browserslist-config/CHANGELOG.md +++ b/packages/browserslist-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index e78bf84269cb31..4f5a1773c25f9e 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/browserslist-config", - "version": "5.41.0", + "version": "6.0.0", "description": "WordPress Browserslist shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/commands/CHANGELOG.md b/packages/commands/CHANGELOG.md index a9f5a293061827..17a55cbf221dec 100644 --- a/packages/commands/CHANGELOG.md +++ b/packages/commands/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/commands/package.json b/packages/commands/package.json index fc89c6dd2508c2..a4a8e952f3ef1c 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/commands", - "version": "0.29.0", + "version": "1.0.0", "description": "Handles the commands menu.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 34c1a6ca410c33..4ad4dd4ec63125 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 28.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). @@ -11,11 +13,14 @@ - `Tabs`: Animate indicator ([#60560](https://github.com/WordPress/gutenberg/pull/60560)). - `ComboboxControl`: Introduce Combobox expandOnFocus prop ([#61705](https://github.com/WordPress/gutenberg/pull/61705)). +- `ProgressBar`: Expose as public API ([#61062](https://github.com/WordPress/gutenberg/pull/61062)). +- `ProgressBar`: Simplify default width implementation and make it more easily overridable ([#61976](https://github.com/WordPress/gutenberg/pull/61976)). ### Bug Fixes - `Autocomplete`: Stabilize rendering of autocomplete items ([#61877](https://github.com/WordPress/gutenberg/pull/61877)). - `TabPanel`: Make the the focus styles consistent with `Tabs`. ([#61317](https://github.com/WordPress/gutenberg/pull/61317)). +- `InputControl`: Fixed z-index issue where slider dots appeared in front of the Appearance dropdown. ([#61937](https://github.com/WordPress/gutenberg/pull/61937)) ### Internal diff --git a/packages/components/package.json b/packages/components/package.json index 79b01e5f020ee9..89ed60367d88ea 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "27.6.0", + "version": "28.0.0", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/src/animate/stories/index.story.tsx b/packages/components/src/animate/stories/index.story.tsx index be076b4e6976c6..de28495a97fc57 100644 --- a/packages/components/src/animate/stories/index.story.tsx +++ b/packages/components/src/animate/stories/index.story.tsx @@ -27,7 +27,12 @@ export const Default = Template.bind( {} ); Default.args = { children: ( { className } ) => ( - { `No default animation. Use one of type = "appear", "slide-in", or "loading".` } + + { /* eslint-disable react/no-unescaped-entities */ } + No default animation. Use one of type = "appear", "slide-in", or + "loading". + { /* eslint-enable react/no-unescaped-entities */ } + ), }; diff --git a/packages/components/src/autocomplete/test/index.tsx b/packages/components/src/autocomplete/test/index.tsx index 7a31680b3dd5e1..0df784f8367cdd 100644 --- a/packages/components/src/autocomplete/test/index.tsx +++ b/packages/components/src/autocomplete/test/index.tsx @@ -67,10 +67,10 @@ describe( 'AutocompleterUI', () => { return ( {} } onSelect={ () => {} } diff --git a/packages/components/src/button/index.native.js b/packages/components/src/button/index.native.js index f093502a750fd7..063a2cc191eaed 100644 --- a/packages/components/src/button/index.native.js +++ b/packages/components/src/button/index.native.js @@ -183,7 +183,7 @@ export function Button( props ) { accessible accessibilityLabel={ label } accessibilityStates={ states } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityHint={ hint } onPress={ onClick } style={ containerStyle } diff --git a/packages/components/src/button/index.tsx b/packages/components/src/button/index.tsx index b60f6af039e595..e2ef34f35712e7 100644 --- a/packages/components/src/button/index.tsx +++ b/packages/components/src/button/index.tsx @@ -174,14 +174,16 @@ export function UnforwardedButton( const anchorProps: ComponentPropsWithoutRef< 'a' > = Tag === 'a' ? { href, target } : {}; + const disableEventProps: { + [ key: string ]: ( event: MouseEvent ) => void; + } = {}; if ( disabled && isFocusable ) { // In this case, the button will be disabled, but still focusable and // perceivable by screen reader users. buttonProps[ 'aria-disabled' ] = true; anchorProps[ 'aria-disabled' ] = true; - for ( const disabledEvent of disabledEventsOnDisabledButton ) { - additionalProps[ disabledEvent ] = ( event: MouseEvent ) => { + disableEventProps[ disabledEvent ] = ( event: MouseEvent ) => { if ( event ) { event.stopPropagation(); event.preventDefault(); @@ -234,6 +236,7 @@ export function UnforwardedButton( ) } + { ...disableEventProps } { ...commonProps } > { elementChildren } @@ -242,6 +245,7 @@ export function UnforwardedButton( ) } + { ...disableEventProps } { ...commonProps } > { elementChildren } diff --git a/packages/components/src/circular-option-picker/circular-option-picker.tsx b/packages/components/src/circular-option-picker/circular-option-picker.tsx index 346a912535fa35..cd2ddcf90d7ce0 100644 --- a/packages/components/src/circular-option-picker/circular-option-picker.tsx +++ b/packages/components/src/circular-option-picker/circular-option-picker.tsx @@ -102,7 +102,7 @@ function ListboxCircularOptionPicker( { ...additionalProps } id={ baseId } store={ compositeStore } - role={ 'listbox' } + role="listbox" > { options } @@ -156,7 +156,7 @@ function CircularOptionPicker( props: CircularOptionPickerProps ) { ) : undefined; const options = ( - + { optionsProp } ); diff --git a/packages/components/src/circular-option-picker/stories/index.story.tsx b/packages/components/src/circular-option-picker/stories/index.story.tsx index 45643e9d6dd6e1..e091a2ac54d41e 100644 --- a/packages/components/src/circular-option-picker/stories/index.story.tsx +++ b/packages/components/src/circular-option-picker/stories/index.story.tsx @@ -101,7 +101,7 @@ const DefaultActions = () => { setCurrentColor?.( undefined ) } > - { 'Clear' } + Clear ); }; diff --git a/packages/components/src/circular-option-picker/test/index.tsx b/packages/components/src/circular-option-picker/test/index.tsx index b37cb7e534168f..0b5ccbeecb57d8 100644 --- a/packages/components/src/circular-option-picker/test/index.tsx +++ b/packages/components/src/circular-option-picker/test/index.tsx @@ -9,17 +9,11 @@ import { press, sleep } from '@ariakit/test'; */ import CircularOptionPicker from '..'; -const SINGLE_OPTION = [ ]; +const SINGLE_OPTION = [ ]; const MULTIPLE_OPTIONS = [ - , - , + , + , ]; const DEFAULT_PROPS = { diff --git a/packages/components/src/color-palette/index.native.js b/packages/components/src/color-palette/index.native.js index 87b8e704b704ec..bb45de6d66e884 100644 --- a/packages/components/src/color-palette/index.native.js +++ b/packages/components/src/color-palette/index.native.js @@ -285,7 +285,7 @@ function ColorPalette( { onColorPress( color ) } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityState={ { selected: isSelected( color ), } } @@ -326,7 +326,7 @@ function ColorPalette( { ) } { describe( 'rendering', () => { it( 'renders with defaults', () => { const { container } = render( - + ); expect( container ).toMatchSnapshot(); } ); @@ -37,7 +34,7 @@ describe( 'DimensionControl', () => { const { container } = render( ); @@ -48,9 +45,9 @@ describe( 'DimensionControl', () => { const { container } = render( ); expect( container ).toMatchSnapshot(); @@ -78,7 +75,7 @@ describe( 'DimensionControl', () => { const { container } = render( ); @@ -93,7 +90,7 @@ describe( 'DimensionControl', () => { render( ); @@ -115,7 +112,7 @@ describe( 'DimensionControl', () => { render( ); diff --git a/packages/components/src/dropdown-menu-v2/stories/index.story.tsx b/packages/components/src/dropdown-menu-v2/stories/index.story.tsx index eba4a43fe49aa0..6996431fc6053a 100644 --- a/packages/components/src/dropdown-menu-v2/stories/index.story.tsx +++ b/packages/components/src/dropdown-menu-v2/stories/index.story.tsx @@ -110,7 +110,7 @@ export const Default: StoryFn< typeof DropdownMenu > = ( props ) => ( > With prefix - With suffix + With suffix } diff --git a/packages/components/src/dropdown/stories/index.story.tsx b/packages/components/src/dropdown/stories/index.story.tsx index 96d67441f78d48..3df2f783450666 100644 --- a/packages/components/src/dropdown/stories/index.story.tsx +++ b/packages/components/src/dropdown/stories/index.story.tsx @@ -66,7 +66,9 @@ WithMorePadding.args = { ...Default.args, renderContent: () => ( - Content wrapped with { `paddingSize="medium"` }. + { /* eslint-disable react/no-unescaped-entities */ } + Content wrapped with paddingSize="medium". + { /* eslint-enable react/no-unescaped-entities */ } ), }; @@ -81,7 +83,9 @@ WithNoPadding.args = { ...Default.args, renderContent: () => ( - Content wrapped with { `paddingSize="none"` }. + { /* eslint-disable react/no-unescaped-entities */ } + Content wrapped with paddingSize="none". + { /* eslint-enable react/no-unescaped-entities */ } ), }; diff --git a/packages/components/src/font-size-picker/index.native.js b/packages/components/src/font-size-picker/index.native.js index 06f0d386b74d78..b12a27296ca297 100644 --- a/packages/components/src/font-size-picker/index.native.js +++ b/packages/components/src/font-size-picker/index.native.js @@ -85,7 +85,7 @@ function FontSizePicker( { : __( 'Default' ) } onPress={ openSubSheet } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityLabel={ accessibilityLabel } accessibilityHint={ sprintf( // translators: %s: Select control button label e.g. Small @@ -112,8 +112,8 @@ function FontSizePicker( { label={ __( 'Default' ) } onPress={ onChangeValue( undefined ) } leftAlign - key={ 'default' } - accessibilityRole={ 'button' } + key="default" + accessibilityRole="button" accessibilityLabel={ __( 'Selected: Default' ) } accessibilityHint={ __( 'Double tap to select default font size' @@ -139,7 +139,7 @@ function FontSizePicker( { onPress={ onChangeValue( item.sizePx ) } leftAlign key={ index } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityLabel={ item.sizePx === selectedValue ? sprintf( diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index a824162cb24129..f3643a1499a02c 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -132,6 +132,7 @@ export { default as PanelHeader } from './panel/header'; export { default as PanelRow } from './panel/row'; export { default as Placeholder } from './placeholder'; export { default as Popover } from './popover'; +export { default as ProgressBar } from './progress-bar'; export { default as QueryControls } from './query-controls'; export { default as __experimentalRadio } from './radio-group/radio'; export { default as __experimentalRadioGroup } from './radio-group'; diff --git a/packages/components/src/input-control/styles/input-control-styles.tsx b/packages/components/src/input-control/styles/input-control-styles.tsx index 3192141a8ac0b1..ddfad620b91244 100644 --- a/packages/components/src/input-control/styles/input-control-styles.tsx +++ b/packages/components/src/input-control/styles/input-control-styles.tsx @@ -83,8 +83,6 @@ export const Root = styled( Flex )` // Focus within, excluding cases where auxiliary controls in prefix or suffix have focus. &:focus-within:not( :has( :is( ${ Prefix }, ${ Suffix } ):focus-within ) ) { - z-index: 1; - ${ BackdropUI } { border-color: ${ COLORS.ui.borderFocus }; box-shadow: ${ CONFIG.controlBoxShadowFocus }; diff --git a/packages/components/src/keyboard-shortcuts/stories/index.story.tsx b/packages/components/src/keyboard-shortcuts/stories/index.story.tsx index d181be737353cd..9e3a1e207451bb 100644 --- a/packages/components/src/keyboard-shortcuts/stories/index.story.tsx +++ b/packages/components/src/keyboard-shortcuts/stories/index.story.tsx @@ -32,7 +32,9 @@ Default.args = { }, children: ( - { `Hit the "a" or "b" key in this textarea:` } + { /* eslint-disable react/no-unescaped-entities */ } + Hit the "a" or "b" key in this textarea: + { /* eslint-enable react/no-unescaped-entities */ } ), diff --git a/packages/components/src/menu-group/stories/index.story.tsx b/packages/components/src/menu-group/stories/index.story.tsx index 80cdae3dab600a..7cb9004b45a8c2 100644 --- a/packages/components/src/menu-group/stories/index.story.tsx +++ b/packages/components/src/menu-group/stories/index.story.tsx @@ -54,7 +54,7 @@ const MultiGroupsTemplate: StoryFn< typeof MenuGroup > = ( args ) => { return ( <> - + Top Toolbar Spotlight Mode Distraction Free diff --git a/packages/components/src/mobile/bottom-sheet-select-control/index.native.js b/packages/components/src/mobile/bottom-sheet-select-control/index.native.js index 3df0f47f2f363f..89c157d85b9b2d 100644 --- a/packages/components/src/mobile/bottom-sheet-select-control/index.native.js +++ b/packages/components/src/mobile/bottom-sheet-select-control/index.native.js @@ -62,7 +62,7 @@ const BottomSheetSelectControl = ( { icon={ icon } value={ selectedOption.label } onPress={ openSubSheet } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityLabel={ sprintf( // translators: %1$s: Select control button label e.g. "Button width". %2$s: Select control option value e.g: "Auto, 25%". __( '%1$s. Currently selected: %2$s' ), @@ -98,7 +98,7 @@ const BottomSheetSelectControl = ( { onPress={ onChangeValue( item.value ) } leftAlign key={ index } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityLabel={ item.value === selectedValue ? sprintf( diff --git a/packages/components/src/mobile/bottom-sheet-text-control/index.native.js b/packages/components/src/mobile/bottom-sheet-text-control/index.native.js index 6c42b1ff817834..430981f8f9695a 100644 --- a/packages/components/src/mobile/bottom-sheet-text-control/index.native.js +++ b/packages/components/src/mobile/bottom-sheet-text-control/index.native.js @@ -82,9 +82,9 @@ const BottomSheetTextControl = ( { defaultValue={ initialValue } multiline placeholder={ placeholder } - placeholderTextColor={ '#87a6bc' } + placeholderTextColor="#87a6bc" style={ textEditorStyle } - textAlignVertical={ 'top' } + textAlignVertical="top" /> > diff --git a/packages/components/src/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js index bfd0d00c0007f1..6618566837cfc9 100644 --- a/packages/components/src/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -272,7 +272,7 @@ class BottomSheetCell extends Component { { value } @@ -419,7 +419,7 @@ class BottomSheetCell extends Component { { customActionButton && ( { title } diff --git a/packages/components/src/mobile/bottom-sheet/color-cell.native.js b/packages/components/src/mobile/bottom-sheet/color-cell.native.js index 0049a36cbb5711..a03b968e3b03ef 100644 --- a/packages/components/src/mobile/bottom-sheet/color-cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/color-cell.native.js @@ -17,7 +17,7 @@ export default function BottomSheetColorCell( props ) { return ( ); diff --git a/packages/components/src/mobile/bottom-sheet/link-suggestion-item-cell.native.js b/packages/components/src/mobile/bottom-sheet/link-suggestion-item-cell.native.js index f183f8f291b5b4..46029a7585f54a 100644 --- a/packages/components/src/mobile/bottom-sheet/link-suggestion-item-cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/link-suggestion-item-cell.native.js @@ -64,7 +64,7 @@ function LinkSuggestionItemCell( { suggestion, onLinkPicked, ...props } ) { { ...props } icon={ icons[ type ] || empty } onPress={ pickLink } - separatorType={ 'none' } + separatorType="none" cellContainerStyle={ suggestionStyles.itemContainerStyle } labelStyle={ suggestionStyles.hidden } valueStyle={ suggestionStyles.hidden } @@ -73,14 +73,14 @@ function LinkSuggestionItemCell( { suggestion, onLinkPicked, ...props } ) { { title } { summary } diff --git a/packages/components/src/mobile/bottom-sheet/nav-bar/action-button.native.js b/packages/components/src/mobile/bottom-sheet/nav-bar/action-button.native.js index 6b11f30959fe5d..6a13bca18e9efb 100644 --- a/packages/components/src/mobile/bottom-sheet/nav-bar/action-button.native.js +++ b/packages/components/src/mobile/bottom-sheet/nav-bar/action-button.native.js @@ -18,7 +18,7 @@ function ActionButton( { return ( diff --git a/packages/components/src/mobile/bottom-sheet/radio-cell.native.js b/packages/components/src/mobile/bottom-sheet/radio-cell.native.js index 2700cb0f923882..c2b5df004d5c31 100644 --- a/packages/components/src/mobile/bottom-sheet/radio-cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/radio-cell.native.js @@ -21,14 +21,14 @@ export default function BottomSheetRadioCell( props ) { return ( { selected && ( diff --git a/packages/components/src/mobile/bottom-sheet/stepper-cell/stepper.android.js b/packages/components/src/mobile/bottom-sheet/stepper-cell/stepper.android.js index 37f72425640fe0..ce0fbba03eae88 100644 --- a/packages/components/src/mobile/bottom-sheet/stepper-cell/stepper.android.js +++ b/packages/components/src/mobile/bottom-sheet/stepper-cell/stepper.android.js @@ -56,7 +56,7 @@ function Stepper( { ) } { children } diff --git a/packages/components/src/mobile/image/icon-retry.native.js b/packages/components/src/mobile/image/icon-retry.native.js index 37e4283bdb47a8..1ac6c714b3f6e4 100644 --- a/packages/components/src/mobile/image/icon-retry.native.js +++ b/packages/components/src/mobile/image/icon-retry.native.js @@ -6,6 +6,6 @@ import { SVG, Path } from '@wordpress/primitives'; export default ( - + ); diff --git a/packages/components/src/mobile/image/image-editing-button.native.js b/packages/components/src/mobile/image/image-editing-button.native.js index 0f29b117bd6138..b3dfd546b63645 100644 --- a/packages/components/src/mobile/image/image-editing-button.native.js +++ b/packages/components/src/mobile/image/image-editing-button.native.js @@ -40,7 +40,7 @@ const ImageEditingButton = ( { diff --git a/packages/components/src/mobile/image/index.native.js b/packages/components/src/mobile/image/index.native.js index 57c83ddbec0a85..9f8673b7bdcdff 100644 --- a/packages/components/src/mobile/image/index.native.js +++ b/packages/components/src/mobile/image/index.native.js @@ -285,7 +285,7 @@ const ImageComponent = ( { disabled={ ! isSelected } accessibilityLabel={ alt } accessibilityHint={ __( 'Double tap and hold to edit' ) } - accessibilityRole={ 'imagebutton' } + accessibilityRole="imagebutton" key={ url } style={ imageContainerStyles } > diff --git a/packages/components/src/mobile/picker/index.android.js b/packages/components/src/mobile/picker/index.android.js index 997af1109f5435..06d2665f189c88 100644 --- a/packages/components/src/mobile/picker/index.android.js +++ b/packages/components/src/mobile/picker/index.android.js @@ -62,7 +62,7 @@ export default class Picker extends Component { icon={ option.icon } leftAlign={ leftAlign } label={ option.label } - separatorType={ 'none' } + separatorType="none" onPress={ () => this.onCellPress( option.value ) } disabled={ option.disabled } style={ @@ -94,7 +94,7 @@ export default class Picker extends Component { ) } diff --git a/packages/components/src/mobile/segmented-control/index.native.js b/packages/components/src/mobile/segmented-control/index.native.js index d76a42d6d8c3b1..49d46b8cd08beb 100644 --- a/packages/components/src/mobile/segmented-control/index.native.js +++ b/packages/components/src/mobile/segmented-control/index.native.js @@ -173,7 +173,7 @@ const SegmentedControls = ( { accessibilityState={ { selected: activeSegmentIndex === index, } } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityLabel={ segment } accessibilityHint={ `${ index + 1 } on ${ segments.length diff --git a/packages/components/src/notice/stories/index.story.tsx b/packages/components/src/notice/stories/index.story.tsx index 16a68ab293f551..3314fc722af969 100644 --- a/packages/components/src/notice/stories/index.story.tsx +++ b/packages/components/src/notice/stories/index.story.tsx @@ -109,7 +109,7 @@ export const NoticeListSubcomponent: StoryFn< typeof NoticeList > = () => { return ( <> - + Reset Notices > diff --git a/packages/components/src/palette-edit/test/index.tsx b/packages/components/src/palette-edit/test/index.tsx index 43c1e5743ff2e9..a8c12e7fc6fcd0 100644 --- a/packages/components/src/palette-edit/test/index.tsx +++ b/packages/components/src/palette-edit/test/index.tsx @@ -154,7 +154,7 @@ describe( 'PaletteEdit', () => { render( ); diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx index 4e96bad1de23f5..9df0eca5880516 100644 --- a/packages/components/src/popover/index.tsx +++ b/packages/components/src/popover/index.tsx @@ -78,7 +78,7 @@ export const SLOT_NAME = 'Popover'; const ArrowTriangle = () => ( diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index 6694c1d30bdce8..f55373664efffa 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -15,7 +15,6 @@ import { } from './composite/v2'; import { default as CustomSelectControl } from './custom-select-control'; import { positionToPlacement as __experimentalPopoverLegacyPositionToPlacement } from './popover/utils'; -import { default as ProgressBar } from './progress-bar'; import { createPrivateSlotFill } from './slot-fill'; import { DropdownMenu as DropdownMenuV2, @@ -44,7 +43,6 @@ lock( privateApis, { __experimentalPopoverLegacyPositionToPlacement, createPrivateSlotFill, ComponentsContext, - ProgressBar, Tabs, Theme, DropdownMenuV2, diff --git a/packages/components/src/progress-bar/README.md b/packages/components/src/progress-bar/README.md index ad69ab54b098af..07bd1d6c12071b 100644 --- a/packages/components/src/progress-bar/README.md +++ b/packages/components/src/progress-bar/README.md @@ -1,13 +1,47 @@ # ProgressBar - -This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. - - A simple horizontal progress bar component. Supports two modes: determinate and indeterminate. A progress bar is determinate when a specific progress value has been specified (from 0 to 100), and indeterminate when a value hasn't been specified. +## Usage + +Basic usage: + +```jsx +import { ProgressBar } from '@wordpress/components'; + +const MyLoadingComponent = () => { + return ; +}; +``` + +You can also make it determinate by passing a `value` (from 0 to 100) representing the progress: + +```jsx +import { ProgressBar } from '@wordpress/components'; + +const MyLoadingComponent = ( { progress } ) => { + return ; +}; +``` + +You can customize its appearance by passing a custom CSS class name to `className`. + +```css +.my-custom-progress-bar { + width: 100%; +} +``` + +```jsx +import { ProgressBar } from '@wordpress/components'; + +const MyLoadingComponent = () => { + return ; +}; +``` + ### Props The component accepts the following props: @@ -23,7 +57,7 @@ If a `value` is not specified, the progress bar will be considered indeterminate A CSS class to apply to the underlying `div` element, serving as a progress bar track. -- Required: No +- Required: No #### Inherited props diff --git a/packages/components/src/progress-bar/index.tsx b/packages/components/src/progress-bar/index.tsx index d670d558ebd837..e6a34850954563 100644 --- a/packages/components/src/progress-bar/index.tsx +++ b/packages/components/src/progress-bar/index.tsx @@ -44,6 +44,21 @@ function UnforwardedProgressBar( ); } +/** + * A simple horizontal progress bar component. + * + * Supports two modes: determinate and indeterminate. A progress bar is determinate + * when a specific progress value has been specified (from 0 to 100), and indeterminate + * when a value hasn't been specified. + * + * ```jsx + * import { ProgressBar } from '@wordpress/components'; + * + * const MyLoadingComponent = () => { + * return ; + * }; + * ``` + */ export const ProgressBar = forwardRef( UnforwardedProgressBar ); export default ProgressBar; diff --git a/packages/components/src/progress-bar/stories/index.story.tsx b/packages/components/src/progress-bar/stories/index.story.tsx index d09111cf32f044..4396c4cc48eebb 100644 --- a/packages/components/src/progress-bar/stories/index.story.tsx +++ b/packages/components/src/progress-bar/stories/index.story.tsx @@ -10,11 +10,10 @@ import { ProgressBar } from '..'; const meta: Meta< typeof ProgressBar > = { component: ProgressBar, - title: 'Components (Experimental)/ProgressBar', + title: 'Components/ProgressBar', argTypes: { value: { control: { type: 'number', min: 0, max: 100, step: 1 } }, }, - tags: [ 'status-private' ], parameters: { controls: { expanded: true, @@ -30,3 +29,38 @@ const Template: StoryFn< typeof ProgressBar > = ( { ...args } ) => { export const Default: StoryFn< typeof ProgressBar > = Template.bind( {} ); Default.args = {}; + +const withCustomWidthCustomCSS = ` + .custom-progress-bar { + width: 100%; + } +`; + +/** + * A progress bar with a custom width. + * + * You can override the default `width` by passing a custom CSS class via the + * `className` prop. + * + * This example shows a progress bar with an overriden `width` of `100%` which + * makes it fit all available horizontal space of the parent element. The CSS + * class looks like this: + * + * ```css + * .custom-progress-bar { + * width: 100%; + * } + * ``` + */ +export const WithCustomWidth = Template.bind( {} ); +WithCustomWidth.args = { + className: 'custom-progress-bar', +}; +WithCustomWidth.decorators = [ + ( Story ) => ( + <> + + + > + ), +]; diff --git a/packages/components/src/progress-bar/styles.ts b/packages/components/src/progress-bar/styles.ts index f04002f458c0aa..0e91556b96b9d2 100644 --- a/packages/components/src/progress-bar/styles.ts +++ b/packages/components/src/progress-bar/styles.ts @@ -24,8 +24,6 @@ export const INDETERMINATE_TRACK_WIDTH = 50; export const Track = styled.div` position: relative; overflow: hidden; - width: 100%; - max-width: 160px; height: ${ CONFIG.borderWidthFocus }; /* Text color at 10% opacity */ background-color: color-mix( @@ -38,6 +36,10 @@ export const Track = styled.div` // Windows high contrast mode. outline: 2px solid transparent; outline-offset: 2px; + + :where( & ) { + width: 160px; + } `; export const Indicator = styled.div< { diff --git a/packages/components/src/search-control/index.native.js b/packages/components/src/search-control/index.native.js index 487364bb07e44d..8bff86049b0182 100644 --- a/packages/components/src/search-control/index.native.js +++ b/packages/components/src/search-control/index.native.js @@ -237,7 +237,7 @@ function SearchControl( { onPress={ onCancel } style={ cancelButtonTextStyle } accessible - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityLabel={ __( 'Cancel search' ) } accessibilityHint={ __( 'Cancel search' ) } > diff --git a/packages/components/src/snackbar/list.tsx b/packages/components/src/snackbar/list.tsx index ed6417fdead1c6..f97f8cc5db8945 100644 --- a/packages/components/src/snackbar/list.tsx +++ b/packages/components/src/snackbar/list.tsx @@ -87,9 +87,9 @@ export function SnackbarList( { return ( { children( selectedTab ) } diff --git a/packages/components/src/tabs/stories/index.story.tsx b/packages/components/src/tabs/stories/index.story.tsx index 16f8ac956ccf75..164b2cbb5ea9ba 100644 --- a/packages/components/src/tabs/stories/index.story.tsx +++ b/packages/components/src/tabs/stories/index.story.tsx @@ -216,7 +216,7 @@ const CloseButtonTemplate: StoryFn< typeof Tabs > = ( props ) => { Tab 3 = ( props ) => { ) : ( - setIsOpen( true ) } - > + setIsOpen( true ) }> Open Tabs ) } @@ -282,31 +279,29 @@ const ControlledModeTemplate: StoryFn< typeof Tabs > = ( props ) => { Selected tab: Tab 3 - { - - Select a tab: - setSelectedTabId( 'tab1' ), - title: 'Tab 1', - isActive: selectedTabId === 'tab1', - }, - { - onClick: () => setSelectedTabId( 'tab2' ), - title: 'Tab 2', - isActive: selectedTabId === 'tab2', - }, - { - onClick: () => setSelectedTabId( 'tab3' ), - title: 'Tab 3', - isActive: selectedTabId === 'tab3', - }, - ] } - label="Choose a tab. The power is yours." - /> - - } + + Select a tab: + setSelectedTabId( 'tab1' ), + title: 'Tab 1', + isActive: selectedTabId === 'tab1', + }, + { + onClick: () => setSelectedTabId( 'tab2' ), + title: 'Tab 2', + isActive: selectedTabId === 'tab2', + }, + { + onClick: () => setSelectedTabId( 'tab3' ), + title: 'Tab 3', + isActive: selectedTabId === 'tab3', + }, + ] } + label="Choose a tab. The power is yours." + /> + > ); }; diff --git a/packages/components/src/tools-panel/stories/index.story.tsx b/packages/components/src/tools-panel/stories/index.story.tsx index cd6f61c6e921bd..459932c9d22d7a 100644 --- a/packages/components/src/tools-panel/stories/index.story.tsx +++ b/packages/components/src/tools-panel/stories/index.story.tsx @@ -367,7 +367,7 @@ export const WithSlotFillItems: StoryFn< typeof ToolsPanel > = ( { label="Item for alternate panel" onDeselect={ () => undefined } resetAllFilter={ () => undefined } - panelId={ 'intended-for-another-panel-via-shared-slot' } + panelId="intended-for-another-panel-via-shared-slot" > This panel item will not be displayed in the demo as its diff --git a/packages/components/src/tools-panel/tools-panel-header/component.tsx b/packages/components/src/tools-panel/tools-panel-header/component.tsx index b08981f94949a6..a30a8aa1c2f4b4 100644 --- a/packages/components/src/tools-panel/tools-panel-header/component.tsx +++ b/packages/components/src/tools-panel/tools-panel-header/component.tsx @@ -224,7 +224,7 @@ const ToolsPanelHeader = ( // @ts-expect-error - TODO: If this "tertiary" style is something we really want to allow on MenuItem, // we should rename it and explicitly allow it as an official API. All the other Button variants // don't make sense in a MenuItem context, and should be disallowed. - variant={ 'tertiary' } + variant="tertiary" onClick={ () => { if ( canResetAll ) { resetAll(); diff --git a/packages/components/src/unit-control/test/index.tsx b/packages/components/src/unit-control/test/index.tsx index 777004a6e8ae27..d91498d46478b3 100644 --- a/packages/components/src/unit-control/test/index.tsx +++ b/packages/components/src/unit-control/test/index.tsx @@ -138,7 +138,7 @@ describe( 'UnitControl', () => { const user = userEvent.setup(); const onChangeSpy = jest.fn(); - render( ); + render( ); const input = getInput(); await user.clear( input ); @@ -159,7 +159,7 @@ describe( 'UnitControl', () => { const user = userEvent.setup(); const onChangeSpy = jest.fn(); - render( ); + render( ); const input = getInput(); await user.type( input, '{ArrowUp}' ); @@ -175,7 +175,7 @@ describe( 'UnitControl', () => { const user = userEvent.setup(); const onChangeSpy = jest.fn(); - render( ); + render( ); const input = getInput(); await user.type( input, '{Shift>}{ArrowUp}{/Shift}' ); @@ -253,7 +253,7 @@ describe( 'UnitControl', () => { render( @@ -282,7 +282,7 @@ describe( 'UnitControl', () => { render( @@ -348,7 +348,7 @@ describe( 'UnitControl', () => { render( @@ -468,7 +468,7 @@ describe( 'UnitControl', () => { render( @@ -570,19 +570,19 @@ describe( 'UnitControl', () => { describe( 'Unit Parser', () => { it( 'should update unit after initial render and with new unit prop', async () => { - const { rerender } = render( ); + const { rerender } = render( ); const select = getSelect(); expect( select.value ).toBe( '%' ); - rerender( ); + rerender( ); expect( select.value ).toBe( 'vh' ); } ); it( 'should fallback to default unit if parsed unit is invalid', () => { - render( ); + render( ); expect( getSelect().value ).toBe( 'px' ); } ); @@ -590,7 +590,7 @@ describe( 'UnitControl', () => { it( 'should display valid CSS unit when not explicitly included in units list', () => { render( { render( diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md index 3ebf8f4b32e33b..702df49b9a28bb 100644 --- a/packages/compose/CHANGELOG.md +++ b/packages/compose/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/compose/README.md b/packages/compose/README.md index c60d202f59aacf..f4f0631ba36a51 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -69,7 +69,7 @@ This is inspired by `lodash`'s `flowRight` function. _Related_ -- +- ### createHigherOrderComponent @@ -145,7 +145,7 @@ This is inspired by `lodash`'s `flow` function. _Related_ -- +- ### pure @@ -247,7 +247,7 @@ Debounces a function similar to Lodash's `debounce`. A new debounced function wi _Related_ -- +- _Parameters_ @@ -535,7 +535,7 @@ Throttles a function similar to Lodash's `throttle`. A new throttled function wi _Related_ -- +- _Parameters_ diff --git a/packages/compose/package.json b/packages/compose/package.json index 0a3308e7adb0ac..7568f2f26f08dc 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "6.35.0", + "version": "7.0.0", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/compose/src/higher-order/compose.ts b/packages/compose/src/higher-order/compose.ts index 2b7b598c70c958..1fcaa55eb4512b 100644 --- a/packages/compose/src/higher-order/compose.ts +++ b/packages/compose/src/higher-order/compose.ts @@ -9,7 +9,7 @@ import { basePipe } from './pipe'; * * This is inspired by `lodash`'s `flowRight` function. * - * @see https://docs-lodash.com/v4/flow-right/ + * @see https://lodash.com/docs/4#flow-right */ const compose = basePipe( true ); diff --git a/packages/compose/src/higher-order/pipe.ts b/packages/compose/src/higher-order/pipe.ts index ced7618ad81bfd..29003d818ccc3a 100644 --- a/packages/compose/src/higher-order/pipe.ts +++ b/packages/compose/src/higher-order/pipe.ts @@ -43,7 +43,7 @@ * * Allows to choose whether to perform left-to-right or right-to-left composition. * - * @see https://docs-lodash.com/v4/flow/ + * @see https://lodash.com/docs/4#flow * * @param {boolean} reverse True if right-to-left, false for left-to-right composition. */ @@ -67,7 +67,7 @@ const basePipe = * * This is inspired by `lodash`'s `flow` function. * - * @see https://docs-lodash.com/v4/flow/ + * @see https://lodash.com/docs/4#flow */ const pipe = basePipe(); diff --git a/packages/compose/src/hooks/use-debounce/index.js b/packages/compose/src/hooks/use-debounce/index.js index 5d48dba91bde6e..6da42f159a8640 100644 --- a/packages/compose/src/hooks/use-debounce/index.js +++ b/packages/compose/src/hooks/use-debounce/index.js @@ -19,7 +19,7 @@ import { debounce } from '../../utils/debounce'; * including the function to debounce, so please wrap functions created on * render in components in `useCallback`. * - * @see https://docs-lodash.com/v4/debounce/ + * @see https://lodash.com/docs/4#debounce * * @template {(...args: any[]) => void} TFunc * diff --git a/packages/compose/src/hooks/use-throttle/index.js b/packages/compose/src/hooks/use-throttle/index.js index 8cade9a8442ac8..4bf6cc6e85555a 100644 --- a/packages/compose/src/hooks/use-throttle/index.js +++ b/packages/compose/src/hooks/use-throttle/index.js @@ -19,7 +19,7 @@ import { throttle } from '../../utils/throttle'; * including the function to throttle, so please wrap functions created on * render in components in `useCallback`. * - * @see https://docs-lodash.com/v4/throttle/ + * @see https://lodash.com/docs/4#throttle * * @template {(...args: any[]) => void} TFunc * diff --git a/packages/core-commands/CHANGELOG.md b/packages/core-commands/CHANGELOG.md index 528201ee2c5664..b05dda0cca2b23 100644 --- a/packages/core-commands/CHANGELOG.md +++ b/packages/core-commands/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json index 3efea9abf58e48..f79adf41f48984 100644 --- a/packages/core-commands/package.json +++ b/packages/core-commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-commands", - "version": "0.27.0", + "version": "1.0.0", "description": "WordPress core reusable commands.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index bce1d9e9d520bf..810e8ab00d21c9 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/core-data/package.json b/packages/core-data/package.json index ac77ad911becbe..984de2f7628ec4 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "6.35.0", + "version": "7.0.0", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index be4d12f0cb9ef6..36b0ba5f84c9d1 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -598,10 +598,14 @@ export const saveEntityRecord = return acc; }, { + // Do not update the `status` if we have edited it when auto saving. + // It's very important to let the user explicitly save this change, + // because it can lead to unexpected results. An example would be to + // have a draft post and change the status to publish. status: data.status === 'auto-draft' ? 'draft' - : data.status, + : undefined, } ); updatedRecord = await __unstableFetch( { diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md index a3e1cba44c0881..223f5de7796c62 100644 --- a/packages/create-block-interactive-template/CHANGELOG.md +++ b/packages/create-block-interactive-template/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/create-block-interactive-template/package.json b/packages/create-block-interactive-template/package.json index 337b950b0ac499..0e11939d484025 100644 --- a/packages/create-block-interactive-template/package.json +++ b/packages/create-block-interactive-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-interactive-template", - "version": "1.21.0", + "version": "2.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", diff --git a/packages/create-block-tutorial-template/CHANGELOG.md b/packages/create-block-tutorial-template/CHANGELOG.md index 125c54356e63bd..00537097ff945a 100644 --- a/packages/create-block-tutorial-template/CHANGELOG.md +++ b/packages/create-block-tutorial-template/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json index e03355e6e6a2a4..1b2c920457641d 100644 --- a/packages/create-block-tutorial-template/package.json +++ b/packages/create-block-tutorial-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-tutorial-template", - "version": "3.12.0", + "version": "4.0.0", "description": "This is a template for @wordpress/create-block that creates an example 'Copyright Date' block. This block is used in the official WordPress block development Quick Start Guide.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md index 409c2fbcc8ec50..0362b2a6f13412 100644 --- a/packages/create-block/CHANGELOG.md +++ b/packages/create-block/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.43.0 (2024-05-31) + ## 4.42.0 (2024-05-16) ## 4.41.0 (2024-05-02) diff --git a/packages/create-block/package.json b/packages/create-block/package.json index 8771a1064f3288..00acf80f0ac9d1 100644 --- a/packages/create-block/package.json +++ b/packages/create-block/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block", - "version": "4.42.0", + "version": "4.43.0", "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/customize-widgets/CHANGELOG.md b/packages/customize-widgets/CHANGELOG.md index 499363d1920ef2..aec25853b2914b 100644 --- a/packages/customize-widgets/CHANGELOG.md +++ b/packages/customize-widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index c3fe2dbd5e0bbf..ec320eee22a43a 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/customize-widgets", - "version": "4.35.0", + "version": "5.0.0", "description": "Widgets blocks in Customizer Module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data-controls/CHANGELOG.md b/packages/data-controls/CHANGELOG.md index f90608d4d8c0cd..6b94332e70db8b 100644 --- a/packages/data-controls/CHANGELOG.md +++ b/packages/data-controls/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index f4e1f9beda2a98..88fa9ae4faf264 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "3.27.0", + "version": "4.0.0", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 200d37efa904d9..0e72225f04c80e 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 10.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/data/package.json b/packages/data/package.json index b289f599fad018..3246870cbe52fb 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "9.28.0", + "version": "10.0.0", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index 1b772a002ff7be..8b3b3656258c9e 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Legacy support for `in` and `notIn` operators introduced in 0.8 .0 has been removed and they no longer work. Please, convert them to `is` and `isNot` respectively. diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 5fb67307954014..723441e0e8b81f 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dataviews", - "version": "1.2.0", + "version": "2.0.0", "description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dataviews/src/bulk-actions-toolbar.tsx b/packages/dataviews/src/bulk-actions-toolbar.tsx index 38e1f799491866..3cce31c5139047 100644 --- a/packages/dataviews/src/bulk-actions-toolbar.tsx +++ b/packages/dataviews/src/bulk-actions-toolbar.tsx @@ -250,9 +250,9 @@ export default function BulkActionsToolbar< Item extends AnyItem >( { diff --git a/packages/dataviews/src/pagination.tsx b/packages/dataviews/src/pagination.tsx index da37eb99313799..4020e78525b984 100644 --- a/packages/dataviews/src/pagination.tsx +++ b/packages/dataviews/src/pagination.tsx @@ -74,7 +74,7 @@ const Pagination = memo( function Pagination( { page: +newValue, } ); } } - size={ 'compact' } + size="compact" __nextHasNoMarginBottom /> ), diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index 66ba61147bc882..bfff3786b1112c 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -119,7 +119,8 @@ th:first-child { padding-left: $grid-unit-60; - .dataviews-view-table-header-button { + .dataviews-view-table-header-button, + .dataviews-view-table-header { margin-left: - #{$grid-unit-10}; } } @@ -235,6 +236,10 @@ } } + .dataviews-view-table-header { + padding-left: $grid-unit-05; + } + .dataviews-view-table__actions-column { width: 1%; } diff --git a/packages/dataviews/src/view-grid.tsx b/packages/dataviews/src/view-grid.tsx index 5c6b8ba5bc3b50..77ac3c92738523 100644 --- a/packages/dataviews/src/view-grid.tsx +++ b/packages/dataviews/src/view-grid.tsx @@ -131,7 +131,7 @@ function GridItem< Item extends AnyItem >( { return ( { renderedValue } diff --git a/packages/dataviews/src/view-table.tsx b/packages/dataviews/src/view-table.tsx index dcfb8a67bf3636..075a60fccd129c 100644 --- a/packages/dataviews/src/view-table.tsx +++ b/packages/dataviews/src/view-table.tsx @@ -15,7 +15,6 @@ import { privateApis as componentsPrivateApis, CheckboxControl, Spinner, - VisuallyHidden, } from '@wordpress/components'; import { forwardRef, @@ -549,9 +548,9 @@ function ViewTable< Item extends AnyItem >( { data-field-id="actions" className="dataviews-view-table__actions-column" > - + { __( 'Actions' ) } - + ) } diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md index 0bdf48871145b6..b66b2983b20be8 100644 --- a/packages/date/CHANGELOG.md +++ b/packages/date/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/date/package.json b/packages/date/package.json index a3790d910e3b2d..23c66ba25823d0 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "4.58.0", + "version": "5.0.0", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 27d099308df2ad..32c012ea3b6447 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index 3ee7f0bdb7deac..a551439c6caeb8 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "5.9.0", + "version": "6.0.0", "description": "Extract WordPress script dependencies from webpack bundles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/deprecated/CHANGELOG.md b/packages/deprecated/CHANGELOG.md index a38f511de523dc..99322e16983dc3 100644 --- a/packages/deprecated/CHANGELOG.md +++ b/packages/deprecated/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 656787a875e66b..4ef4b237980024 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "3.58.0", + "version": "4.0.0", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/docgen/CHANGELOG.md b/packages/docgen/CHANGELOG.md index 492b7035e959c5..daffb59b72f5e9 100644 --- a/packages/docgen/CHANGELOG.md +++ b/packages/docgen/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 0582121d16d74b..832b0fb6a2a2aa 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/docgen", - "version": "1.67.0", + "version": "2.0.0", "description": "Autogenerate public API documentation from exports and JSDoc comments.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom-ready/CHANGELOG.md b/packages/dom-ready/CHANGELOG.md index 67d4e465a65edc..b83d377c11db03 100644 --- a/packages/dom-ready/CHANGELOG.md +++ b/packages/dom-ready/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 54f4842c2d93e2..bc189f6e387b98 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "3.58.0", + "version": "4.0.0", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md index dc504c46551367..878205fae89e90 100644 --- a/packages/dom/CHANGELOG.md +++ b/packages/dom/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/dom/package.json b/packages/dom/package.json index 9b8c0b8ded4ae1..e059d757b2c5ca 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "3.58.0", + "version": "4.0.0", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils-playwright/CHANGELOG.md b/packages/e2e-test-utils-playwright/CHANGELOG.md index f327489308b648..4fe11b5ea95014 100644 --- a/packages/e2e-test-utils-playwright/CHANGELOG.md +++ b/packages/e2e-test-utils-playwright/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json index 5cee2f8ee1b30d..c630ac1869c7f9 100644 --- a/packages/e2e-test-utils-playwright/package.json +++ b/packages/e2e-test-utils-playwright/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils-playwright", - "version": "0.26.0", + "version": "1.0.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md index cb853af4cdb213..696b6e56b02dd1 100644 --- a/packages/e2e-test-utils/CHANGELOG.md +++ b/packages/e2e-test-utils/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 11.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index 1faa1848272ab2..d791e6e37d775f 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "10.29.0", + "version": "11.0.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/CHANGELOG.md b/packages/e2e-tests/CHANGELOG.md index c377e0db8a79fc..7dcde1e1cfefef 100644 --- a/packages/e2e-tests/CHANGELOG.md +++ b/packages/e2e-tests/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index d678f568a92a5d..312a60d670c66a 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "7.29.0", + "version": "8.0.0", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/plugins/interactive-blocks.php b/packages/e2e-tests/plugins/interactive-blocks.php index ab2f85156e3b3e..54bd4325bb29b1 100644 --- a/packages/e2e-tests/plugins/interactive-blocks.php +++ b/packages/e2e-tests/plugins/interactive-blocks.php @@ -29,11 +29,7 @@ function () { // Ensure the interactivity API is loaded. wp_interactivity(); // But remove the server directive processing. - remove_filter( - 'render_block_data', - 'wp_interactivity_process_directives_of_interactive_blocks', - 100 - ); + add_filter( 'interactivity_process_directives', '__return_false' ); } } ); diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index 7e6b861f8183ba..3934ab8cda8848 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index a968caadb3191d..b3327218dfc873 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "7.35.0", + "version": "8.0.0", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/src/commands/use-commands.js b/packages/edit-post/src/commands/use-commands.js new file mode 100644 index 00000000000000..edd2a6a5b84042 --- /dev/null +++ b/packages/edit-post/src/commands/use-commands.js @@ -0,0 +1,48 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { fullscreen } from '@wordpress/icons'; +import { useCommand } from '@wordpress/commands'; +import { store as preferencesStore } from '@wordpress/preferences'; +import { store as noticesStore } from '@wordpress/notices'; + +export default function useCommands() { + const { isFullscreen } = useSelect( ( select ) => { + const { get } = select( preferencesStore ); + + return { + isFullscreen: get( 'core/edit-post', 'fullscreenMode' ), + }; + }, [] ); + const { toggle } = useDispatch( preferencesStore ); + const { createInfoNotice } = useDispatch( noticesStore ); + + useCommand( { + name: 'core/toggle-fullscreen-mode', + label: isFullscreen + ? __( 'Exit fullscreen' ) + : __( 'Enter fullscreen' ), + icon: fullscreen, + callback: ( { close } ) => { + toggle( 'core/edit-post', 'fullscreenMode' ); + close(); + createInfoNotice( + isFullscreen ? __( 'Fullscreen off.' ) : __( 'Fullscreen on.' ), + { + id: 'core/edit-post/toggle-fullscreen-mode/notice', + type: 'snackbar', + actions: [ + { + label: __( 'Undo' ), + onClick: () => { + toggle( 'core/edit-post', 'fullscreenMode' ); + }, + }, + ], + } + ); + }, + } ); +} diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js b/packages/edit-post/src/components/back-button/fullscreen-mode-close.js similarity index 97% rename from packages/edit-post/src/components/header/fullscreen-mode-close/index.js rename to packages/edit-post/src/components/back-button/fullscreen-mode-close.js index 839802f380e1f3..59694de16cb925 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js +++ b/packages/edit-post/src/components/back-button/fullscreen-mode-close.js @@ -22,7 +22,7 @@ import { useReducedMotion } from '@wordpress/compose'; /** * Internal dependencies */ -import { store as editPostStore } from '../../../store'; +import { store as editPostStore } from '../../store'; function FullscreenModeClose( { showTooltip, icon, href, initialPost } ) { const { isActive, isRequestingSiteIcon, postType, siteIconUrl } = useSelect( diff --git a/packages/edit-post/src/components/back-button/index.js b/packages/edit-post/src/components/back-button/index.js new file mode 100644 index 00000000000000..ecce4623cea493 --- /dev/null +++ b/packages/edit-post/src/components/back-button/index.js @@ -0,0 +1,34 @@ +/** + * WordPress dependencies + */ +import { privateApis as editorPrivateApis } from '@wordpress/editor'; +import { __unstableMotion as motion } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import FullscreenModeClose from './fullscreen-mode-close'; +import { unlock } from '../../lock-unlock'; + +const { BackButton: BackButtonFill } = unlock( editorPrivateApis ); + +const slideX = { + hidden: { x: '-100%' }, + distractionFreeInactive: { x: 0 }, + hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, +}; + +function BackButton( { initialPost } ) { + return ( + + + + + + ); +} + +export default BackButton; diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss b/packages/edit-post/src/components/back-button/style.scss similarity index 87% rename from packages/edit-post/src/components/header/fullscreen-mode-close/style.scss rename to packages/edit-post/src/components/back-button/style.scss index 911445344febcd..d6455c707fb1d9 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss +++ b/packages/edit-post/src/components/back-button/style.scss @@ -14,10 +14,9 @@ background: $gray-900; color: $white; border-radius: 0; - height: $header-height + $border-width; + height: $header-height; width: $header-height; position: relative; - margin-bottom: - $border-width; &:active { color: $white; @@ -65,3 +64,18 @@ margin-top: -($border-width); } } + +/** + * Show icon label overrides. + */ +.show-icon-labels .editor-header { + .edit-post-fullscreen-mode-close.has-icon { + width: $header-height; + svg { + display: block; + } + &::after { + content: none; + } + } +} diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/back-button/test/__snapshots__/fullscreen-mode-close.js.snap similarity index 100% rename from packages/edit-post/src/components/header/fullscreen-mode-close/test/__snapshots__/index.js.snap rename to packages/edit-post/src/components/back-button/test/__snapshots__/fullscreen-mode-close.js.snap diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/test/index.js b/packages/edit-post/src/components/back-button/test/fullscreen-mode-close.js similarity index 97% rename from packages/edit-post/src/components/header/fullscreen-mode-close/test/index.js rename to packages/edit-post/src/components/back-button/test/fullscreen-mode-close.js index b66d548045ad42..4a549370d0f056 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/test/index.js +++ b/packages/edit-post/src/components/back-button/test/fullscreen-mode-close.js @@ -11,7 +11,7 @@ import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import FullscreenModeClose from '../'; +import FullscreenModeClose from '../fullscreen-mode-close'; jest.mock( '@wordpress/data/src/components/use-select', () => { // This allows us to tweak the returned value on each test. diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js deleted file mode 100644 index 311279292d8f6a..00000000000000 --- a/packages/edit-post/src/components/header/index.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * WordPress dependencies - */ -import { privateApis as editorPrivateApis } from '@wordpress/editor'; -import { useSelect } from '@wordpress/data'; -import { __unstableMotion as motion } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import FullscreenModeClose from './fullscreen-mode-close'; -import PostEditorMoreMenu from './more-menu'; -import MainDashboardButton from './main-dashboard-button'; -import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -const { Header: EditorHeader } = unlock( editorPrivateApis ); - -const slideX = { - hidden: { x: '-100%' }, - distractionFreeInactive: { x: 0 }, - hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, -}; - -function Header( { setEntitiesSavedStatesCallback, initialPost } ) { - const { hasActiveMetaboxes } = useSelect( ( select ) => { - return { - hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - }; - }, [] ); - - return ( - - - - - - - - - ); -} - -export default Header; diff --git a/packages/edit-post/src/components/header/style.scss b/packages/edit-post/src/components/header/style.scss deleted file mode 100644 index 53672eb09e701f..00000000000000 --- a/packages/edit-post/src/components/header/style.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Show icon label overrides. - */ -.show-icon-labels .editor-header { - .edit-post-fullscreen-mode-close.has-icon { - width: $header-height; - svg { - display: block; - } - &::after { - content: none; - } - } -} diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index fde9319a348daf..6603b1f6851522 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -10,7 +10,6 @@ import { AutosaveMonitor, LocalAutosaveMonitor, UnsavedChangesWarning, - EditorNotices, EditorKeyboardShortcutsRegister, EditorSnackbars, store as editorStore, @@ -18,16 +17,13 @@ import { } from '@wordpress/editor'; import { useSelect, useDispatch } from '@wordpress/data'; import { - BlockBreadcrumb, - BlockToolbar, privateApis as blockEditorPrivateApis, store as blockEditorStore, } from '@wordpress/block-editor'; import { useViewportMatch } from '@wordpress/compose'; import { PluginArea } from '@wordpress/plugins'; -import { __, _x, sprintf } from '@wordpress/i18n'; -import { useState, useCallback, useMemo } from '@wordpress/element'; -import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { __, sprintf } from '@wordpress/i18n'; +import { useCallback, useMemo } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { store as preferencesStore } from '@wordpress/preferences'; import { privateApis as commandsPrivateApis } from '@wordpress/commands'; @@ -38,55 +34,54 @@ import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies */ -import VisualEditor from '../visual-editor'; +import BackButton from '../back-button'; import EditPostKeyboardShortcuts from '../keyboard-shortcuts'; import InitPatternModal from '../init-pattern-modal'; import BrowserURL from '../browser-url'; -import Header from '../header'; import MetaBoxes from '../meta-boxes'; +import PostEditorMoreMenu from '../more-menu'; import WelcomeGuide from '../welcome-guide'; import { store as editPostStore } from '../../store'; import { unlock } from '../../lock-unlock'; -import useCommonCommands from '../../hooks/commands/use-common-commands'; +import useEditPostCommands from '../../commands/use-commands'; +import { usePaddingAppender } from './use-padding-appender'; +import { useShouldIframe } from './use-should-iframe'; const { getLayoutStyles } = unlock( blockEditorPrivateApis ); const { useCommands } = unlock( coreCommandsPrivateApis ); const { useCommandContext } = unlock( commandsPrivateApis ); -const { - InserterSidebar, - ListViewSidebar, - ComplementaryArea, - FullscreenMode, - SavePublishPanels, - InterfaceSkeleton, - interfaceStore, - Sidebar, - TextEditor, -} = unlock( editorPrivateApis ); +const { EditorInterface, FullscreenMode, Sidebar } = + unlock( editorPrivateApis ); const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis ); - -const interfaceLabels = { - /* translators: accessibility text for the editor top bar landmark region. */ - header: __( 'Editor top bar' ), - /* translators: accessibility text for the editor content landmark region. */ - body: __( 'Editor content' ), - /* translators: accessibility text for the editor settings landmark region. */ - sidebar: __( 'Editor settings' ), - /* translators: accessibility text for the editor publish landmark region. */ - actions: __( 'Editor publish' ), - /* translators: accessibility text for the editor footer landmark region. */ - footer: __( 'Editor footer' ), -}; +const DESIGN_POST_TYPES = [ + 'wp_template', + 'wp_template_part', + 'wp_block', + 'wp_navigation', +]; function useEditorStyles() { - const { hasThemeStyleSupport, editorSettings } = useSelect( - ( select ) => ( { + const { + hasThemeStyleSupport, + editorSettings, + isZoomedOutView, + hasMetaBoxes, + renderingMode, + postType, + } = useSelect( ( select ) => { + const { __unstableGetEditorMode } = select( blockEditorStore ); + const { getCurrentPostType, getRenderingMode } = select( editorStore ); + const _postType = getCurrentPostType(); + return { hasThemeStyleSupport: select( editPostStore ).isFeatureActive( 'themeStyles' ), editorSettings: select( editorStore ).getEditorSettings(), - } ), - [] - ); + isZoomedOutView: __unstableGetEditorMode() === 'zoom-out', + hasMetaBoxes: select( editPostStore ).hasMetaBoxes(), + renderingMode: getRenderingMode(), + postType: _postType, + }; + }, [] ); // Compute the default styles. return useMemo( () => { @@ -120,7 +115,24 @@ function useEditorStyles() { } ); } - return hasThemeStyles ? editorSettings.styles : defaultEditorStyles; + const baseStyles = hasThemeStyles + ? editorSettings.styles ?? [] + : defaultEditorStyles; + + // Add a constant padding for the typewritter effect. When typing at the + // bottom, there needs to be room to scroll up. + if ( + ! isZoomedOutView && + ! hasMetaBoxes && + renderingMode === 'post-only' && + ! DESIGN_POST_TYPES.includes( postType ) + ) { + baseStyles.push( { + css: 'body{padding-bottom: 40vh}', + } ); + } + + return baseStyles; }, [ editorSettings.defaultEditorStyles, editorSettings.disableLayoutStyles, @@ -131,73 +143,45 @@ function useEditorStyles() { function Layout( { initialPost } ) { useCommands(); - useCommonCommands(); - - const isMobileViewport = useViewportMatch( 'medium', '<' ); + useEditPostCommands(); const isWideViewport = useViewportMatch( 'large' ); - const isLargeViewport = useViewportMatch( 'medium' ); - - const { closeGeneralSidebar } = useDispatch( editPostStore ); + const paddingAppenderRef = usePaddingAppender(); + const shouldIframe = useShouldIframe(); const { createErrorNotice } = useDispatch( noticesStore ); const { mode, isFullscreenActive, - isRichEditingEnabled, sidebarIsOpened, hasActiveMetaboxes, - previousShortcut, - nextShortcut, hasBlockSelected, - isInserterOpened, - isListViewOpened, showIconLabels, isDistractionFree, showBlockBreadcrumbs, showMetaBoxes, - documentLabel, hasHistory, - hasBlockBreadcrumbs, - blockEditorMode, isEditingTemplate, + isWelcomeGuideVisible, } = useSelect( ( select ) => { const { get } = select( preferencesStore ); - const { getEditorSettings, getPostTypeLabel } = select( editorStore ); - const editorSettings = getEditorSettings(); - const postTypeLabel = getPostTypeLabel(); + const { getEditorSettings } = select( editorStore ); + const { isFeatureActive } = select( editPostStore ); return { - showMetaBoxes: - select( editorStore ).getRenderingMode() === 'post-only', - sidebarIsOpened: !! ( - select( interfaceStore ).getActiveComplementaryArea( 'core' ) || - select( editorStore ).isPublishSidebarOpened() - ), + mode: select( editorStore ).getEditorMode(), isFullscreenActive: select( editPostStore ).isFeatureActive( 'fullscreenMode' ), - isInserterOpened: select( editorStore ).isInserterOpened(), - isListViewOpened: select( editorStore ).isListViewOpened(), - mode: select( editorStore ).getEditorMode(), - isRichEditingEnabled: editorSettings.richEditingEnabled, hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - previousShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( 'core/editor/previous-region' ), - nextShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( 'core/editor/next-region' ), + hasBlockSelected: + !! select( blockEditorStore ).getBlockSelectionStart(), showIconLabels: get( 'core', 'showIconLabels' ), isDistractionFree: get( 'core', 'distractionFree' ), showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), - // translators: Default label for the Document in the Block Breadcrumb. - documentLabel: postTypeLabel || _x( 'Document', 'noun' ), - hasBlockSelected: - !! select( blockEditorStore ).getBlockSelectionStart(), + showMetaBoxes: + select( editorStore ).getRenderingMode() === 'post-only', hasHistory: !! getEditorSettings().onNavigateToPreviousEntityRecord, - hasBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), - blockEditorMode: - select( blockEditorStore ).__unstableGetEditorMode(), isEditingTemplate: select( editorStore ).getCurrentPostType() === 'wp_template', + isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ), }; }, [] ); @@ -209,21 +193,6 @@ function Layout( { initialPost } ) { const styles = useEditorStyles(); - // Local state for save panel. - // Note 'truthy' callback implies an open panel. - const [ entitiesSavedStatesCallback, setEntitiesSavedStatesCallback ] = - useState( false ); - - const closeEntitiesSavedStates = useCallback( - ( arg ) => { - if ( typeof entitiesSavedStatesCallback === 'function' ) { - entitiesSavedStatesCallback( arg ); - } - setEntitiesSavedStatesCallback( false ); - }, - [ entitiesSavedStatesCallback ] - ); - // We need to add the show-icon-labels class to the body element so it is applied to modals. if ( showIconLabels ) { document.body.classList.add( 'show-icon-labels' ); @@ -235,31 +204,10 @@ function Layout( { initialPost } ) { 'is-sidebar-opened': sidebarIsOpened, 'has-metaboxes': hasActiveMetaboxes, 'is-distraction-free': isDistractionFree && isWideViewport, - 'is-entity-save-view-open': !! entitiesSavedStatesCallback, 'has-block-breadcrumbs': - hasBlockBreadcrumbs && ! isDistractionFree && isWideViewport, + showBlockBreadcrumbs && ! isDistractionFree && isWideViewport, } ); - const secondarySidebarLabel = isListViewOpened - ? __( 'Document Overview' ) - : __( 'Block Library' ); - - const secondarySidebar = () => { - if ( mode === 'visual' && isInserterOpened ) { - return ( - - ); - } - if ( mode === 'visual' && isListViewOpened ) { - return ; - } - - return null; - }; - function onPluginAreaError( name ) { createErrorNotice( sprintf( @@ -334,79 +282,6 @@ function Layout( { initialPost } ) { - - - } - editorNotices={ } - secondarySidebar={ secondarySidebar() } - sidebar={ - ! isDistractionFree && ( - - ) - } - notices={ } - content={ - <> - { ! isDistractionFree && } - { ( mode === 'text' || ! isRichEditingEnabled ) && ( - - ) } - { ! isLargeViewport && mode === 'visual' && ( - - ) } - { isRichEditingEnabled && mode === 'visual' && ( - - ) } - { ! isDistractionFree && showMetaBoxes && ( - - - - - ) } - > - } - footer={ - ! isDistractionFree && - ! isMobileViewport && - showBlockBreadcrumbs && - isRichEditingEnabled && - blockEditorMode !== 'zoom-out' && - mode === 'visual' && ( - - - - ) - } - actions={ - - } - shortcuts={ { - previous: previousShortcut, - next: nextShortcut, - } } - /> @@ -418,6 +293,26 @@ function Layout( { initialPost } ) { } /> ) } + + + + + { ! isDistractionFree && showMetaBoxes && ( + + + + + ) } + > ); } diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index bb0ce2e38732ea..2c5a035cad1447 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -1,4 +1,4 @@ -.edit-post-visual-editor { +.editor-visual-editor { flex: 1 0 auto; height: auto; } @@ -9,25 +9,12 @@ } // Adjust the position of the notices -.edit-post-layout .components-editor-notices__snackbar { +.components-editor-notices__snackbar { position: fixed; right: 0; - bottom: 16px; - padding-left: 16px; - padding-right: 16px; -} - -.is-distraction-free { - .components-editor-notices__snackbar { - bottom: 16px; - } -} - -// Adjust the position of the notices when breadcrumbs are present -.has-block-breadcrumbs { - .components-editor-notices__snackbar { - bottom: 40px; - } + bottom: 24px; + padding-left: 24px; + padding-right: 24px; } @include editor-left(".edit-post-layout .components-editor-notices__snackbar"); diff --git a/packages/edit-post/src/components/visual-editor/use-padding-appender.js b/packages/edit-post/src/components/layout/use-padding-appender.js similarity index 100% rename from packages/edit-post/src/components/visual-editor/use-padding-appender.js rename to packages/edit-post/src/components/layout/use-padding-appender.js diff --git a/packages/edit-post/src/components/layout/use-should-iframe.js b/packages/edit-post/src/components/layout/use-should-iframe.js new file mode 100644 index 00000000000000..03efae92c72f7d --- /dev/null +++ b/packages/edit-post/src/components/layout/use-should-iframe.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { store as editorStore } from '@wordpress/editor'; +import { useSelect } from '@wordpress/data'; +import { store as blocksStore } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + +const isGutenbergPlugin = globalThis.IS_GUTENBERG_PLUGIN ? true : false; + +export function useShouldIframe() { + const { + isBlockBasedTheme, + hasV3BlocksOnly, + isEditingTemplate, + hasMetaBoxes, + } = useSelect( ( select ) => { + const { getEditorSettings, getCurrentPostType } = select( editorStore ); + const { getBlockTypes } = select( blocksStore ); + const editorSettings = getEditorSettings(); + return { + isBlockBasedTheme: editorSettings.__unstableIsBlockBasedTheme, + hasV3BlocksOnly: getBlockTypes().every( ( type ) => { + return type.apiVersion >= 3; + } ), + isEditingTemplate: getCurrentPostType() === 'wp_template', + hasMetaBoxes: select( editPostStore ).hasMetaBoxes(), + }; + }, [] ); + + return ( + ( ( hasV3BlocksOnly || ( isGutenbergPlugin && isBlockBasedTheme ) ) && + ! hasMetaBoxes ) || + isEditingTemplate + ); +} diff --git a/packages/edit-post/src/components/header/more-menu/index.js b/packages/edit-post/src/components/more-menu/index.js similarity index 92% rename from packages/edit-post/src/components/header/more-menu/index.js rename to packages/edit-post/src/components/more-menu/index.js index c9f1c50251ca21..136742a19dddd4 100644 --- a/packages/edit-post/src/components/header/more-menu/index.js +++ b/packages/edit-post/src/components/more-menu/index.js @@ -10,10 +10,10 @@ import { displayShortcut } from '@wordpress/keycodes'; /** * Internal dependencies */ -import { unlock } from '../../../lock-unlock'; +import { unlock } from '../../lock-unlock'; import ManagePatternsMenuItem from './manage-patterns-menu-item'; import WelcomeGuideMenuItem from './welcome-guide-menu-item'; -import EditPostPreferencesModal from '../../preferences-modal'; +import EditPostPreferencesModal from '../preferences-modal'; const { ToolsMoreMenuGroup, ViewMoreMenuGroup } = unlock( editorPrivateApis ); diff --git a/packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js b/packages/edit-post/src/components/more-menu/manage-patterns-menu-item.js similarity index 100% rename from packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js rename to packages/edit-post/src/components/more-menu/manage-patterns-menu-item.js diff --git a/packages/edit-post/src/components/header/more-menu/welcome-guide-menu-item.js b/packages/edit-post/src/components/more-menu/welcome-guide-menu-item.js similarity index 100% rename from packages/edit-post/src/components/header/more-menu/welcome-guide-menu-item.js rename to packages/edit-post/src/components/more-menu/welcome-guide-menu-item.js diff --git a/packages/edit-post/src/components/visual-editor/block-inspector-button.js b/packages/edit-post/src/components/visual-editor/block-inspector-button.js deleted file mode 100644 index 87b55d4eb3fdca..00000000000000 --- a/packages/edit-post/src/components/visual-editor/block-inspector-button.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { MenuItem } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { speak } from '@wordpress/a11y'; -import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; - -/** - * Internal dependencies - */ -import { store as editPostStore } from '../../store'; - -const noop = () => {}; - -export function BlockInspectorButton( { onClick = noop, small = false } ) { - const { shortcut, areAdvancedSettingsOpened } = useSelect( - ( select ) => ( { - shortcut: select( - keyboardShortcutsStore - ).getShortcutRepresentation( 'core/editor/toggle-sidebar' ), - areAdvancedSettingsOpened: - select( editPostStore ).getActiveGeneralSidebarName() === - 'edit-post/block', - } ), - [] - ); - const { openGeneralSidebar, closeGeneralSidebar } = - useDispatch( editPostStore ); - - const label = areAdvancedSettingsOpened - ? __( 'Hide more settings' ) - : __( 'Show more settings' ); - - return ( - { - if ( areAdvancedSettingsOpened ) { - closeGeneralSidebar(); - speak( __( 'Block settings closed' ) ); - } else { - openGeneralSidebar( 'edit-post/block' ); - speak( - __( - 'Additional settings are now available in the Editor block settings sidebar' - ) - ); - } - onClick(); - } } - shortcut={ shortcut } - > - { ! small && label } - - ); -} - -export default BlockInspectorButton; diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js deleted file mode 100644 index 24a50cb7ba69a3..00000000000000 --- a/packages/edit-post/src/components/visual-editor/index.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * WordPress dependencies - */ -import { - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; -import { useMemo } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; -import { store as blocksStore } from '@wordpress/blocks'; -import { store as blockEditorStore } from '@wordpress/block-editor'; - -/** - * Internal dependencies - */ -import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; -import { usePaddingAppender } from './use-padding-appender'; - -const { VisualEditor: VisualEditorRoot } = unlock( editorPrivateApis ); - -const isGutenbergPlugin = globalThis.IS_GUTENBERG_PLUGIN ? true : false; -const DESIGN_POST_TYPES = [ - 'wp_template', - 'wp_template_part', - 'wp_block', - 'wp_navigation', -]; - -export default function VisualEditor( { styles } ) { - const { - isWelcomeGuideVisible, - renderingMode, - isBlockBasedTheme, - hasV3BlocksOnly, - isEditingTemplate, - isZoomedOutView, - postType, - } = useSelect( ( select ) => { - const { isFeatureActive } = select( editPostStore ); - const { getEditorSettings, getRenderingMode, getCurrentPostType } = - select( editorStore ); - const { getBlockTypes } = select( blocksStore ); - const { __unstableGetEditorMode } = select( blockEditorStore ); - const editorSettings = getEditorSettings(); - const _postType = getCurrentPostType(); - return { - isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ), - renderingMode: getRenderingMode(), - isBlockBasedTheme: editorSettings.__unstableIsBlockBasedTheme, - hasV3BlocksOnly: getBlockTypes().every( ( type ) => { - return type.apiVersion >= 3; - } ), - isEditingTemplate: _postType === 'wp_template', - isZoomedOutView: __unstableGetEditorMode() === 'zoom-out', - postType: _postType, - }; - }, [] ); - const hasMetaBoxes = useSelect( - ( select ) => select( editPostStore ).hasMetaBoxes(), - [] - ); - - const paddingAppenderRef = usePaddingAppender(); - - let paddingBottom; - - // Add a constant padding for the typewritter effect. When typing at the - // bottom, there needs to be room to scroll up. - if ( - ! isZoomedOutView && - ! hasMetaBoxes && - renderingMode === 'post-only' && - ! DESIGN_POST_TYPES.includes( postType ) - ) { - paddingBottom = '40vh'; - } - - styles = useMemo( - () => [ - ...styles, - { - // We should move this in to future to the body. - css: paddingBottom - ? `body{padding-bottom:${ paddingBottom }}` - : '', - }, - ], - [ styles, paddingBottom ] - ); - - const isToBeIframed = - ( ( hasV3BlocksOnly || ( isGutenbergPlugin && isBlockBasedTheme ) ) && - ! hasMetaBoxes ) || - isEditingTemplate; - - return ( - - ); -} diff --git a/packages/edit-post/src/hooks/commands/use-common-commands.js b/packages/edit-post/src/hooks/commands/use-common-commands.js deleted file mode 100644 index bd2036eb85e748..00000000000000 --- a/packages/edit-post/src/hooks/commands/use-common-commands.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect, useDispatch } from '@wordpress/data'; -import { __, isRTL } from '@wordpress/i18n'; -import { - drawerLeft, - drawerRight, - blockDefault, - fullscreen, - formatListBullets, -} from '@wordpress/icons'; -import { useCommand } from '@wordpress/commands'; -import { store as preferencesStore } from '@wordpress/preferences'; -import { - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; -import { store as noticesStore } from '@wordpress/notices'; - -/** - * Internal dependencies - */ -import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -const { interfaceStore } = unlock( editorPrivateApis ); - -export default function useCommonCommands() { - const { openGeneralSidebar, closeGeneralSidebar } = - useDispatch( editPostStore ); - const { activeSidebar, isFullscreen, isPublishSidebarEnabled } = useSelect( - ( select ) => { - const { get } = select( preferencesStore ); - - return { - activeSidebar: - select( interfaceStore ).getActiveComplementaryArea( - 'core' - ), - isPublishSidebarEnabled: - select( editorStore ).isPublishSidebarEnabled(), - isFullscreen: get( 'core/edit-post', 'fullscreenMode' ), - }; - }, - [] - ); - const { toggle } = useDispatch( preferencesStore ); - const { createInfoNotice } = useDispatch( noticesStore ); - - useCommand( { - name: 'core/open-settings-sidebar', - label: __( 'Toggle settings sidebar' ), - icon: isRTL() ? drawerLeft : drawerRight, - callback: ( { close } ) => { - close(); - if ( activeSidebar === 'edit-post/document' ) { - closeGeneralSidebar(); - } else { - openGeneralSidebar( 'edit-post/document' ); - } - }, - } ); - - useCommand( { - name: 'core/open-block-inspector', - label: __( 'Toggle block inspector' ), - icon: blockDefault, - callback: ( { close } ) => { - close(); - if ( activeSidebar === 'edit-post/block' ) { - closeGeneralSidebar(); - } else { - openGeneralSidebar( 'edit-post/block' ); - } - }, - } ); - - useCommand( { - name: 'core/toggle-fullscreen-mode', - label: isFullscreen - ? __( 'Exit fullscreen' ) - : __( 'Enter fullscreen' ), - icon: fullscreen, - callback: ( { close } ) => { - toggle( 'core/edit-post', 'fullscreenMode' ); - close(); - createInfoNotice( - isFullscreen ? __( 'Fullscreen off.' ) : __( 'Fullscreen on.' ), - { - id: 'core/edit-post/toggle-fullscreen-mode/notice', - type: 'snackbar', - actions: [ - { - label: __( 'Undo' ), - onClick: () => { - toggle( 'core/edit-post', 'fullscreenMode' ); - }, - }, - ], - } - ); - }, - } ); - - useCommand( { - name: 'core/toggle-publish-sidebar', - label: isPublishSidebarEnabled - ? __( 'Disable pre-publish checks' ) - : __( 'Enable pre-publish checks' ), - icon: formatListBullets, - callback: ( { close } ) => { - close(); - toggle( 'core', 'isPublishSidebarEnabled' ); - createInfoNotice( - isPublishSidebarEnabled - ? __( 'Pre-publish checks disabled.' ) - : __( 'Pre-publish checks enabled.' ), - { - id: 'core/editor/publish-sidebar/notice', - type: 'snackbar', - } - ); - }, - } ); -} diff --git a/packages/edit-post/src/hooks/index.js b/packages/edit-post/src/hooks/index.js deleted file mode 100644 index 0397f1fee936c3..00000000000000 --- a/packages/edit-post/src/hooks/index.js +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Internal dependencies - */ -import './validate-multiple-use'; diff --git a/packages/edit-post/src/hooks/validate-multiple-use/index.js b/packages/edit-post/src/hooks/validate-multiple-use/index.js deleted file mode 100644 index bf771f45220362..00000000000000 --- a/packages/edit-post/src/hooks/validate-multiple-use/index.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * WordPress dependencies - */ -import { - createBlock, - findTransform, - getBlockTransforms, - getBlockType, - hasBlockSupport, -} from '@wordpress/blocks'; -import { Button } from '@wordpress/components'; -import { withSelect, withDispatch } from '@wordpress/data'; -import { Warning, store as blockEditorStore } from '@wordpress/block-editor'; -import { addFilter } from '@wordpress/hooks'; -import { __ } from '@wordpress/i18n'; -import { compose, createHigherOrderComponent } from '@wordpress/compose'; - -/** - * Recursively find very first block of an specific block type. - * - * @param {Object[]} blocks List of blocks. - * @param {string} name Block name to search. - * - * @return {Object|undefined} Return block object or undefined. - */ -function findFirstOfSameType( blocks, name ) { - if ( ! Array.isArray( blocks ) || ! blocks.length ) { - return; - } - - for ( const block of blocks ) { - if ( block.name === name ) { - return block; - } - - // Search inside innerBlocks. - const firstBlock = findFirstOfSameType( block.innerBlocks, name ); - - if ( firstBlock ) { - return firstBlock; - } - } -} - -const enhance = compose( - /** - * For blocks whose block type doesn't support `multiple`, provides the - * wrapped component with `originalBlockClientId` -- a reference to the - * first block of the same type in the content -- if and only if that - * "original" block is not the current one. Thus, an inexisting - * `originalBlockClientId` prop signals that the block is valid. - * - * @param {Component} WrappedBlockEdit A filtered BlockEdit instance. - * - * @return {Component} Enhanced component with merged state data props. - */ - withSelect( ( select, block ) => { - const multiple = hasBlockSupport( block.name, 'multiple', true ); - - // For block types with `multiple` support, there is no "original - // block" to be found in the content, as the block itself is valid. - if ( multiple ) { - return {}; - } - - // Otherwise, only pass `originalBlockClientId` if it refers to a different - // block from the current one. - const blocks = select( blockEditorStore ).getBlocks(); - const firstOfSameType = findFirstOfSameType( blocks, block.name ); - const isInvalid = - firstOfSameType && firstOfSameType.clientId !== block.clientId; - return { - originalBlockClientId: isInvalid && firstOfSameType.clientId, - }; - } ), - withDispatch( ( dispatch, { originalBlockClientId } ) => ( { - selectFirst: () => - dispatch( blockEditorStore ).selectBlock( originalBlockClientId ), - } ) ) -); - -const withMultipleValidation = createHigherOrderComponent( ( BlockEdit ) => { - return enhance( ( { originalBlockClientId, selectFirst, ...props } ) => { - if ( ! originalBlockClientId ) { - return ; - } - - const blockType = getBlockType( props.name ); - const outboundType = getOutboundType( props.name ); - - return [ - - - , - - { __( 'Find original' ) } - , - props.onReplace( [] ) } - > - { __( 'Remove' ) } - , - outboundType && ( - - props.onReplace( - createBlock( - outboundType.name, - props.attributes - ) - ) - } - > - { __( 'Transform into:' ) } { outboundType.title } - - ), - ] } - > - { blockType?.title }: - { __( 'This block can only be used once.' ) } - , - ]; - } ); -}, 'withMultipleValidation' ); - -/** - * Given a base block name, returns the default block type to which to offer - * transforms. - * - * @param {string} blockName Base block name. - * - * @return {?Object} The chosen default block type. - */ -function getOutboundType( blockName ) { - // Grab the first outbound transform. - const transform = findTransform( - getBlockTransforms( 'to', blockName ), - ( { type, blocks } ) => type === 'block' && blocks.length === 1 // What about when .length > 1? - ); - - if ( ! transform ) { - return null; - } - - return getBlockType( transform.blocks[ 0 ] ); -} - -addFilter( - 'editor.BlockEdit', - 'core/edit-post/validate-multiple-use/with-multiple-validation', - withMultipleValidation -); diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 1e0b3fe7d4d6ff..0ec4388a9af70e 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -14,13 +14,19 @@ import { registerLegacyWidgetBlock, registerWidgetGroupBlock, } from '@wordpress/widgets'; -import { store as editorStore } from '@wordpress/editor'; +import { + store as editorStore, + privateApis as editorPrivateApis, +} from '@wordpress/editor'; /** * Internal dependencies */ -import './hooks'; import Editor from './editor'; +import { unlock } from './lock-unlock'; + +const { BackButton: __experimentalMainDashboardButton } = + unlock( editorPrivateApis ); /** * Initializes and returns an instance of Editor. @@ -152,8 +158,7 @@ export function reinitializeEditor() { } ); } -export { default as __experimentalFullscreenModeClose } from './components/header/fullscreen-mode-close'; -export { default as __experimentalMainDashboardButton } from './components/header/main-dashboard-button'; - +export { default as __experimentalFullscreenModeClose } from './components/back-button/fullscreen-mode-close'; +export { __experimentalMainDashboardButton }; export { store } from './store'; export * from './deprecated'; diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index 7a935f198660a8..a95c02242644b5 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -1,5 +1,4 @@ -@import "./components/header/style.scss"; -@import "./components/header/fullscreen-mode-close/style.scss"; +@import "./components/back-button/style.scss"; @import "./components/layout/style.scss"; @import "./components/meta-boxes/meta-boxes-area/style.scss"; @import "./components/welcome-guide/style.scss"; diff --git a/packages/edit-post/src/test/__snapshots__/editor.native.js.snap b/packages/edit-post/src/test/__snapshots__/editor.native.js.snap index 8b820cd38f11bd..76bb42d5a2ccea 100644 --- a/packages/edit-post/src/test/__snapshots__/editor.native.js.snap +++ b/packages/edit-post/src/test/__snapshots__/editor.native.js.snap @@ -19,3 +19,75 @@ exports[`Editor appends media correctly for allowed types and skips unsupported " `; + +exports[`Editor on content update parses markdown into blocks 1`] = ` +" +Lorem ipsum dolor sit amet, consectetur adipiscingelit. + + + +Overview + + + + +Lorem ipsum dolor sit amet + + + +Consectetur adipiscingelit + + + +Integer nec odio + + + + +Details + + + + +Sed cursus ante dapibus diam + + + +Nulla quis sem at nibh elementum imperdiet + + + +Duis sagittis ipsum ## Mixed Lists + + + + + +Key Points: + + + + + +Lorem ipsum dolor sit amet + + + +Consectetur adipiscing elit + + + +Integer nec odio + + + + + +Additional Info:-Sed cursus ante dapibus diam + + + +Nulla quis sem at nibh elementum imperdiet + +" +`; diff --git a/packages/edit-post/src/test/editor.native.js b/packages/edit-post/src/test/editor.native.js index 0de2c528b2452a..acafc4d68d42a5 100644 --- a/packages/edit-post/src/test/editor.native.js +++ b/packages/edit-post/src/test/editor.native.js @@ -7,6 +7,7 @@ import { fireEvent, getBlock, getEditorHtml, + getEditorTitle, initializeEditor, screen, setupCoreBlocks, @@ -20,6 +21,7 @@ import { requestMediaImport, subscribeMediaAppend, subscribeParentToggleHTMLMode, + subscribeToContentUpdate, } from '@wordpress/react-native-bridge'; setupCoreBlocks(); @@ -34,6 +36,11 @@ subscribeMediaAppend.mockImplementation( ( callback ) => { mediaAppendCallback = callback; } ); +let onContentUpdateCallback; +subscribeToContentUpdate.mockImplementation( ( callback ) => { + onContentUpdateCallback = callback; +} ); + const MEDIA = [ { localId: 1, @@ -149,4 +156,74 @@ describe( 'Editor', () => { screen.queryAllByLabelText( 'Open Settings' ); expect( openBlockSettingsButton.length ).toBe( 0 ); } ); + + describe( 'on content update', () => { + const MARKDOWN = `# Sample Document\nLorem ipsum dolor sit amet, consectetur adipiscing + elit.\n## Overview\n- Lorem ipsum dolor sit amet\n- Consectetur adipiscing + elit\n- Integer nec odio\n## Details\n1. Sed cursus ante dapibus diam\n2. + Nulla quis sem at nibh elementum imperdiet\n3. Duis sagittis ipsum\n + ## Mixed Lists\n- Key Points:\n 1. Lorem ipsum dolor sit amet\n 2. + Consectetur adipiscing elit\n 3. Integer nec odio\n- Additional Info:\n - + Sed cursus ante dapibus diam\n - Nulla quis sem at nibh elementum imperdiet\n`; + + it( 'parses markdown into blocks', async () => { + // Arrange + await initializeEditor( { + initialTitle: null, + } ); + + // Act + act( () => { + onContentUpdateCallback( { + content: MARKDOWN, + } ); + } ); + + // Assert + // Needed to for the "Processed HTML piece" log. + expect( console ).toHaveLogged(); + expect( getEditorTitle() ).toBe( 'Sample Document' ); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'parses a markdown heading into a title', async () => { + // Arrange + await initializeEditor( { + initialTitle: null, + } ); + + // Act + act( () => { + onContentUpdateCallback( { + content: `# Sample Document`, + } ); + } ); + + // Assert + // Needed to for the "Processed HTML piece" log. + expect( console ).toHaveLogged(); + expect( getEditorTitle() ).toBe( 'Sample Document' ); + expect( getEditorHtml() ).toBe( '' ); + } ); + + it( 'parses standard text into blocks', async () => { + // Arrange + await initializeEditor( { + initialTitle: null, + } ); + + // Act + act( () => { + onContentUpdateCallback( { + content: `Lorem ipsum dolor sit amet`, + } ); + } ); + + // Assert + // Needed to for the "Processed HTML piece" log. + expect( console ).toHaveLogged(); + expect( getEditorTitle() ).toBe( 'Lorem ipsum dolor sit amet' ); + expect( getEditorHtml() ).toBe( '' ); + } ); + } ); } ); diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index 0997d17e849ede..67b5d055450f80 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 7132e67401a225..784407cd3f12ba 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "5.35.0", + "version": "6.0.0", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-site/src/components/add-new-pattern/index.js b/packages/edit-site/src/components/add-new-pattern/index.js index bca37df9715004..33d1b8cbca1c2d 100644 --- a/packages/edit-site/src/components/add-new-pattern/index.js +++ b/packages/edit-site/src/components/add-new-pattern/index.js @@ -13,11 +13,11 @@ import { } from '@wordpress/patterns'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; /** * Internal dependencies */ -import CreateTemplatePartModal from '../create-template-part-modal'; import { unlock } from '../../lock-unlock'; import { PATTERN_TYPES, @@ -29,6 +29,7 @@ const { useHistory } = unlock( routerPrivateApis ); const { CreatePatternModal, useAddPatternCategory } = unlock( editPatternsPrivateApis ); +const { CreateTemplatePartModal } = unlock( editorPrivateApis ); export default function AddNewPattern() { const history = useHistory(); @@ -108,7 +109,11 @@ export default function AddNewPattern() { diff --git a/packages/edit-site/src/components/add-new-template/index.js b/packages/edit-site/src/components/add-new-template/index.js index 90d40a013b82ec..3705010be48533 100644 --- a/packages/edit-site/src/components/add-new-template/index.js +++ b/packages/edit-site/src/components/add-new-template/index.js @@ -128,6 +128,7 @@ function TemplateListItem( { spacing={ 0 } > @@ -354,6 +355,7 @@ function NewTemplate() { variant="primary" onClick={ () => setShowModal( true ) } label={ postType.labels.add_new_item } + __next40pxDefaultSize > { postType.labels.add_new_item } diff --git a/packages/edit-site/src/components/block-editor/site-editor-canvas.js b/packages/edit-site/src/components/block-editor/site-editor-canvas.js deleted file mode 100644 index 03d11dcf8237ef..00000000000000 --- a/packages/edit-site/src/components/block-editor/site-editor-canvas.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Internal dependencies - */ -import EditorCanvas from './editor-canvas'; -import EditorCanvasContainer from '../editor-canvas-container'; -import useSiteEditorSettings from './use-site-editor-settings'; - -export default function SiteEditorCanvas() { - const settings = useSiteEditorSettings(); - - return ( - - { ( [ editorCanvasView ] ) => - editorCanvasView ? ( - - { editorCanvasView } - - ) : ( - - ) - } - - ); -} diff --git a/packages/edit-site/src/components/block-editor/style.scss b/packages/edit-site/src/components/block-editor/style.scss index 14f2074130d116..3849eed24a4ea3 100644 --- a/packages/edit-site/src/components/block-editor/style.scss +++ b/packages/edit-site/src/components/block-editor/style.scss @@ -1,29 +1,3 @@ -// The button element easily inherits styles that are meant for the editor style. -// These rules enhance the specificity to reduce that inheritance. -// This is duplicated in visual-editor. -.edit-site-block-editor__editor-styles-wrapper .components-button { - font-family: $default-font; - font-size: $default-font-size; - padding: 6px 12px; - - &.is-tertiary, - &.has-icon { - padding: 6px; - } -} - -.edit-site-visual-editor { - height: 100%; - background-color: $gray-300; - - // Controls height of editor and editor canvas container (style book, global styles revisions previews etc.) - iframe { - display: block; - width: 100%; - height: 100%; - } -} - .edit-site-visual-editor__editor-canvas { &.is-focused { outline: calc(2 * var(--wp-admin-border-width-focus)) solid var(--wp-admin-theme-color); diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/use-editor-iframe-props.js similarity index 62% rename from packages/edit-site/src/components/block-editor/editor-canvas.js rename to packages/edit-site/src/components/block-editor/use-editor-iframe-props.js index bac51c5df7d335..3b5c9f7fe65726 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/use-editor-iframe-props.js @@ -8,12 +8,9 @@ import clsx from 'clsx'; */ import { useSelect, useDispatch } from '@wordpress/data'; import { ENTER, SPACE } from '@wordpress/keycodes'; -import { useState, useEffect, useMemo } from '@wordpress/element'; +import { useState, useEffect } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; +import { store as editorStore } from '@wordpress/editor'; /** * Internal dependencies @@ -21,9 +18,7 @@ import { import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; -const { VisualEditor } = unlock( editorPrivateApis ); - -function EditorCanvas( { settings } ) { +export default function useEditorIframeProps() { const { canvasMode, currentPostIsTrashed } = useSelect( ( select ) => { const { getCanvasMode } = unlock( select( editSiteStore ) ); @@ -75,36 +70,10 @@ function EditorCanvas( { settings } ) { readonly: true, }; - const styles = useMemo( - () => [ - ...settings.styles, - { - // Forming a "block formatting context" to prevent margin collapsing. - // @see https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context - - css: `body{${ - canvasMode === 'view' - ? `min-height: 100vh; ${ - currentPostIsTrashed ? '' : 'cursor: pointer;' - }` - : '' - }}}`, - }, - ], - [ settings.styles, canvasMode, currentPostIsTrashed ] - ); - - return ( - - ); + return { + className: clsx( 'edit-site-visual-editor__editor-canvas', { + 'is-focused': isFocused && canvasMode === 'view', + } ), + ...( canvasMode === 'view' ? viewModeIframeProps : {} ), + }; } - -export default EditorCanvas; diff --git a/packages/edit-site/src/components/canvas-loader/index.js b/packages/edit-site/src/components/canvas-loader/index.js index 99d83c3ad69b3b..95dd8f24612938 100644 --- a/packages/edit-site/src/components/canvas-loader/index.js +++ b/packages/edit-site/src/components/canvas-loader/index.js @@ -2,7 +2,10 @@ * WordPress dependencies */ import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; -import { privateApis as componentsPrivateApis } from '@wordpress/components'; +import { + privateApis as componentsPrivateApis, + ProgressBar, +} from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; @@ -12,7 +15,7 @@ import { useSelect } from '@wordpress/data'; import { unlock } from '../../lock-unlock'; import { useStylesPreviewColors } from '../global-styles/hooks'; -const { ProgressBar, Theme } = unlock( componentsPrivateApis ); +const { Theme } = unlock( componentsPrivateApis ); const { useGlobalStyle } = unlock( blockEditorPrivateApis ); export default function CanvasLoader( { id } ) { diff --git a/packages/edit-site/src/components/editor-canvas-container/index.js b/packages/edit-site/src/components/editor-canvas-container/index.js index ca5bc3a0b34f45..baebc1bc1bc894 100644 --- a/packages/edit-site/src/components/editor-canvas-container/index.js +++ b/packages/edit-site/src/components/editor-canvas-container/index.js @@ -4,7 +4,6 @@ import { Children, cloneElement, useState } from '@wordpress/element'; import { Button, - privateApis as componentsPrivateApis, __experimentalUseSlotFills as useSlotFills, } from '@wordpress/components'; import { ESCAPE } from '@wordpress/keycodes'; @@ -24,7 +23,7 @@ import { import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; -const { ResizableEditor } = unlock( editorPrivateApis ); +const { EditorContentSlotFill, ResizableEditor } = unlock( editorPrivateApis ); /** * Returns a translated string for the title of the editor canvas container. @@ -45,15 +44,6 @@ function getEditorCanvasContainerTitle( view ) { } } -// Creates a private slot fill. -const { createPrivateSlotFill } = unlock( componentsPrivateApis ); -const SLOT_FILL_NAME = 'EditSiteEditorCanvasContainerSlot'; -const { - privateKey, - Slot: EditorCanvasContainerSlot, - Fill: EditorCanvasContainerFill, -} = createPrivateSlotFill( SLOT_FILL_NAME ); - function EditorCanvasContainer( { children, closeButtonLabel, @@ -123,7 +113,7 @@ function EditorCanvasContainer( { const shouldShowCloseButton = onClose || closeButtonLabel; return ( - + { /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ } @@ -145,14 +135,13 @@ function EditorCanvasContainer( { - + ); } function useHasEditorCanvasContainer() { - const fills = useSlotFills( privateKey ); + const fills = useSlotFills( EditorContentSlotFill.privateKey ); return !! fills?.length; } -EditorCanvasContainer.Slot = EditorCanvasContainerSlot; export default EditorCanvasContainer; export { useHasEditorCanvasContainer, getEditorCanvasContainerTitle }; diff --git a/packages/edit-site/src/components/editor-canvas-container/style.scss b/packages/edit-site/src/components/editor-canvas-container/style.scss index 0aca5f8045ce8a..fad566212e7328 100644 --- a/packages/edit-site/src/components/editor-canvas-container/style.scss +++ b/packages/edit-site/src/components/editor-canvas-container/style.scss @@ -1,5 +1,13 @@ .edit-site-editor-canvas-container { height: 100%; + background-color: $gray-300; + + // Controls height of editor and editor canvas container (style book, global styles revisions previews etc.) + iframe { + display: block; + width: 100%; + height: 100%; + } .edit-site-layout.is-full-canvas & { padding: $grid-unit-30 $grid-unit-30 0; diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index b7fc350c3eabb9..26b20b66f11c76 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -7,39 +7,24 @@ import clsx from 'clsx'; * WordPress dependencies */ import { useDispatch, useSelect } from '@wordpress/data'; -import { - Notice, - __unstableAnimatePresence as AnimatePresence, - __unstableMotion as motion, -} from '@wordpress/components'; -import { - useInstanceId, - useViewportMatch, - useReducedMotion, -} from '@wordpress/compose'; -import { store as preferencesStore } from '@wordpress/preferences'; -import { - BlockBreadcrumb, - BlockToolbar, - store as blockEditorStore, -} from '@wordpress/block-editor'; +import { Notice } from '@wordpress/components'; +import { useInstanceId } from '@wordpress/compose'; import { EditorKeyboardShortcutsRegister, - EditorNotices, privateApis as editorPrivateApis, store as editorStore, } from '@wordpress/editor'; import { __, sprintf } from '@wordpress/i18n'; import { store as coreDataStore } from '@wordpress/core-data'; import { privateApis as blockLibraryPrivateApis } from '@wordpress/block-library'; -import { useState, useCallback } from '@wordpress/element'; +import { useCallback, useMemo } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies */ -import Header from '../header-edit-mode'; import WelcomeGuide from '../welcome-guide'; import { store as editSiteStore } from '../../store'; import { GlobalStylesRenderer } from '../global-styles-renderer'; @@ -48,82 +33,51 @@ import CanvasLoader from '../canvas-loader'; import { unlock } from '../../lock-unlock'; import useEditedEntityRecord from '../use-edited-entity-record'; import { POST_TYPE_LABELS, TEMPLATE_POST_TYPE } from '../../utils/constants'; -import SiteEditorCanvas from '../block-editor/site-editor-canvas'; import TemplatePartConverter from '../template-part-converter'; import { useSpecificEditorSettings } from '../block-editor/use-site-editor-settings'; import PluginTemplateSettingPanel from '../plugin-template-setting-panel'; import GlobalStylesSidebar from '../global-styles-sidebar'; +import { isPreviewingTheme } from '../../utils/is-previewing-theme'; +import { + getEditorCanvasContainerTitle, + useHasEditorCanvasContainer, +} from '../editor-canvas-container'; +import SaveButton from '../save-button'; +import SiteEditorMoreMenu from '../more-menu'; +import useEditorIframeProps from '../block-editor/use-editor-iframe-props'; const { + EditorInterface, ExperimentalEditorProvider: EditorProvider, - InserterSidebar, - ListViewSidebar, - InterfaceSkeleton, - ComplementaryArea, - interfaceStore, - SavePublishPanels, Sidebar, - TextEditor, } = unlock( editorPrivateApis ); const { useHistory } = unlock( routerPrivateApis ); const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis ); -const interfaceLabels = { - /* translators: accessibility text for the editor content landmark region. */ - body: __( 'Editor content' ), - /* translators: accessibility text for the editor settings landmark region. */ - sidebar: __( 'Editor settings' ), - /* translators: accessibility text for the editor publish landmark region. */ - actions: __( 'Editor publish' ), - /* translators: accessibility text for the editor footer landmark region. */ - footer: __( 'Editor footer' ), - /* translators: accessibility text for the editor header landmark region. */ - header: __( 'Editor top bar' ), -}; - -const ANIMATION_DURATION = 0.25; - export default function Editor( { isLoading } ) { const { record: editedPost, getTitle, isLoaded: hasLoadedPost, } = useEditedEntityRecord(); - const { type: editedPostType } = editedPost; - - const isLargeViewport = useViewportMatch( 'medium' ); - const disableMotion = useReducedMotion(); - const { context, contextPost, editorMode, canvasMode, - blockEditorMode, - isRightSidebarOpen, - isInserterOpen, - isListViewOpen, - isDistractionFree, - showIconLabels, - showBlockBreadcrumbs, - postTypeLabel, isEditingPage, supportsGlobalStyles, + showIconLabels, + editorCanvasView, + currentPostIsTrashed, } = useSelect( ( select ) => { - const { get } = select( preferencesStore ); const { getEditedPostContext, getCanvasMode, isPage } = unlock( select( editSiteStore ) ); - const { __unstableGetEditorMode } = select( blockEditorStore ); - const { getActiveComplementaryArea } = select( interfaceStore ); + const { get } = select( preferencesStore ); const { getEntityRecord, getCurrentTheme } = select( coreDataStore ); - const { - isInserterOpened, - isListViewOpened, - getPostTypeLabel, - getEditorMode, - } = select( editorStore ); + const { getEditorMode } = select( editorStore ); const _context = getEditedPostContext(); // The currently selected entity to display. @@ -139,33 +93,24 @@ export default function Editor( { isLoading } ) { : undefined, editorMode: getEditorMode(), canvasMode: getCanvasMode(), - blockEditorMode: __unstableGetEditorMode(), - isInserterOpen: isInserterOpened(), - isListViewOpen: isListViewOpened(), - isRightSidebarOpen: getActiveComplementaryArea( 'core' ), - isDistractionFree: get( 'core', 'distractionFree' ), - showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), - showIconLabels: get( 'core', 'showIconLabels' ), - postTypeLabel: getPostTypeLabel(), isEditingPage: isPage(), supportsGlobalStyles: getCurrentTheme()?.is_block_theme, + showIconLabels: get( 'core', 'showIconLabels' ), + editorCanvasView: unlock( + select( editSiteStore ) + ).getEditorCanvasContainerView(), + currentPostIsTrashed: + select( editorStore ).getCurrentPostAttribute( 'status' ) === + 'trash', }; }, [] ); + const _isPreviewingTheme = isPreviewingTheme(); + const hasDefaultEditorCanvasView = ! useHasEditorCanvasContainer(); + const iframeProps = useEditorIframeProps(); const isViewMode = canvasMode === 'view'; const isEditMode = canvasMode === 'edit'; const showVisualEditor = isViewMode || editorMode === 'visual'; - const shouldShowBlockBreadcrumbs = - ! isDistractionFree && - showBlockBreadcrumbs && - isEditMode && - showVisualEditor && - blockEditorMode !== 'zoom-out'; - const shouldShowInserter = isEditMode && showVisualEditor && isInserterOpen; - const shouldShowListView = isEditMode && showVisualEditor && isListViewOpen; - const secondarySidebarLabel = isListViewOpen - ? __( 'List View' ) - : __( 'Block Library' ); const postWithTemplate = !! context?.postId; let title; @@ -188,25 +133,25 @@ export default function Editor( { isLoading } ) { 'edit-site-editor__loading-progress' ); - const { closeGeneralSidebar } = useDispatch( editSiteStore ); - const settings = useSpecificEditorSettings(); + const styles = useMemo( + () => [ + ...settings.styles, + { + // Forming a "block formatting context" to prevent margin collapsing. + // @see https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context - // Local state for save panel. - // Note 'truthy' callback implies an open panel. - const [ entitiesSavedStatesCallback, setEntitiesSavedStatesCallback ] = - useState( false ); - - const closeEntitiesSavedStates = useCallback( - ( arg ) => { - if ( typeof entitiesSavedStatesCallback === 'function' ) { - entitiesSavedStatesCallback( arg ); - } - setEntitiesSavedStatesCallback( false ); - }, - [ entitiesSavedStatesCallback ] + css: `body{${ + canvasMode === 'view' + ? `min-height: 100vh; ${ + currentPostIsTrashed ? '' : 'cursor: pointer;' + }` + : '' + }}}`, + }, + ], + [ settings.styles, canvasMode, currentPostIsTrashed ] ); - const { createSuccessNotice } = useDispatch( noticesStore ); const history = useHistory(); const onActionPerformed = useCallback( @@ -286,102 +231,28 @@ export default function Editor( { isLoading } ) { settings={ settings } useSubRegistry={ false } > - + - { canvasMode === 'edit' && ( - - - - ) } - - } - actions={ - - } - content={ - <> - { isEditMode && } - { editorMode === 'text' && isEditMode && ( - - ) } - { ! isLargeViewport && showVisualEditor && ( - - ) } - { showVisualEditor && } - > - } - secondarySidebar={ - isEditMode && - ( ( shouldShowInserter && ( - - ) ) || - ( shouldShowListView && ) ) - } - sidebar={ - isEditMode && - ! isDistractionFree && ( - - ) + styles={ styles } + enableRegionNavigation={ false } + customSaveButton={ + _isPreviewingTheme && } - footer={ - shouldShowBlockBreadcrumbs && ( - - ) + forceDisableBlockTools={ ! hasDefaultEditorCanvasView } + title={ + ! hasDefaultEditorCanvasView + ? getEditorCanvasContainerTitle( + editorCanvasView + ) + : undefined } - labels={ { - ...interfaceLabels, - secondarySidebar: secondarySidebarLabel, - } } + iframeProps={ iframeProps } /> - + ); } diff --git a/packages/edit-site/src/components/global-styles/font-families.js b/packages/edit-site/src/components/global-styles/font-families.js index 9e6694d69b64cb..7713b27e306ef1 100644 --- a/packages/edit-site/src/components/global-styles/font-families.js +++ b/packages/edit-site/src/components/global-styles/font-families.js @@ -7,8 +7,8 @@ import { __experimentalVStack as VStack, __experimentalHStack as HStack, Button, - Tooltip, } from '@wordpress/components'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; import { settings } from '@wordpress/icons'; import { useContext } from '@wordpress/element'; @@ -21,18 +21,31 @@ import FontLibraryProvider, { import FontLibraryModal from './font-library-modal'; import FontFamilyItem from './font-family-item'; import Subtitle from './subtitle'; +import { setUIValuesNeeded } from './font-library-modal/utils'; +import { unlock } from '../../lock-unlock'; -function FontFamilies() { - const { modalTabOpen, toggleModal, themeFonts, customFonts } = - useContext( FontLibraryContext ); +const { useGlobalSetting } = unlock( blockEditorPrivateApis ); +function FontFamilies() { + const { modalTabOpen, setModalTabOpen } = useContext( FontLibraryContext ); + const [ fontFamilies ] = useGlobalSetting( 'typography.fontFamilies' ); + const themeFonts = fontFamilies?.theme + ? fontFamilies.theme + .map( ( f ) => setUIValuesNeeded( f, { source: 'theme' } ) ) + .sort( ( a, b ) => a.name.localeCompare( b.name ) ) + : []; + const customFonts = fontFamilies?.custom + ? fontFamilies.custom + .map( ( f ) => setUIValuesNeeded( f, { source: 'custom' } ) ) + .sort( ( a, b ) => a.name.localeCompare( b.name ) ) + : []; const hasFonts = 0 < customFonts.length || 0 < themeFonts.length; return ( <> { !! modalTabOpen && ( toggleModal() } + onRequestClose={ () => setModalTabOpen( null ) } defaultTabId={ modalTabOpen } /> ) } @@ -40,18 +53,12 @@ function FontFamilies() { { __( 'Fonts' ) } - - - - toggleModal( 'installed-fonts' ) - } - aria-label={ __( 'Manage fonts' ) } - icon={ settings } - size={ 'small' } - /> - - + setModalTabOpen( 'installed-fonts' ) } + label={ __( 'Manage fonts' ) } + icon={ settings } + size="small" + /> { hasFonts ? ( @@ -68,7 +75,7 @@ function FontFamilies() { toggleModal( 'upload-fonts' ) } + onClick={ () => setModalTabOpen( 'upload-fonts' ) } > { __( 'Add fonts' ) } diff --git a/packages/edit-site/src/components/global-styles/font-family-item.js b/packages/edit-site/src/components/global-styles/font-family-item.js index fc5418d5c7eb17..b80865fd2e21af 100644 --- a/packages/edit-site/src/components/global-styles/font-family-item.js +++ b/packages/edit-site/src/components/global-styles/font-family-item.js @@ -16,14 +16,14 @@ import { FontLibraryContext } from './font-library-modal/context'; import { getFamilyPreviewStyle } from './font-library-modal/utils/preview-styles'; function FontFamilyItem( { font } ) { - const { handleSetLibraryFontSelected, toggleModal } = + const { handleSetLibraryFontSelected, setModalTabOpen } = useContext( FontLibraryContext ); const variantsCount = font?.fontFace?.length || 1; const handleClick = () => { handleSetLibraryFontSelected( font ); - toggleModal( 'installed-fonts' ); + setModalTabOpen( 'installed-fonts' ); }; const previewStyle = getFamilyPreviewStyle( font ); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/context.js b/packages/edit-site/src/components/global-styles/font-library-modal/context.js index 9eafabe9424fb5..52a40d7dc90707 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/context.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/context.js @@ -51,8 +51,6 @@ function FontLibraryProvider( { children } ) { 'globalStyles', globalStylesId ); - const fontFamiliesHasChanges = - !! globalStyles?.edits?.settings?.typography?.fontFamilies; const [ isInstalling, setIsInstalling ] = useState( false ); const [ refreshKey, setRefreshKey ] = useState( 0 ); @@ -62,14 +60,11 @@ function FontLibraryProvider( { children } ) { setRefreshKey( Date.now() ); }; - const { - records: libraryPosts = [], - isResolving: isResolvingLibrary, - hasResolved: hasResolvedLibrary, - } = useEntityRecords( 'postType', 'wp_font_family', { - refreshKey, - _embed: true, - } ); + const { records: libraryPosts = [], isResolving: isResolvingLibrary } = + useEntityRecords( 'postType', 'wp_font_family', { + refreshKey, + _embed: true, + } ); const libraryFonts = ( libraryPosts || [] ).map( ( fontFamilyPost ) => { @@ -87,12 +82,6 @@ function FontLibraryProvider( { children } ) { const [ fontFamilies, setFontFamilies ] = useGlobalSetting( 'typography.fontFamilies' ); - // theme.json file font families - const [ baseFontFamilies ] = useGlobalSetting( - 'typography.fontFamilies', - undefined, - 'base' - ); /* * Save the font families to the database. @@ -131,24 +120,6 @@ function FontLibraryProvider( { children } ) { .sort( ( a, b ) => a.name.localeCompare( b.name ) ) : []; - const themeFontsSlugs = new Set( themeFonts.map( ( f ) => f.slug ) ); - - /* - * Base Theme Fonts are the fonts defined in the theme.json *file*. - * - * Uses the fonts from global styles + the ones from the theme.json file that hasn't repeated slugs. - * Avoids inconsistencies with the fonts listed in the font library modal as base (inactivated). - * These inconsistencies can happen when the active theme fonts in global styles aren't defined in theme.json file as when a theme style variation is applied. - */ - const baseThemeFonts = baseFontFamilies?.theme - ? themeFonts.concat( - baseFontFamilies.theme - .filter( ( f ) => ! themeFontsSlugs.has( f.slug ) ) - .map( ( f ) => setUIValuesNeeded( f, { source: 'theme' } ) ) - .sort( ( a, b ) => a.name.localeCompare( b.name ) ) - ) - : []; - const customFonts = fontFamilies?.custom ? fontFamilies.custom .map( ( f ) => setUIValuesNeeded( f, { source: 'custom' } ) ) @@ -187,10 +158,6 @@ function FontLibraryProvider( { children } ) { } ); }; - const toggleModal = ( tabName ) => { - setModalTabOpen( tabName || null ); - }; - // Demo const [ loadedFontUrls ] = useState( new Set() ); @@ -549,9 +516,6 @@ function FontLibraryProvider( { children } ) { libraryFontSelected, handleSetLibraryFontSelected, fontFamilies, - themeFonts, - baseThemeFonts, - customFonts, baseCustomFonts, isFontActivated, getFontFacesActivated, @@ -561,14 +525,12 @@ function FontLibraryProvider( { children } ) { toggleActivateFont, getAvailableFontsOutline, modalTabOpen, - toggleModal, + setModalTabOpen, refreshLibrary, notice, setNotice, saveFontFamilies, - fontFamiliesHasChanges, isResolvingLibrary, - hasResolvedLibrary, isInstalling, collections, getFontCollection, diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js b/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js index 7d89a92cb44bac..db9e3cb402561b 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js @@ -24,7 +24,7 @@ import { Button, DropdownMenu, SearchControl, - privateApis as componentsPrivateApis, + ProgressBar, } from '@wordpress/components'; import { debounce } from '@wordpress/compose'; import { sprintf, __, _x } from '@wordpress/i18n'; @@ -45,8 +45,6 @@ import GoogleFontsConfirmDialog from './google-fonts-confirm-dialog'; import { downloadFontFaceAssets } from './utils'; import { sortFontFaces } from './utils/sort-font-faces'; import CollectionFontVariant from './collection-font-variant'; -import { unlock } from '../../../lock-unlock'; -const { ProgressBar } = unlock( componentsPrivateApis ); const DEFAULT_CATEGORY = { slug: 'all', @@ -352,7 +350,7 @@ function FontCollection( { slug } ) { font={ font.font_family_settings } - navigatorPath={ '/fontFamily' } + navigatorPath="/fontFamily" onClick={ () => { setSelectedFont( font.font_family_settings @@ -504,7 +502,7 @@ function FontCollection( { slug } ) { parseInt( newPage ) ) } - size={ 'compact' } + size="compact" __nextHasNoMarginBottom /> ), diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js b/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js index cdc81aa09b7373..fd962618b510ac 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js @@ -15,29 +15,31 @@ import { __experimentalVStack as VStack, Flex, Notice, - privateApis as componentsPrivateApis, + ProgressBar, } from '@wordpress/components'; -import { store as coreStore } from '@wordpress/core-data'; +import { useEntityRecord, store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { useContext, useEffect, useState } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { chevronLeft } from '@wordpress/icons'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { unlock } from '../../../lock-unlock'; import { FontLibraryContext } from './context'; import FontCard from './font-card'; import LibraryFontVariant from './library-font-variant'; import { sortFontFaces } from './utils/sort-font-faces'; -const { ProgressBar } = unlock( componentsPrivateApis ); +import { setUIValuesNeeded } from './utils'; +import { unlock } from '../../../lock-unlock'; + +const { useGlobalSetting } = unlock( blockEditorPrivateApis ); function InstalledFonts() { const { baseCustomFonts, libraryFontSelected, - baseThemeFonts, handleSetLibraryFontSelected, refreshLibrary, uninstallFontFamily, @@ -45,12 +47,44 @@ function InstalledFonts() { isInstalling, saveFontFamilies, getFontFacesActivated, - fontFamiliesHasChanges, notice, setNotice, fontFamilies, } = useContext( FontLibraryContext ); const [ isConfirmDeleteOpen, setIsConfirmDeleteOpen ] = useState( false ); + const [ baseFontFamilies ] = useGlobalSetting( + 'typography.fontFamilies', + undefined, + 'base' + ); + const globalStylesId = useSelect( ( select ) => { + const { __experimentalGetCurrentGlobalStylesId } = select( coreStore ); + return __experimentalGetCurrentGlobalStylesId(); + } ); + + const globalStyles = useEntityRecord( + 'root', + 'globalStyles', + globalStylesId + ); + const fontFamiliesHasChanges = + !! globalStyles?.edits?.settings?.typography?.fontFamilies; + + const themeFonts = fontFamilies?.theme + ? fontFamilies.theme + .map( ( f ) => setUIValuesNeeded( f, { source: 'theme' } ) ) + .sort( ( a, b ) => a.name.localeCompare( b.name ) ) + : []; + const themeFontsSlugs = new Set( themeFonts.map( ( f ) => f.slug ) ); + const baseThemeFonts = baseFontFamilies?.theme + ? themeFonts.concat( + baseFontFamilies.theme + .filter( ( f ) => ! themeFontsSlugs.has( f.slug ) ) + .map( ( f ) => setUIValuesNeeded( f, { source: 'theme' } ) ) + .sort( ( a, b ) => a.name.localeCompare( b.name ) ) + ) + : []; + const customFontFamilyId = libraryFontSelected?.source === 'custom' && libraryFontSelected?.id; @@ -156,9 +190,7 @@ function InstalledFonts() { > 0 - ? sprintf( - // Translators: %d: Number of palette colors. - _n( '%d color', '%d colors', colors.length ), - colors.length - ) - : __( 'Add custom colors' ); + colors.length > 0 ? __( 'Edit palette' ) : __( 'Add colors' ); return ( @@ -69,13 +63,12 @@ function Palette( { name } ) { - + + { colors.length <= 0 && ( + { __( 'Add colors' ) } + ) } { colors .slice( 0, 5 ) @@ -87,9 +80,7 @@ function Palette( { name } ) { ) ) } - - { paletteButtonText } - + diff --git a/packages/edit-site/src/components/global-styles/screen-css.js b/packages/edit-site/src/components/global-styles/screen-css.js index 71d47a52a50274..e49207aaea2241 100644 --- a/packages/edit-site/src/components/global-styles/screen-css.js +++ b/packages/edit-site/src/components/global-styles/screen-css.js @@ -34,7 +34,7 @@ function ScreenCSS() { <> { description } { __( 'Learn more about CSS' ) } diff --git a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js index a9b0c859acff76..443d72deb9573b 100644 --- a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js +++ b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js @@ -145,7 +145,7 @@ export default function ShadowsEditPanel() { }; return ! selectedShadow ? ( - + ) : ( <> @@ -305,7 +305,7 @@ function ShadowEditor( { shadow, onChange } ) { align="center" className="edit-site-global-styles__shadows-panel__title" > - { 'Shadows' } + { __( 'Shadows' ) } + { label } { + setCurrentUserStyles( user ); + }, [ user ] ); + const variations = useSelect( ( select ) => { return select( coreStore @@ -29,20 +41,71 @@ export default function StyleVariationsContainer( { gap = 2 } ) { ); } ); - const withEmptyVariation = useMemo( () => { - return [ + const themeVariations = useMemo( () => { + const withEmptyVariation = [ { title: __( 'Default' ), settings: {}, styles: {}, }, - ...( multiplePropertyVariations ?? [] ).map( ( variation ) => ( { - ...variation, - settings: variation.settings ?? {}, - styles: variation.styles ?? {}, - } ) ), + ...( multiplePropertyVariations ?? [] ), + ]; + return [ + ...withEmptyVariation.map( ( variation ) => { + const blockStyles = { ...variation?.styles?.blocks } || {}; + + // We need to copy any user custom CSS to the variation to prevent it being lost + // when switching variations. + if ( userStyles?.blocks ) { + Object.keys( userStyles.blocks ).forEach( ( blockName ) => { + // First get any block specific custom CSS from the current user styles and merge with any custom CSS for + // that block in the variation. + if ( userStyles.blocks[ blockName ].css ) { + const variationBlockStyles = + blockStyles[ blockName ] || {}; + const customCSS = { + css: `${ + blockStyles[ blockName ]?.css || '' + } ${ + userStyles.blocks[ blockName ].css.trim() || + '' + }`, + }; + blockStyles[ blockName ] = { + ...variationBlockStyles, + ...customCSS, + }; + } + } ); + } + // Now merge any global custom CSS from current user styles with global custom CSS in the variation. + const css = + userStyles?.css || variation.styles?.css + ? { + css: `${ variation.styles?.css || '' } ${ + userStyles?.css || '' + }`, + } + : {}; + + const blocks = + Object.keys( blockStyles ).length > 0 + ? { blocks: blockStyles } + : {}; + + const styles = { + ...variation.styles, + ...css, + ...blocks, + }; + return { + ...variation, + settings: variation.settings ?? {}, + styles, + }; + } ), ]; - }, [ multiplePropertyVariations ] ); + }, [ multiplePropertyVariations, userStyles?.blocks, userStyles?.css ] ); return ( - { withEmptyVariation.map( ( variation, index ) => ( + { themeVariations.map( ( variation, index ) => ( { ( isFocused ) => ( - + diff --git a/packages/edit-site/src/components/global-styles/variations/variations-color.js b/packages/edit-site/src/components/global-styles/variations/variations-color.js index 28469fccab0ad5..2b3758e92a5123 100644 --- a/packages/edit-site/src/components/global-styles/variations/variations-color.js +++ b/packages/edit-site/src/components/global-styles/variations/variations-color.js @@ -14,7 +14,8 @@ import Variation from './variation'; export default function ColorVariations( { title, gap = 2 } ) { const colorVariations = useColorVariations(); - if ( ! colorVariations?.length ) { + // Return null if there is only one variation (the default). + if ( colorVariations?.length <= 1 ) { return null; } diff --git a/packages/edit-site/src/components/global-styles/variations/variations-typography.js b/packages/edit-site/src/components/global-styles/variations/variations-typography.js index 6ee7be561e6114..208ed822b5a6f6 100644 --- a/packages/edit-site/src/components/global-styles/variations/variations-typography.js +++ b/packages/edit-site/src/components/global-styles/variations/variations-typography.js @@ -19,7 +19,8 @@ import Subtitle from '../subtitle'; export default function TypographyVariations( { title, gap = 2 } ) { const typographyVariations = useTypographyVariations(); - if ( ! typographyVariations?.length ) { + // Return null if there is only one variation (the default). + if ( typographyVariations?.length <= 1 ) { return null; } diff --git a/packages/edit-site/src/components/header-edit-mode/index.js b/packages/edit-site/src/components/header-edit-mode/index.js deleted file mode 100644 index fcd4ea1b388028..00000000000000 --- a/packages/edit-site/src/components/header-edit-mode/index.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * WordPress dependencies - */ -import { privateApis as editorPrivateApis } from '@wordpress/editor'; -import { useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import SiteEditorMoreMenu from './more-menu'; -import { unlock } from '../../lock-unlock'; -import SaveButton from '../save-button'; -import { isPreviewingTheme } from '../../utils/is-previewing-theme'; -import { - getEditorCanvasContainerTitle, - useHasEditorCanvasContainer, -} from '../editor-canvas-container'; -import { store as editSiteStore } from '../../store'; - -const { Header: EditorHeader } = unlock( editorPrivateApis ); - -function Header( { setEntitiesSavedStatesCallback } ) { - const _isPreviewingTheme = isPreviewingTheme(); - const hasDefaultEditorCanvasView = ! useHasEditorCanvasContainer(); - const { editorCanvasView } = useSelect( ( select ) => { - return { - editorCanvasView: unlock( - select( editSiteStore ) - ).getEditorCanvasContainerView(), - }; - }, [] ); - - return ( - - } - forceDisableBlockTools={ ! hasDefaultEditorCanvasView } - title={ - ! hasDefaultEditorCanvasView - ? getEditorCanvasContainerTitle( editorCanvasView ) - : undefined - } - > - - - ); -} - -export default Header; diff --git a/packages/edit-site/src/components/header-edit-mode/style.scss b/packages/edit-site/src/components/header-edit-mode/style.scss deleted file mode 100644 index 69b1e9dff38492..00000000000000 --- a/packages/edit-site/src/components/header-edit-mode/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.editor-header { - padding-left: $header-height; -} diff --git a/packages/edit-site/src/components/layout/router.js b/packages/edit-site/src/components/layout/router.js index f3c0f40c73c95b..25ab81d9a173cc 100644 --- a/packages/edit-site/src/components/layout/router.js +++ b/packages/edit-site/src/components/layout/router.js @@ -87,7 +87,7 @@ export default function useLayoutAreas() { areas: { sidebar: ( } /> diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss index fdbf6b184aa211..8c15cdae338818 100644 --- a/packages/edit-site/src/components/layout/style.scss +++ b/packages/edit-site/src/components/layout/style.scss @@ -162,15 +162,13 @@ position: relative; color: $white; border-radius: 0; - height: $header-height + $border-width; + height: $header-height; width: $header-height; - margin-bottom: - $border-width; overflow: hidden; padding: 0; display: flex; align-items: center; justify-content: center; - border-bottom: 1px solid transparent; &:hover, &:active { diff --git a/packages/edit-site/src/components/header-edit-mode/more-menu/index.js b/packages/edit-site/src/components/more-menu/index.js similarity index 94% rename from packages/edit-site/src/components/header-edit-mode/more-menu/index.js rename to packages/edit-site/src/components/more-menu/index.js index f0df5cf57fe6f0..4c4e9bb45c093e 100644 --- a/packages/edit-site/src/components/header-edit-mode/more-menu/index.js +++ b/packages/edit-site/src/components/more-menu/index.js @@ -10,7 +10,7 @@ import { privateApis as editorPrivateApis } from '@wordpress/editor'; */ import SiteExport from './site-export'; import WelcomeGuideMenuItem from './welcome-guide-menu-item'; -import { unlock } from '../../../lock-unlock'; +import { unlock } from '../../lock-unlock'; const { ToolsMoreMenuGroup, PreferencesModal } = unlock( editorPrivateApis ); diff --git a/packages/edit-site/src/components/header-edit-mode/more-menu/site-export.js b/packages/edit-site/src/components/more-menu/site-export.js similarity index 100% rename from packages/edit-site/src/components/header-edit-mode/more-menu/site-export.js rename to packages/edit-site/src/components/more-menu/site-export.js diff --git a/packages/edit-site/src/components/header-edit-mode/more-menu/welcome-guide-menu-item.js b/packages/edit-site/src/components/more-menu/welcome-guide-menu-item.js similarity index 100% rename from packages/edit-site/src/components/header-edit-mode/more-menu/welcome-guide-menu-item.js rename to packages/edit-site/src/components/more-menu/welcome-guide-menu-item.js diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index 391db38fbc51b6..c20fff8851c829 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -498,7 +498,11 @@ export default function PagePages() { actions={ addNewLabel && ( <> - + { addNewLabel } { showAddPageModal && ( diff --git a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js b/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js deleted file mode 100644 index e01dca97c3be46..00000000000000 --- a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * WordPress dependencies - */ -import { __, _x, sprintf } from '@wordpress/i18n'; - -import { useDispatch } from '@wordpress/data'; -import { store as noticesStore } from '@wordpress/notices'; -import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; - -/** - * Internal dependencies - */ -import { unlock } from '../../lock-unlock'; -import { PATTERN_TYPES, TEMPLATE_PART_POST_TYPE } from '../../utils/constants'; -import { CreateTemplatePartModalContents } from '../create-template-part-modal'; - -const { CreatePatternModalContents, useDuplicatePatternProps } = - unlock( patternsPrivateApis ); - -export const duplicatePatternAction = { - id: 'duplicate-pattern', - label: _x( 'Duplicate', 'action label' ), - isEligible: ( item ) => item.type !== TEMPLATE_PART_POST_TYPE, - modalHeader: _x( 'Duplicate pattern', 'action label' ), - RenderModal: ( { items, closeModal } ) => { - const [ item ] = items; - const isThemePattern = item.type === PATTERN_TYPES.theme; - const duplicatedProps = useDuplicatePatternProps( { - pattern: isThemePattern ? item : item.patternPost, - onSuccess: () => closeModal(), - } ); - return ( - - ); - }, -}; - -export const duplicateTemplatePartAction = { - id: 'duplicate-template-part', - label: _x( 'Duplicate', 'action label' ), - isEligible: ( item ) => item.type === TEMPLATE_PART_POST_TYPE, - modalHeader: _x( 'Duplicate template part', 'action label' ), - RenderModal: ( { items, closeModal } ) => { - const [ item ] = items; - const { createSuccessNotice } = useDispatch( noticesStore ); - function onTemplatePartSuccess() { - createSuccessNotice( - sprintf( - // translators: %s: The new template part's title e.g. 'Call to action (copy)'. - __( '"%s" duplicated.' ), - item.title - ), - { type: 'snackbar', id: 'edit-site-patterns-success' } - ); - closeModal(); - } - return ( - - ); - }, -}; diff --git a/packages/edit-site/src/components/page-patterns/header.js b/packages/edit-site/src/components/page-patterns/header.js index ebb4a647f28ad9..a128daa6ed58f9 100644 --- a/packages/edit-site/src/components/page-patterns/header.js +++ b/packages/edit-site/src/components/page-patterns/header.js @@ -58,9 +58,12 @@ export default function PatternsHeader( { } return ( - - - + + + { title } @@ -96,7 +99,12 @@ export default function PatternsHeader( { { description ? ( - + { description } ) : null } diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js index eea2fd61221909..223ede9ac1d988 100644 --- a/packages/edit-site/src/components/page-patterns/index.js +++ b/packages/edit-site/src/components/page-patterns/index.js @@ -46,10 +46,6 @@ import { PATTERN_DEFAULT_CATEGORY, OPERATOR_IS, } from '../../utils/constants'; -import { - duplicatePatternAction, - duplicateTemplatePartAction, -} from './dataviews-pattern-actions'; import usePatternSettings from './use-pattern-settings'; import { unlock } from '../../lock-unlock'; import usePatterns from './use-patterns'; @@ -361,15 +357,9 @@ export default function DataviewsPatterns() { const actions = useMemo( () => { if ( type === TEMPLATE_PART_POST_TYPE ) { - return [ - editAction, - duplicateTemplatePartAction, - ...templatePartActions, - ].filter( Boolean ); + return [ editAction, ...templatePartActions ].filter( Boolean ); } - return [ editAction, duplicatePatternAction, ...patternActions ].filter( - Boolean - ); + return [ editAction, ...patternActions ].filter( Boolean ); }, [ editAction, type, templatePartActions, patternActions ] ); const onChangeView = useCallback( ( newView ) => { diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index af708356aa4b3e..6de771389a73d0 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -87,14 +87,22 @@ .edit-site-patterns__section-header { border-bottom: 1px solid #f0f0f0; - min-height: 72px; - padding: $grid-unit-30 $grid-unit-60; + padding: $grid-unit-20 $grid-unit-60; position: sticky; top: 0; z-index: 2; flex-shrink: 0; transition: padding ease-out 0.1s; @include reduce-motion("transition"); + min-height: $grid-unit-50; + + .edit-site-patterns__title { + min-height: $grid-unit-50; + } + + .edit-site-patterns__sub-title { + margin-bottom: $grid-unit-10; + } } .edit-site-patterns__pattern-title { diff --git a/packages/edit-site/src/components/page/header.js b/packages/edit-site/src/components/page/header.js index 274fd395a16f1d..f138fa8fae09d3 100644 --- a/packages/edit-site/src/components/page/header.js +++ b/packages/edit-site/src/components/page/header.js @@ -5,7 +5,7 @@ import { __experimentalHeading as Heading, __experimentalText as Text, __experimentalHStack as HStack, - FlexBlock, + __experimentalVStack as VStack, FlexItem, } from '@wordpress/components'; @@ -15,8 +15,8 @@ import { export default function Header( { title, subTitle, actions } ) { return ( - - + + { title } - { subTitle && ( - - { subTitle } - - ) } - - - { actions } - - + + { actions } + + + { subTitle && ( + + { subTitle } + + ) } + ); } diff --git a/packages/edit-site/src/components/page/style.scss b/packages/edit-site/src/components/page/style.scss index acf3a40d08b174..b2ec2b2f178448 100644 --- a/packages/edit-site/src/components/page/style.scss +++ b/packages/edit-site/src/components/page/style.scss @@ -17,15 +17,17 @@ z-index: z-index(".edit-site-page-header"); transition: padding ease-out 0.1s; @include reduce-motion("transition"); - .components-text { - color: $gray-800; - } + .components-heading { color: $gray-900; } + + .edit-site-page-header__page-title { + min-height: $grid-unit-50; + } + .edit-site-page-header__sub-title { - margin-top: $grid-unit-10; - color: $gray-700; + margin-bottom: $grid-unit-10; } } diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 1723a5a89a516a..b589a3861c75d3 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -263,7 +263,7 @@ function ResizableFrame( { } } minWidth={ FRAME_MIN_WIDTH } maxWidth={ isFullWidth ? '100%' : '150%' } - maxHeight={ '100%' } + maxHeight="100%" onFocus={ () => setShouldShowHandle( true ) } onBlur={ () => setShouldShowHandle( false ) } onMouseOver={ () => setShouldShowHandle( true ) } diff --git a/packages/edit-site/src/components/save-panel/index.js b/packages/edit-site/src/components/save-panel/index.js index 1f784edc52234f..1422014b08ccc8 100644 --- a/packages/edit-site/src/components/save-panel/index.js +++ b/packages/edit-site/src/components/save-panel/index.js @@ -153,7 +153,7 @@ export default function SavePanel() { variant="secondary" className="edit-site-editor__toggle-save-panel-button" onClick={ () => setIsSaveViewOpened( true ) } - aria-haspopup={ 'dialog' } + aria-haspopup="dialog" disabled={ disabled } __experimentalIsFocusable > diff --git a/packages/edit-site/src/components/sidebar-dataviews/default-views.js b/packages/edit-site/src/components/sidebar-dataviews/default-views.js index 6a10e0cd95d7b3..e75faa60b92e90 100644 --- a/packages/edit-site/src/components/sidebar-dataviews/default-views.js +++ b/packages/edit-site/src/components/sidebar-dataviews/default-views.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { trash, pages, drafts } from '@wordpress/icons'; +import { trash, pages, drafts, unseen, inbox } from '@wordpress/icons'; /** * Internal dependencies @@ -99,6 +99,36 @@ export const DEFAULT_VIEWS = { ], }, }, + { + title: __( 'Pending' ), + slug: 'pending', + icon: inbox, + view: { + ...DEFAULT_PAGE_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS_ANY, + value: 'pending', + }, + ], + }, + }, + { + title: __( 'Private' ), + slug: 'private', + icon: unseen, + view: { + ...DEFAULT_PAGE_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS_ANY, + value: 'private', + }, + ], + }, + }, { title: __( 'Trash' ), slug: 'trash', diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js index 31e359acb4027d..29ce24cfb3b7b1 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js @@ -96,7 +96,7 @@ function SidebarNavigationScreenGlobalStylesContent() { > { colorVariations?.length && ( - + ) } { typographyVariations?.length && ( { } } > - { decodeEntities( siteTitle ) } - - { aria-label={ __( 'View site (opens in a new tab)' ) } - icon={ external } - className="edit-site-site-hub__site-view-link" - /> - + > + { decodeEntities( siteTitle ) } + + + { - return { - canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), - activeSidebar: - select( interfaceStore ).getActiveComplementaryArea( 'core' ), - }; - }, [] ); - - if ( canvasMode !== 'edit' ) { - return { isLoading: false, commands: [] }; - } - - const commands = []; - - commands.push( { - name: 'core/open-settings-sidebar', - label: __( 'Toggle settings sidebar' ), - icon: isRTL() ? drawerLeft : drawerRight, - callback: ( { close } ) => { - close(); - if ( activeSidebar === 'edit-post/document' ) { - closeGeneralSidebar(); - } else { - openGeneralSidebar( 'edit-post/document' ); - } - }, - } ); - - commands.push( { - name: 'core/open-block-inspector', - label: __( 'Toggle block inspector' ), - icon: blockDefault, - callback: ( { close } ) => { - close(); - if ( activeSidebar === 'edit-site/block' ) { - closeGeneralSidebar(); - } else { - openGeneralSidebar( 'edit-site/block' ); - } - }, - } ); - - return { - isLoading: false, - commands, - }; -} - export function useEditModeCommands() { useCommandLoader( { name: 'core/edit-site/page-content-focus', @@ -233,9 +169,4 @@ export function useEditModeCommands() { name: 'core/edit-site/manipulate-document', hook: useManipulateDocumentCommands, } ); - - useCommandLoader( { - name: 'core/edit-site/edit-ui', - hook: useEditUICommands, - } ); } diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 7416b055cae7ce..6203c24196592b 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -6,14 +6,12 @@ @import "./components/global-styles/style.scss"; @import "./components/global-styles/screen-revisions/style.scss"; @import "./components/global-styles-sidebar/style.scss"; -@import "./components/header-edit-mode/style.scss"; @import "./components/page/style.scss"; @import "./components/page-pages/style.scss"; @import "./components/page-patterns/style.scss"; @import "./components/page-templates/style.scss"; @import "./components/table/style.scss"; @import "./components/editor/style.scss"; -@import "./components/create-template-part-modal/style.scss"; @import "./components/welcome-guide/style.scss"; @import "./components/layout/style.scss"; @import "./components/save-hub/style.scss"; diff --git a/packages/edit-site/src/utils/template-part-create.js b/packages/edit-site/src/utils/template-part-create.js index 955519a91a2344..e69de29bb2d1d6 100644 --- a/packages/edit-site/src/utils/template-part-create.js +++ b/packages/edit-site/src/utils/template-part-create.js @@ -1,67 +0,0 @@ -/** - * External dependencies - */ -import { paramCase as kebabCase } from 'change-case'; - -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { store as coreStore } from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import { TEMPLATE_PART_POST_TYPE } from './constants'; - -export const useExistingTemplateParts = () => { - return useSelect( - ( select ) => - select( coreStore ).getEntityRecords( - 'postType', - TEMPLATE_PART_POST_TYPE, - { - per_page: -1, - } - ), - [] - ); -}; - -/** - * Return a unique template part title based on - * the given title and existing template parts. - * - * @param {string} title The original template part title. - * @param {Object} templateParts The array of template part entities. - * @return {string} A unique template part title. - */ -export const getUniqueTemplatePartTitle = ( title, templateParts ) => { - const lowercaseTitle = title.toLowerCase(); - const existingTitles = templateParts.map( ( templatePart ) => - templatePart.title.rendered.toLowerCase() - ); - - if ( ! existingTitles.includes( lowercaseTitle ) ) { - return title; - } - - let suffix = 2; - while ( existingTitles.includes( `${ lowercaseTitle } ${ suffix }` ) ) { - suffix++; - } - - return `${ title } ${ suffix }`; -}; - -/** - * Get a valid slug for a template part. - * Currently template parts only allow latin chars. - * The fallback slug will receive suffix by default. - * - * @param {string} title The template part title. - * @return {string} A valid template part slug. - */ -export const getCleanTemplatePartSlug = ( title ) => { - return kebabCase( title ).replace( /[^\w-]+/g, '' ) || 'wp-custom-part'; -}; diff --git a/packages/edit-widgets/CHANGELOG.md b/packages/edit-widgets/CHANGELOG.md index df05e7281b7f21..c2551dfbe9df96 100644 --- a/packages/edit-widgets/CHANGELOG.md +++ b/packages/edit-widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 1d48948f81f77d..a11cfbf56273df 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-widgets", - "version": "5.35.0", + "version": "6.0.0", "description": "Widgets Page module for WordPress..", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index 7fb4b4ecf3196f..41ab4c4b5ca26c 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 14.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/editor/README.md b/packages/editor/README.md index f25cab506df1ef..6e887b6c0b82e6 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -350,7 +350,35 @@ _Returns_ ### EditorProvider -Undocumented declaration. +This component establishes a new post editing context, and serves as the entry point for a new post editor (or post with template editor). + +It supports a large number of post types, including post, page, templates, custom post types, patterns, template parts. + +All modification and changes are performed to the `@wordpress/core-data` store. + +_Usage_ + +```jsx + + { children } + +``` + +_Parameters_ + +- _props_ `Object`: The component props. +- _props.post_ `[Object]`: The post object to edit. This is required. +- _props.\_\_unstableTemplate_ `[Object]`: The template object wrapper the edited post. This is optional and can only be used when the post type supports templates (like posts and pages). +- _props.settings_ `[Object]`: The settings object to use for the editor. This is optional and can be used to override the default settings. +- _props.children_ `[Element]`: Children elements for which the BlockEditorProvider context should apply. This is optional. + +_Returns_ + +- `JSX.Element`: The rendered EditorProvider component. ### EditorSnackbars @@ -366,7 +394,11 @@ Undocumented declaration. ### ErrorBoundary -Undocumented declaration. +ErrorBoundary is used to catch JavaScript errors anywhere in a child component tree, log those errors, and display a fallback UI. + +It uses the lifecycle methods getDerivedStateFromError and componentDidCatch to catch errors in a child component tree. + +getDerivedStateFromError is used to render a fallback UI after an error has been thrown, and componentDidCatch is used to log error information. ### FontSizePicker @@ -422,7 +454,14 @@ _Returns_ ### LocalAutosaveMonitor -Undocumented declaration. +Monitors local autosaves of a post in the editor. It uses several hooks and functions to manage autosave behavior: + +- `useAutosaveNotice` hook: Manages the creation of a notice prompting the user to restore a local autosave, if one exists. +- `useAutosavePurge` hook: Ejects a local autosave after a successful save occurs. +- `hasSessionStorageSupport` function: Checks if the current environment supports browser sessionStorage. +- `LocalAutosaveMonitor` component: Uses the `AutosaveMonitor` component to perform autosaves at a specified interval. + +The module also checks for sessionStorage support and conditionally exports the `LocalAutosaveMonitor` component based on that. ### MediaPlaceholder @@ -953,7 +992,11 @@ _Returns_ ### PostDiscussionPanel -Undocumented declaration. +This component allows to update comment and pingback settings for the current post. Internally there are checks whether the current post has support for the above and if the `discussion-panel` panel is enabled. + +_Returns_ + +- `JSX.Element|null`: The rendered PostDiscussionPanel component. ### PostExcerpt @@ -980,7 +1023,11 @@ _Returns_ ### PostExcerptPanel -Undocumented declaration. +Is rendered if the post type supports excerpts and allows editing the excerpt. + +_Returns_ + +- `JSX.Element`: The rendered PostExcerptPanel component. ### PostFeaturedImage @@ -1094,19 +1141,45 @@ _Returns_ ### PostPendingStatus -Undocumented declaration. +A component for displaying and toggling the pending status of a post. + +_Returns_ + +- `JSX.Element`: The rendered component. ### PostPendingStatusCheck -Undocumented declaration. +This component checks the publishing status of the current post. If the post is already published or the user doesn't have the capability to publish, it returns null. + +_Parameters_ + +- _props_ `Object`: Component properties. +- _props.children_ `Element`: Children to be rendered. + +_Returns_ + +- `JSX.Element|null`: The rendered child elements or null if the post is already published or the user doesn't have the capability to publish. ### PostPingbacks -Undocumented declaration. +Renders a control for enabling or disabling pingbacks and trackbacks in a WordPress post. ### PostPreviewButton -Undocumented declaration. +Renders a button that opens a new window or tab for the preview, writes the interstitial message to this window, and then navigates to the actual preview link. The button is not rendered if the post is not viewable and disabled if the post is not saveable. + +_Parameters_ + +- _props_ `Object`: The component props. +- _props.className_ `string`: The class name for the button. +- _props.textContent_ `string`: The text content for the button. +- _props.forceIsAutosaveable_ `boolean`: Whether to force autosave. +- _props.role_ `string`: The role attribute for the button. +- _props.onPreview_ `Function`: The callback function for preview event. + +_Returns_ + +- `JSX.Element|null`: The rendered button component. ### PostPublishButton @@ -1242,7 +1315,11 @@ _Returns_ ### PostTextEditor -Undocumented declaration. +Displays the Post Text Editor along with content in Visual and Text mode. + +_Returns_ + +- `JSX.Element|null`: The rendered PostTextEditor component. ### PostTitle diff --git a/packages/editor/package.json b/packages/editor/package.json index d6d5731ebbc1fd..24461df0369318 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "13.35.0", + "version": "14.0.0", "description": "Enhanced block editor for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -70,6 +70,7 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "deepmerge": "^4.3.0", + "fast-deep-equal": "^3.1.3", "is-plain-object": "^5.0.0", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0", diff --git a/packages/editor/src/bindings/pattern-overrides.js b/packages/editor/src/bindings/pattern-overrides.js index 4065cefe362808..107ed72e722ba5 100644 --- a/packages/editor/src/bindings/pattern-overrides.js +++ b/packages/editor/src/bindings/pattern-overrides.js @@ -89,7 +89,5 @@ export default { }, } ); }, - lockAttributesEditing() { - return false; - }, + canUserEditValue: () => true, }; diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index f5b3b526dbfd4a..aec890c5ceff87 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -16,13 +16,53 @@ export default { return args.key; }, getValue( { registry, context, args } ) { - const postType = context.postType - ? context.postType - : registry.select( editorStore ).getCurrentPostType(); - return registry .select( coreDataStore ) - .getEditedEntityRecord( 'postType', postType, context.postId ) - .meta?.[ args.key ]; + .getEditedEntityRecord( + 'postType', + context?.postType, + context?.postId + ).meta?.[ args.key ]; + }, + setValue( { registry, context, args, value } ) { + registry + .dispatch( coreDataStore ) + .editEntityRecord( 'postType', context?.postType, context?.postId, { + meta: { + [ args.key ]: value, + }, + } ); + }, + canUserEditValue( { select, context, args } ) { + const postType = + context?.postType || select( editorStore ).getCurrentPostType(); + + // Check that editing is happening in the post editor and not a template. + if ( postType === 'wp_template' ) { + return false; + } + + // Check that the custom field is not protected and available in the REST API. + const isFieldExposed = !! select( coreDataStore ).getEntityRecord( + 'postType', + postType, + context?.postId + )?.meta?.[ args.key ]; + + if ( ! isFieldExposed ) { + return false; + } + + // Check that the user has the capability to edit post meta. + const canUserEdit = select( coreDataStore ).canUserEditEntityRecord( + 'postType', + context?.postType, + context?.postId + ); + if ( ! canUserEdit ) { + return false; + } + + return true; }, }; diff --git a/packages/editor/src/components/blog-title/index.js b/packages/editor/src/components/blog-title/index.js index a82498b5220f4d..4964ac3a0ec04d 100644 --- a/packages/editor/src/components/blog-title/index.js +++ b/packages/editor/src/components/blog-title/index.js @@ -107,7 +107,7 @@ export default function BlogTitle() { /> { const { get } = select( preferencesStore ); const { isListViewOpened, getCurrentPostType, getEditorSettings } = @@ -56,8 +61,11 @@ function useEditorCommandLoader() { isViewable: getPostType( getCurrentPostType() )?.viewable ?? false, isCodeEditingEnabled: getEditorSettings().codeEditingEnabled, isRichEditingEnabled: getEditorSettings().richEditingEnabled, + isPublishSidebarEnabled: + select( editorStore ).isPublishSidebarEnabled(), }; }, [] ); + const { getActiveComplementaryArea } = useSelect( interfaceStore ); const { toggle } = useDispatch( preferencesStore ); const { createInfoNotice } = useDispatch( noticesStore ); const { @@ -66,7 +74,8 @@ function useEditorCommandLoader() { switchEditorMode, toggleDistractionFree, } = useDispatch( editorStore ); - const { openModal } = useDispatch( interfaceStore ); + const { openModal, enableComplementaryArea, disableComplementaryArea } = + useDispatch( interfaceStore ); const { getCurrentPostId } = useSelect( editorStore ); const allowSwitchEditorMode = isCodeEditingEnabled && isRichEditingEnabled; @@ -211,6 +220,57 @@ function useEditorCommandLoader() { }, } ); + commands.push( { + name: 'core/open-settings-sidebar', + label: __( 'Toggle settings sidebar' ), + icon: isRTL() ? drawerLeft : drawerRight, + callback: ( { close } ) => { + const activeSidebar = getActiveComplementaryArea( 'core' ); + close(); + if ( activeSidebar === 'edit-post/document' ) { + disableComplementaryArea( 'core' ); + } else { + enableComplementaryArea( 'core', 'edit-post/document' ); + } + }, + } ); + + commands.push( { + name: 'core/open-block-inspector', + label: __( 'Toggle block inspector' ), + icon: blockDefault, + callback: ( { close } ) => { + const activeSidebar = getActiveComplementaryArea( 'core' ); + close(); + if ( activeSidebar === 'edit-post/block' ) { + disableComplementaryArea( 'core' ); + } else { + enableComplementaryArea( 'core', 'edit-post/block' ); + } + }, + } ); + + commands.push( { + name: 'core/toggle-publish-sidebar', + label: isPublishSidebarEnabled + ? __( 'Disable pre-publish checks' ) + : __( 'Enable pre-publish checks' ), + icon: formatListBullets, + callback: ( { close } ) => { + close(); + toggle( 'core', 'isPublishSidebarEnabled' ); + createInfoNotice( + isPublishSidebarEnabled + ? __( 'Pre-publish checks disabled.' ) + : __( 'Pre-publish checks enabled.' ), + { + id: 'core/editor/publish-sidebar/notice', + type: 'snackbar', + } + ); + }, + } ); + if ( isViewable ) { commands.push( { name: 'core/preview-link', diff --git a/packages/edit-site/src/components/create-template-part-modal/index.js b/packages/editor/src/components/create-template-part-modal/index.js similarity index 85% rename from packages/edit-site/src/components/create-template-part-modal/index.js rename to packages/editor/src/components/create-template-part-modal/index.js index a74225dd81404f..276ca200d39d0e 100644 --- a/packages/edit-site/src/components/create-template-part-modal/index.js +++ b/packages/editor/src/components/create-template-part-modal/index.js @@ -19,7 +19,6 @@ import { import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; -import { store as editorStore } from '@wordpress/editor'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; import { check } from '@wordpress/icons'; @@ -28,15 +27,16 @@ import { serialize } from '@wordpress/blocks'; /** * Internal dependencies */ +import { store as editorStore } from '../../store'; import { TEMPLATE_PART_POST_TYPE, TEMPLATE_PART_AREA_DEFAULT_CATEGORY, -} from '../../utils/constants'; +} from '../../store/constants'; import { useExistingTemplateParts, getUniqueTemplatePartTitle, getCleanTemplatePartSlug, -} from '../../utils/template-part-create'; +} from './utils'; export default function CreateTemplatePartModal( { modalTitle, @@ -52,7 +52,7 @@ export default function CreateTemplatePartModal( { @@ -142,13 +142,13 @@ export function CreateTemplatePartModalContents( { /> @@ -157,18 +157,18 @@ export function CreateTemplatePartModalContents( { - + { label } { description } - + { area === value && ( ) } diff --git a/packages/edit-site/src/components/create-template-part-modal/style.scss b/packages/editor/src/components/create-template-part-modal/style.scss similarity index 69% rename from packages/edit-site/src/components/create-template-part-modal/style.scss rename to packages/editor/src/components/create-template-part-modal/style.scss index 1dba3be142cea2..907e42bf52ea47 100644 --- a/packages/edit-site/src/components/create-template-part-modal/style.scss +++ b/packages/editor/src/components/create-template-part-modal/style.scss @@ -1,5 +1,5 @@ -.edit-site-create-template-part-modal { - z-index: z-index(".edit-site-create-template-part-modal"); +.editor-create-template-part-modal { + z-index: z-index(".editor-create-template-part-modal"); .components-modal__frame { @include break-small { @@ -8,12 +8,12 @@ } } -.edit-site-create-template-part-modal__area-radio-group { +.editor-create-template-part-modal__area-radio-group { width: 100%; border: $border-width solid $gray-700; border-radius: 2px; - .components-button.edit-site-create-template-part-modal__area-radio { + .components-button.editor-create-template-part-modal__area-radio { display: block; width: 100%; height: 100%; @@ -46,12 +46,12 @@ color: $gray-900; cursor: auto; - .edit-site-create-template-part-modal__option-label div { + .editor-create-template-part-modal__option-label div { color: $gray-600; } } - .edit-site-create-template-part-modal__option-label { + .editor-create-template-part-modal__option-label { padding-top: $grid-unit-05; white-space: normal; @@ -61,7 +61,7 @@ } } - .edit-site-create-template-part-modal__checkbox { + .editor-create-template-part-modal__checkbox { margin-left: auto; min-width: $grid-unit-30; } diff --git a/packages/edit-site/src/utils/test/template-part-create.js b/packages/editor/src/components/create-template-part-modal/test/utils.js similarity index 93% rename from packages/edit-site/src/utils/test/template-part-create.js rename to packages/editor/src/components/create-template-part-modal/test/utils.js index e39513ccf7e4f4..7e33c16f46d8e8 100644 --- a/packages/edit-site/src/utils/test/template-part-create.js +++ b/packages/editor/src/components/create-template-part-modal/test/utils.js @@ -1,10 +1,7 @@ /** * Internal dependencies */ -import { - getUniqueTemplatePartTitle, - getCleanTemplatePartSlug, -} from '../template-part-create'; +import { getUniqueTemplatePartTitle, getCleanTemplatePartSlug } from '../utils'; describe( 'getUniqueTemplatePartTitle', () => { it( 'should return the title if it is unique', () => { diff --git a/packages/editor/src/components/create-template-part-modal/utils.js b/packages/editor/src/components/create-template-part-modal/utils.js new file mode 100644 index 00000000000000..02f24cf17d2be9 --- /dev/null +++ b/packages/editor/src/components/create-template-part-modal/utils.js @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +import { paramCase as kebabCase } from 'change-case'; + +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import { TEMPLATE_PART_POST_TYPE } from '../../store/constants'; + +export const useExistingTemplateParts = () => { + return useSelect( + ( select ) => + select( coreStore ).getEntityRecords( + 'postType', + TEMPLATE_PART_POST_TYPE, + { + per_page: -1, + } + ), + [] + ); +}; + +/** + * Return a unique template part title based on + * the given title and existing template parts. + * + * @param {string} title The original template part title. + * @param {Object} templateParts The array of template part entities. + * @return {string} A unique template part title. + */ +export const getUniqueTemplatePartTitle = ( title, templateParts ) => { + const lowercaseTitle = title.toLowerCase(); + const existingTitles = templateParts.map( ( templatePart ) => + templatePart.title.rendered.toLowerCase() + ); + + if ( ! existingTitles.includes( lowercaseTitle ) ) { + return title; + } + + let suffix = 2; + while ( existingTitles.includes( `${ lowercaseTitle } ${ suffix }` ) ) { + suffix++; + } + + return `${ title } ${ suffix }`; +}; + +/** + * Get a valid slug for a template part. + * Currently template parts only allow latin chars. + * The fallback slug will receive suffix by default. + * + * @param {string} title The template part title. + * @return {string} A valid template part slug. + */ +export const getCleanTemplatePartSlug = ( title ) => { + return kebabCase( title ).replace( /[^\w-]+/g, '' ) || 'wp-custom-part'; +}; diff --git a/packages/editor/src/components/editor-help/help-get-support-button.native.js b/packages/editor/src/components/editor-help/help-get-support-button.native.js index 35802379fe9e47..5d6ad0a9dc4d33 100644 --- a/packages/editor/src/components/editor-help/help-get-support-button.native.js +++ b/packages/editor/src/components/editor-help/help-get-support-button.native.js @@ -28,7 +28,7 @@ const HelpGetSupportButton = ( { onPress, title } ) => { { title } diff --git a/packages/editor/src/components/editor-interface/content-slot-fill.js b/packages/editor/src/components/editor-interface/content-slot-fill.js new file mode 100644 index 00000000000000..1aab394e87230f --- /dev/null +++ b/packages/editor/src/components/editor-interface/content-slot-fill.js @@ -0,0 +1,15 @@ +/** + * WordPress dependencies + */ +import { privateApis as componentsPrivateApis } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; + +const { createPrivateSlotFill } = unlock( componentsPrivateApis ); +const SLOT_FILL_NAME = 'EditCanvasContainerSlot'; +const EditorContentSlotFill = createPrivateSlotFill( SLOT_FILL_NAME ); + +export default EditorContentSlotFill; diff --git a/packages/editor/src/components/editor-interface/index.js b/packages/editor/src/components/editor-interface/index.js new file mode 100644 index 00000000000000..b78e3caa0b57cb --- /dev/null +++ b/packages/editor/src/components/editor-interface/index.js @@ -0,0 +1,230 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { InterfaceSkeleton, ComplementaryArea } from '@wordpress/interface'; +import { useSelect } from '@wordpress/data'; +import { __, _x } from '@wordpress/i18n'; +import { store as preferencesStore } from '@wordpress/preferences'; +import { + store as blockEditorStore, + BlockBreadcrumb, + BlockToolbar, +} from '@wordpress/block-editor'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useViewportMatch } from '@wordpress/compose'; +import { useState, useCallback } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { store as editorStore } from '../../store'; +import EditorNotices from '../editor-notices'; +import Header from '../header'; +import InserterSidebar from '../inserter-sidebar'; +import ListViewSidebar from '../list-view-sidebar'; +import SavePublishPanels from '../save-publish-panels'; +import TextEditor from '../text-editor'; +import VisualEditor from '../visual-editor'; +import EditorContentSlotFill from './content-slot-fill'; + +const interfaceLabels = { + /* translators: accessibility text for the editor top bar landmark region. */ + header: __( 'Editor top bar' ), + /* translators: accessibility text for the editor content landmark region. */ + body: __( 'Editor content' ), + /* translators: accessibility text for the editor settings landmark region. */ + sidebar: __( 'Editor settings' ), + /* translators: accessibility text for the editor publish landmark region. */ + actions: __( 'Editor publish' ), + /* translators: accessibility text for the editor footer landmark region. */ + footer: __( 'Editor footer' ), +}; + +export default function EditorInterface( { + className, + enableRegionNavigation, + styles, + children, + forceIsDirty, + contentRef, + disableIframe, + autoFocus, + customSaveButton, + forceDisableBlockTools, + title, + iframeProps, +} ) { + const { + mode, + isRichEditingEnabled, + isInserterOpened, + isListViewOpened, + isDistractionFree, + isPreviewMode, + previousShortcut, + nextShortcut, + showBlockBreadcrumbs, + documentLabel, + blockEditorMode, + } = useSelect( ( select ) => { + const { get } = select( preferencesStore ); + const { getEditorSettings, getPostTypeLabel } = select( editorStore ); + const editorSettings = getEditorSettings(); + const postTypeLabel = getPostTypeLabel(); + + return { + mode: select( editorStore ).getEditorMode(), + isRichEditingEnabled: editorSettings.richEditingEnabled, + isInserterOpened: select( editorStore ).isInserterOpened(), + isListViewOpened: select( editorStore ).isListViewOpened(), + isDistractionFree: get( 'core', 'distractionFree' ), + isPreviewMode: editorSettings.__unstableIsPreviewMode, + previousShortcut: select( + keyboardShortcutsStore + ).getAllShortcutKeyCombinations( 'core/editor/previous-region' ), + nextShortcut: select( + keyboardShortcutsStore + ).getAllShortcutKeyCombinations( 'core/editor/next-region' ), + showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), + // translators: Default label for the Document in the Block Breadcrumb. + documentLabel: postTypeLabel || _x( 'Document', 'noun' ), + blockEditorMode: + select( blockEditorStore ).__unstableGetEditorMode(), + }; + }, [] ); + const isWideViewport = useViewportMatch( 'large' ); + const isLargeViewport = useViewportMatch( 'medium' ); + const secondarySidebarLabel = isListViewOpened + ? __( 'Document Overview' ) + : __( 'Block Library' ); + + // Local state for save panel. + // Note 'truthy' callback implies an open panel. + const [ entitiesSavedStatesCallback, setEntitiesSavedStatesCallback ] = + useState( false ); + const closeEntitiesSavedStates = useCallback( + ( arg ) => { + if ( typeof entitiesSavedStatesCallback === 'function' ) { + entitiesSavedStatesCallback( arg ); + } + setEntitiesSavedStatesCallback( false ); + }, + [ entitiesSavedStatesCallback ] + ); + + return ( + + ) + } + editorNotices={ } + secondarySidebar={ + ! isPreviewMode && + mode === 'visual' && + ( ( isInserterOpened && ) || + ( isListViewOpened && ) ) + } + sidebar={ + ! isPreviewMode && + ! isDistractionFree && + } + content={ + <> + { ! isDistractionFree && ! isPreviewMode && ( + + ) } + + + { ( [ editorCanvasView ] ) => + ! isPreviewMode && editorCanvasView ? ( + editorCanvasView + ) : ( + <> + { ! isPreviewMode && + ( mode === 'text' || + ! isRichEditingEnabled ) && ( + + ) } + { ! isPreviewMode && + ! isLargeViewport && + mode === 'visual' && ( + + ) } + { ( isPreviewMode || + ( isRichEditingEnabled && + mode === 'visual' ) ) && ( + + ) } + { children } + > + ) + } + + > + } + footer={ + ! isPreviewMode && + ! isDistractionFree && + isLargeViewport && + showBlockBreadcrumbs && + isRichEditingEnabled && + blockEditorMode !== 'zoom-out' && + mode === 'visual' && ( + + + + ) + } + actions={ + + } + shortcuts={ { + previous: previousShortcut, + next: nextShortcut, + } } + /> + ); +} diff --git a/packages/editor/src/components/entities-saved-states/entity-record-item.js b/packages/editor/src/components/entities-saved-states/entity-record-item.js index 15bdc8c2f22842..49733489b0a117 100644 --- a/packages/editor/src/components/entities-saved-states/entity-record-item.js +++ b/packages/editor/src/components/entities-saved-states/entity-record-item.js @@ -1,25 +1,32 @@ /** * WordPress dependencies */ -import { CheckboxControl, PanelRow } from '@wordpress/components'; +import { Icon, CheckboxControl, Flex, PanelRow } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; +import { connection } from '@wordpress/icons'; /** * Internal dependencies */ import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; export default function EntityRecordItem( { record, checked, onChange } ) { const { name, kind, title, key } = record; // Handle templates that might use default descriptive titles. - const entityRecordTitle = useSelect( + const { entityRecordTitle, hasPostMetaChanges } = useSelect( ( select ) => { if ( 'postType' !== kind || 'wp_template' !== name ) { - return title; + return { + entityRecordTitle: title, + hasPostMetaChanges: unlock( + select( editorStore ) + ).hasPostMetaChanges( name, key ), + }; } const template = select( coreStore ).getEditedEntityRecord( @@ -27,23 +34,45 @@ export default function EntityRecordItem( { record, checked, onChange } ) { name, key ); - return select( editorStore ).__experimentalGetTemplateInfo( - template - ).title; + return { + entityRecordTitle: + select( editorStore ).__experimentalGetTemplateInfo( + template + ).title, + hasPostMetaChanges: unlock( + select( editorStore ) + ).hasPostMetaChanges( name, key ), + }; }, [ name, kind, title, key ] ); return ( - - - + <> + + + + { hasPostMetaChanges && ( + + + + + { __( 'Post Meta.' ) } + + + + ) } + > ); } diff --git a/packages/editor/src/components/entities-saved-states/style.scss b/packages/editor/src/components/entities-saved-states/style.scss index f4e0c9f814c24f..30317a5367f65e 100644 --- a/packages/editor/src/components/entities-saved-states/style.scss +++ b/packages/editor/src/components/entities-saved-states/style.scss @@ -37,3 +37,16 @@ height: $header-height + $border-width; } } + +.entities-saved-states__post-meta { + margin-left: $grid-unit-30; + align-items: center; +} + +.entities-saved-states__connections-icon { + flex-grow: 0; +} + +.entities-saved-states__bindings-text { + flex-grow: 1; +} diff --git a/packages/editor/src/components/error-boundary/index.js b/packages/editor/src/components/error-boundary/index.js index 0b5158ff7b9e55..966ea433c0d643 100644 --- a/packages/editor/src/components/error-boundary/index.js +++ b/packages/editor/src/components/error-boundary/index.js @@ -75,4 +75,14 @@ class ErrorBoundary extends Component { } } +/** + * ErrorBoundary is used to catch JavaScript errors anywhere in a child component tree, log those errors, and display a fallback UI. + * + * It uses the lifecycle methods getDerivedStateFromError and componentDidCatch to catch errors in a child component tree. + * + * getDerivedStateFromError is used to render a fallback UI after an error has been thrown, and componentDidCatch is used to log error information. + * + * @class ErrorBoundary + * @augments Component + */ export default ErrorBoundary; diff --git a/packages/editor/src/components/error-boundary/index.native.js b/packages/editor/src/components/error-boundary/index.native.js index d627bfa0e7893c..0de048e8114451 100644 --- a/packages/editor/src/components/error-boundary/index.native.js +++ b/packages/editor/src/components/error-boundary/index.native.js @@ -69,7 +69,7 @@ function CopyButton( { activeOpacity={ 0.5 } accessibilityLabel={ accessibilityLabel } style={ [ containerStyle, secondary && containerSecondaryStyle ] } - accessibilityRole={ 'button' } + accessibilityRole="button" accessibilityHint={ accessibilityHint } onPress={ () => { Clipboard.setString( diff --git a/packages/edit-post/src/components/header/main-dashboard-button/index.js b/packages/editor/src/components/header/back-button.js similarity index 55% rename from packages/edit-post/src/components/header/main-dashboard-button/index.js rename to packages/editor/src/components/header/back-button.js index 0878a7cce49012..7b775d11cd7a83 100644 --- a/packages/edit-post/src/components/header/main-dashboard-button/index.js +++ b/packages/editor/src/components/header/back-button.js @@ -6,13 +6,13 @@ import { createSlotFill, } from '@wordpress/components'; +// Keeping an old name for backward compatibility. const slotName = '__experimentalMainDashboardButton'; -const { Fill, Slot: MainDashboardButtonSlot } = createSlotFill( slotName ); +const { Fill, Slot } = createSlotFill( slotName ); -const MainDashboardButton = Fill; - -const Slot = ( { children } ) => { +const BackButton = Fill; +const BackButtonSlot = ( { children } ) => { const fills = useSlotFills( slotName ); const hasFills = Boolean( fills && fills.length ); @@ -20,9 +20,8 @@ const Slot = ( { children } ) => { return children; } - return ; + return ; }; +BackButton.Slot = BackButtonSlot; -MainDashboardButton.Slot = Slot; - -export default MainDashboardButton; +export default BackButton; diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js index 98501386434b69..4f1df093176a7d 100644 --- a/packages/editor/src/components/header/index.js +++ b/packages/editor/src/components/header/index.js @@ -17,6 +17,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; /** * Internal dependencies */ +import BackButton from './back-button'; import CollapsableBlockToolbar from '../collapsible-block-toolbar'; import DocumentBar from '../document-bar'; import DocumentTools from '../document-tools'; @@ -29,10 +30,20 @@ import PostViewLink from '../post-view-link'; import PreviewDropdown from '../preview-dropdown'; import { store as editorStore } from '../../store'; -const slideY = { - hidden: { y: '-50px' }, - distractionFreeInactive: { y: 0 }, - hover: { y: 0, transition: { type: 'tween', delay: 0.2 } }, +const toolbarVariations = { + distractionFreeDisabled: { y: '-50px' }, + distractionFreeHover: { y: 0 }, + distractionFreeHidden: { y: '-50px' }, + visible: { y: 0 }, + hidden: { y: 0 }, +}; + +const backButtonVariations = { + distractionFreeDisabled: { x: '-100%' }, + distractionFreeHover: { x: 0 }, + distractionFreeHidden: { x: '-100%' }, + visible: { x: 0 }, + hidden: { x: 0 }, }; function Header( { @@ -41,7 +52,6 @@ function Header( { forceDisableBlockTools, setEntitiesSavedStatesCallback, title, - children, } ) { const isWideViewport = useViewportMatch( 'large' ); const isLargeViewport = useViewportMatch( 'medium' ); @@ -81,11 +91,16 @@ function Header( { // as some plugins might be relying on its presence. return ( - { children } + + + { ! customSaveButton && ! isPublishSidebarOpened && ( diff --git a/packages/editor/src/components/inserter-sidebar/index.js b/packages/editor/src/components/inserter-sidebar/index.js index 25927c57650f41..11510c21e4300e 100644 --- a/packages/editor/src/components/inserter-sidebar/index.js +++ b/packages/editor/src/components/inserter-sidebar/index.js @@ -3,8 +3,8 @@ */ import { useDispatch, useSelect } from '@wordpress/data'; import { - __experimentalLibrary as Library, store as blockEditorStore, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { useViewportMatch, @@ -13,6 +13,7 @@ import { import { useCallback, useRef } from '@wordpress/element'; import { store as preferencesStore } from '@wordpress/preferences'; import { ESCAPE } from '@wordpress/keycodes'; +import { store as interfaceStore } from '@wordpress/interface'; /** * Internal dependencies @@ -20,22 +21,25 @@ import { ESCAPE } from '@wordpress/keycodes'; import { unlock } from '../../lock-unlock'; import { store as editorStore } from '../../store'; -export default function InserterSidebar( { - closeGeneralSidebar, - isRightSidebarOpen, -} ) { +const { PrivateInserterLibrary } = unlock( blockEditorPrivateApis ); + +export default function InserterSidebar() { const { blockSectionRootClientId, inserterSidebarToggleRef, insertionPoint, showMostUsedBlocks, + sidebarIsOpened, } = useSelect( ( select ) => { - const { getInserterSidebarToggleRef, getInsertionPoint } = unlock( - select( editorStore ) - ); + const { + getInserterSidebarToggleRef, + getInsertionPoint, + isPublishSidebarOpened, + } = unlock( select( editorStore ) ); const { getBlockRootClientId, __unstableGetEditorMode, getSettings } = select( blockEditorStore ); const { get } = select( preferencesStore ); + const { getActiveComplementaryArea } = select( interfaceStore ); const getBlockSectionRootClientId = () => { if ( __unstableGetEditorMode() === 'zoom-out' ) { const { sectionRootClientId } = unlock( getSettings() ); @@ -50,9 +54,13 @@ export default function InserterSidebar( { insertionPoint: getInsertionPoint(), showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ), blockSectionRootClientId: getBlockSectionRootClientId(), + sidebarIsOpened: !! ( + getActiveComplementaryArea( 'core' ) || isPublishSidebarOpened() + ), }; }, [] ); const { setIsInserterOpened } = useDispatch( editorStore ); + const { disableComplementaryArea } = useDispatch( interfaceStore ); const isMobileViewport = useViewportMatch( 'medium', '<' ); const [ inserterDialogRef, inserterDialogProps ] = useDialog( { @@ -79,7 +87,7 @@ export default function InserterSidebar( { const inserterContents = ( - disableComplementaryArea( 'core' ) + : undefined } ref={ libraryRef } onClose={ closeInserterSidebar } diff --git a/packages/editor/src/components/local-autosave-monitor/index.js b/packages/editor/src/components/local-autosave-monitor/index.js index 185497710f35f9..f999cf9ef85340 100644 --- a/packages/editor/src/components/local-autosave-monitor/index.js +++ b/packages/editor/src/components/local-autosave-monitor/index.js @@ -192,4 +192,16 @@ function LocalAutosaveMonitor() { ); } +/** + * Monitors local autosaves of a post in the editor. + * It uses several hooks and functions to manage autosave behavior: + * - `useAutosaveNotice` hook: Manages the creation of a notice prompting the user to restore a local autosave, if one exists. + * - `useAutosavePurge` hook: Ejects a local autosave after a successful save occurs. + * - `hasSessionStorageSupport` function: Checks if the current environment supports browser sessionStorage. + * - `LocalAutosaveMonitor` component: Uses the `AutosaveMonitor` component to perform autosaves at a specified interval. + * + * The module also checks for sessionStorage support and conditionally exports the `LocalAutosaveMonitor` component based on that. + * + * @module LocalAutosaveMonitor + */ export default ifCondition( hasSessionStorageSupport )( LocalAutosaveMonitor ); diff --git a/packages/editor/src/components/page-attributes/order.js b/packages/editor/src/components/page-attributes/order.js index d8a22f0cc949fb..17746ca8237d6f 100644 --- a/packages/editor/src/components/page-attributes/order.js +++ b/packages/editor/src/components/page-attributes/order.js @@ -1,18 +1,22 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { + Button, + Dropdown, Flex, FlexBlock, __experimentalNumberControl as NumberControl, } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useState } from '@wordpress/element'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; /** * Internal dependencies */ +import PostPanelRow from '../post-panel-row'; import PostTypeSupportCheck from '../post-type-support-check'; import { store as editorStore } from '../../store'; @@ -41,9 +45,10 @@ function PageAttributesOrder() { { setOrderInput( null ); } } @@ -66,3 +71,74 @@ export default function PageAttributesOrderWithChecks() { ); } + +function PostOrderToggle( { isOpen, onClick } ) { + const order = useSelect( + ( select ) => + select( editorStore ).getEditedPostAttribute( 'menu_order' ) ?? 0, + [] + ); + return ( + + { order } + + ); +} + +export function OrderRow() { + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + + + + { __( + 'This attribute determines the order of pages in the Pages List block.' + ) } + + { __( + 'Pages with the same order value will sorted alphabetically. Negative order values are also supported.' + ) } + + + + + ) } + /> + + ); +} diff --git a/packages/editor/src/components/page-attributes/panel.js b/packages/editor/src/components/page-attributes/panel.js index 5fb2638b517310..69bbf96206547c 100644 --- a/packages/editor/src/components/page-attributes/panel.js +++ b/packages/editor/src/components/page-attributes/panel.js @@ -1,54 +1,38 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; -import { PanelBody, PanelRow } from '@wordpress/components'; - -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; - /** * Internal dependencies */ import { store as editorStore } from '../../store'; import PageAttributesCheck from './check'; -import PageAttributesOrder from './order'; -import PageAttributesParent from './parent'; +import { OrderRow } from './order'; +import { ParentRow } from './parent'; const PANEL_NAME = 'page-attributes'; function AttributesPanel() { - const { isEnabled, isOpened, postType } = useSelect( ( select ) => { - const { - getEditedPostAttribute, - isEditorPanelEnabled, - isEditorPanelOpened, - } = select( editorStore ); + const { isEnabled, postType } = useSelect( ( select ) => { + const { getEditedPostAttribute, isEditorPanelEnabled } = + select( editorStore ); const { getPostType } = select( coreStore ); return { isEnabled: isEditorPanelEnabled( PANEL_NAME ), - isOpened: isEditorPanelOpened( PANEL_NAME ), postType: getPostType( getEditedPostAttribute( 'type' ) ), }; }, [] ); - const { toggleEditorPanelOpened } = useDispatch( editorStore ); - if ( ! isEnabled || ! postType ) { return null; } return ( - toggleEditorPanelOpened( PANEL_NAME ) } - > - - - - - + <> + + + > ); } diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index 900f6f8a7c66b2..ab52ca21a76ea8 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -6,17 +6,24 @@ import removeAccents from 'remove-accents'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; -import { ComboboxControl } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { + Button, + Dropdown, + ComboboxControl, + ExternalLink, +} from '@wordpress/components'; import { debounce } from '@wordpress/compose'; import { useState, useMemo } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { decodeEntities } from '@wordpress/html-entities'; import { store as coreStore } from '@wordpress/core-data'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; /** * Internal dependencies */ +import PostPanelRow from '../post-panel-row'; import { buildTermsTree } from '../../utils/terms'; import { store as editorStore } from '../../store'; @@ -169,12 +176,101 @@ export function PageAttributesParent() { __next40pxDefaultSize className="editor-page-attributes__parent" label={ __( 'Parent' ) } + help={ __( 'Choose a parent page.' ) } value={ parentPostId } options={ parentOptions } onFilterValueChange={ debounce( handleKeydown, 300 ) } onChange={ handleChange } + hideLabelFromVision /> ); } +function PostParentToggle( { isOpen, onClick } ) { + const parentPost = useSelect( ( select ) => { + const { getEditedPostAttribute } = select( editorStore ); + const parentPostId = getEditedPostAttribute( 'parent' ); + if ( ! parentPostId ) { + return null; + } + const { getEntityRecord } = select( coreStore ); + const postTypeSlug = getEditedPostAttribute( 'type' ); + return getEntityRecord( 'postType', postTypeSlug, parentPostId ); + }, [] ); + const parentTitle = useMemo( + () => ( ! parentPost ? __( 'None' ) : getTitle( parentPost ) ), + [ parentPost ] + ); + return ( + + { parentTitle } + + ); +} + +export function ParentRow() { + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + + + + { __( + "Child pages inherit characteristics from their parent, such as URL structure. For instance, if 'Web Design' is a child of 'Services,' its URL would be mysite.com/services/web-design." + ) } + + { __( + 'They also show up as sub-items in the default navigation menu. ' + ) } + + { __( 'Learn more' ) } + + + + + + ) } + /> + + ); +} + export default PageAttributesParent; diff --git a/packages/editor/src/components/page-attributes/style.scss b/packages/editor/src/components/page-attributes/style.scss new file mode 100644 index 00000000000000..6563462a65bc23 --- /dev/null +++ b/packages/editor/src/components/page-attributes/style.scss @@ -0,0 +1,19 @@ +.editor-post-parent__panel, +.editor-post-order__panel { + padding-top: $grid-unit-10; + .editor-post-panel__row-control > div { + width: 100%; + } +} + +.editor-post-parent__panel-dialog, +.editor-post-order__panel-dialog { + .editor-post-parent, + .editor-post-order { + margin: $grid-unit-10; + } + + .components-popover__content { + min-width: 320px; + } +} diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index eafa41741cbe2e..e12195e63c0df3 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -32,9 +32,11 @@ import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; import isTemplateRevertable from '../../store/utils/is-template-revertable'; import { exportPatternAsJSONAction } from './export-pattern-action'; +import { CreateTemplatePartModalContents } from '../create-template-part-modal'; // Patterns. -const { PATTERN_TYPES } = unlock( patternsPrivateApis ); +const { PATTERN_TYPES, CreatePatternModalContents, useDuplicatePatternProps } = + unlock( patternsPrivateApis ); /** * Check if a template is removable. @@ -968,6 +970,65 @@ const resetTemplateAction = { }, }; +export const duplicatePatternAction = { + id: 'duplicate-pattern', + label: _x( 'Duplicate', 'action label' ), + isEligible: ( item ) => item.type !== TEMPLATE_PART_POST_TYPE, + modalHeader: _x( 'Duplicate pattern', 'action label' ), + RenderModal: ( { items, closeModal } ) => { + const [ item ] = items; + const isThemePattern = item.type === PATTERN_TYPES.theme; + const duplicatedProps = useDuplicatePatternProps( { + pattern: + isThemePattern || ! item.patternPost ? item : item.patternPost, + onSuccess: () => closeModal(), + } ); + return ( + + ); + }, +}; + +export const duplicateTemplatePartAction = { + id: 'duplicate-template-part', + label: _x( 'Duplicate', 'action label' ), + isEligible: ( item ) => item.type === TEMPLATE_PART_POST_TYPE, + modalHeader: _x( 'Duplicate template part', 'action label' ), + RenderModal: ( { items, closeModal } ) => { + const [ item ] = items; + const { createSuccessNotice } = useDispatch( noticesStore ); + function onTemplatePartSuccess() { + createSuccessNotice( + sprintf( + // translators: %s: The new template part's title e.g. 'Call to action (copy)'. + __( '"%s" duplicated.' ), + item.title + ), + { type: 'snackbar', id: 'edit-site-patterns-success' } + ); + closeModal(); + } + return ( + + ); + }, +}; + export function usePostActions( postType, onActionPerformed ) { const { postTypeObject } = useSelect( ( select ) => { @@ -1001,6 +1062,8 @@ export function usePostActions( postType, onActionPerformed ) { ! isPattern && duplicatePostAction : false, + isTemplateOrTemplatePart && duplicateTemplatePartAction, + isPattern && duplicatePatternAction, renamePostAction, isPattern && exportPatternAsJSONAction, isTemplateOrTemplatePart ? resetTemplateAction : restorePostAction, diff --git a/packages/editor/src/components/post-author/panel.js b/packages/editor/src/components/post-author/panel.js index 908617a86f2900..5c162cfa50377f 100644 --- a/packages/editor/src/components/post-author/panel.js +++ b/packages/editor/src/components/post-author/panel.js @@ -58,7 +58,6 @@ export function PostAuthor() { ( diff --git a/packages/editor/src/components/post-card-panel/index.js b/packages/editor/src/components/post-card-panel/index.js index 5804d0955b94be..5aebfb650bfdbc 100644 --- a/packages/editor/src/components/post-card-panel/index.js +++ b/packages/editor/src/components/post-card-panel/index.js @@ -28,42 +28,52 @@ import { import { unlock } from '../../lock-unlock'; export default function PostCardPanel( { actions } ) { - const { title, icon, isSync } = useSelect( ( select ) => { - const { - getEditedPostAttribute, - getCurrentPostType, - getCurrentPostId, - __experimentalGetTemplateInfo, - } = select( editorStore ); - const { getEditedEntityRecord } = select( coreStore ); - const _type = getCurrentPostType(); - const _id = getCurrentPostId(); - const _record = getEditedEntityRecord( 'postType', _type, _id ); - const _templateInfo = - [ TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE ].includes( _type ) && - __experimentalGetTemplateInfo( _record ); - let _isSync = false; - if ( GLOBAL_POST_TYPES.includes( _type ) ) { - if ( PATTERN_POST_TYPE === _type ) { - // When the post is first created, the top level wp_pattern_sync_status is not set so get meta value instead. - const currentSyncStatus = - getEditedPostAttribute( 'meta' )?.wp_pattern_sync_status === - 'unsynced' - ? 'unsynced' - : getEditedPostAttribute( 'wp_pattern_sync_status' ); - _isSync = currentSyncStatus !== 'unsynced'; - } else { - _isSync = true; + const { isFrontPage, isPostsPage, title, icon, isSync } = useSelect( + ( select ) => { + const { + getEditedPostAttribute, + getCurrentPostType, + getCurrentPostId, + __experimentalGetTemplateInfo, + } = select( editorStore ); + const { getEditedEntityRecord } = select( coreStore ); + const siteSettings = getEditedEntityRecord( 'root', 'site' ); + const _type = getCurrentPostType(); + const _id = getCurrentPostId(); + const _record = getEditedEntityRecord( 'postType', _type, _id ); + const _templateInfo = + [ TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE ].includes( + _type + ) && __experimentalGetTemplateInfo( _record ); + let _isSync = false; + if ( GLOBAL_POST_TYPES.includes( _type ) ) { + if ( PATTERN_POST_TYPE === _type ) { + // When the post is first created, the top level wp_pattern_sync_status is not set so get meta value instead. + const currentSyncStatus = + getEditedPostAttribute( 'meta' ) + ?.wp_pattern_sync_status === 'unsynced' + ? 'unsynced' + : getEditedPostAttribute( + 'wp_pattern_sync_status' + ); + _isSync = currentSyncStatus !== 'unsynced'; + } else { + _isSync = true; + } } - } - return { - title: _templateInfo?.title || getEditedPostAttribute( 'title' ), - icon: unlock( select( editorStore ) ).getPostIcon( _type, { - area: _record?.area, - } ), - isSync: _isSync, - }; - }, [] ); + return { + title: + _templateInfo?.title || getEditedPostAttribute( 'title' ), + icon: unlock( select( editorStore ) ).getPostIcon( _type, { + area: _record?.area, + } ), + isSync: _isSync, + isFrontPage: siteSettings?.page_on_front === _id, + isPostsPage: siteSettings?.page_for_posts === _id, + }; + }, + [] + ); return ( { title ? decodeEntities( title ) : __( 'No Title' ) } + { isFrontPage && ( + + { __( 'Front Page' ) } + + ) } + { isPostsPage && ( + + { __( 'Posts Page' ) } + + ) } { actions } diff --git a/packages/editor/src/components/post-card-panel/style.scss b/packages/editor/src/components/post-card-panel/style.scss index 7af82a87081242..82d10eb85b6939 100644 --- a/packages/editor/src/components/post-card-panel/style.scss +++ b/packages/editor/src/components/post-card-panel/style.scss @@ -7,7 +7,13 @@ width: 100%; &.editor-post-card-panel__title { - margin: 3px 0 0 0; + margin: 0; + padding: 2px 0; + display: flex; + align-items: center; + flex-wrap: wrap; + column-gap: $grid-unit-10; + row-gap: $grid-unit-05; } } @@ -30,3 +36,15 @@ .editor-post-card-panel__icon.is-sync { fill: var(--wp-block-synced-color); } + +.editor-post-card-panel__title-badge { + background: $gray-100; + color: $gray-700; + padding: 0 $grid-unit-05; + border-radius: $radius-block-ui; + font-size: 12px; + font-weight: 400; + flex-shrink: 0; + line-height: $grid-unit-05 * 5; + display: inline-block; +} diff --git a/packages/editor/src/components/post-discussion/panel.js b/packages/editor/src/components/post-discussion/panel.js index f829ed01eebb95..718754d56857aa 100644 --- a/packages/editor/src/components/post-discussion/panel.js +++ b/packages/editor/src/components/post-discussion/panel.js @@ -6,7 +6,6 @@ import { Dropdown, Button, __experimentalVStack as VStack, - __experimentalText as Text, } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { useState, useMemo } from '@wordpress/element'; @@ -81,11 +80,19 @@ function PostDiscussionToggle( { isOpen, onClick } ) { aria-expanded={ isOpen } onClick={ onClick } > - { label } + { label } ); } +/** + * This component allows to update comment and pingback + * settings for the current post. Internally there are + * checks whether the current post has support for the + * above and if the `discussion-panel` panel is enabled. + * + * @return {JSX.Element|null} The rendered PostDiscussionPanel component. + */ export default function PostDiscussionPanel() { const { isEnabled } = useSelect( ( select ) => { const { isEditorPanelEnabled } = select( editorStore ); diff --git a/packages/editor/src/components/post-discussion/style.scss b/packages/editor/src/components/post-discussion/style.scss index b1eae851402859..b2d65c9aa7cf3f 100644 --- a/packages/editor/src/components/post-discussion/style.scss +++ b/packages/editor/src/components/post-discussion/style.scss @@ -13,9 +13,7 @@ } } .editor-post-discussion__panel-toggle { - &.components-button { - height: auto; - } + .components-text { color: inherit; } diff --git a/packages/editor/src/components/post-excerpt/panel.js b/packages/editor/src/components/post-excerpt/panel.js index 15858f7be2368c..55952278d22476 100644 --- a/packages/editor/src/components/post-excerpt/panel.js +++ b/packages/editor/src/components/post-excerpt/panel.js @@ -79,6 +79,11 @@ function ExcerptPanel() { ); } +/** + * Is rendered if the post type supports excerpts and allows editing the excerpt. + * + * @return {JSX.Element} The rendered PostExcerptPanel component. + */ export default function PostExcerptPanel() { return ( diff --git a/packages/editor/src/components/post-format/index.js b/packages/editor/src/components/post-format/index.js index 2aec930298c204..a61d230a00119d 100644 --- a/packages/editor/src/components/post-format/index.js +++ b/packages/editor/src/components/post-format/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { Button, SelectControl } from '@wordpress/components'; +import { Button, RadioControl } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { useInstanceId } from '@wordpress/compose'; import { store as coreStore } from '@wordpress/core-data'; @@ -85,16 +85,17 @@ export default function PostFormat() { return ( - onUpdatePostFormat( format ) } id={ postFormatSelectorId } options={ formats.map( ( format ) => ( { label: format.caption, value: format.id, } ) ) } + hideLabelFromVision /> { suggestion && suggestion.id !== postFormat && ( diff --git a/packages/editor/src/components/post-format/panel.js b/packages/editor/src/components/post-format/panel.js index cbd065183eefab..faaf88b785a4b2 100644 --- a/packages/editor/src/components/post-format/panel.js +++ b/packages/editor/src/components/post-format/panel.js @@ -1,20 +1,85 @@ /** * WordPress dependencies */ -import { PanelRow } from '@wordpress/components'; +import { Button, Dropdown } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; /** * Internal dependencies */ -import PostFormatForm from './'; +import { default as PostFormatForm, POST_FORMATS } from './'; import PostFormatCheck from './check'; +import PostPanelRow from '../post-panel-row'; +import { store as editorStore } from '../../store'; -export function PostFormat() { +/** + * Renders the Post Author Panel component. + * + * @return {Component} The component to be rendered. + */ +function PostFormat() { + const { postFormat } = useSelect( ( select ) => { + const { getEditedPostAttribute } = select( editorStore ); + const _postFormat = getEditedPostAttribute( 'format' ); + return { + postFormat: _postFormat ?? 'standard', + }; + }, [] ); + const activeFormat = POST_FORMATS.find( + ( format ) => format.id === postFormat + ); + + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); return ( - - - + + ( + + { activeFormat?.caption } + + ) } + renderContent={ ( { onClose } ) => ( + + + + + ) } + /> + ); } diff --git a/packages/editor/src/components/post-format/style.scss b/packages/editor/src/components/post-format/style.scss index 135ee7f3579029..ec3a8b1626b543 100644 --- a/packages/editor/src/components/post-format/style.scss +++ b/packages/editor/src/components/post-format/style.scss @@ -2,8 +2,14 @@ margin: $grid-unit-05 0 0 0; } -.editor-post-format__panel { - display: flex; - flex-direction: column; - align-items: stretch; +.editor-post-format__dialog .editor-post-format__dialog-content { + // sidebar width - popover padding - form margin + min-width: $sidebar-width - $grid-unit-20 - $grid-unit-20; + margin: $grid-unit-10; +} + +.editor-post-format__options { + .components-base-control__field > .components-v-stack { + gap: $grid-unit-15; + } } diff --git a/packages/editor/src/components/post-panel-row/style.scss b/packages/editor/src/components/post-panel-row/style.scss index 22d0cbbb644d8a..baa7d7dd98977d 100644 --- a/packages/editor/src/components/post-panel-row/style.scss +++ b/packages/editor/src/components/post-panel-row/style.scss @@ -11,6 +11,9 @@ min-height: $grid-unit-40; display: flex; align-items: center; + padding: 6px 0; // Matches button to ensure alignment + line-height: $grid-unit-05 * 5; + hyphens: auto; } .editor-post-panel__row-control { @@ -18,4 +21,16 @@ min-height: $grid-unit-40; display: flex; align-items: center; + + .components-button { + max-width: 100%; + text-align: left; + text-wrap: pretty; + height: auto; + min-height: $button-size-compact; + } + + .components-dropdown { + max-width: 100%; + } } diff --git a/packages/editor/src/components/post-pending-status/check.js b/packages/editor/src/components/post-pending-status/check.js index 347cb6d7272276..7a4ff5195041c6 100644 --- a/packages/editor/src/components/post-pending-status/check.js +++ b/packages/editor/src/components/post-pending-status/check.js @@ -8,6 +8,16 @@ import { useSelect } from '@wordpress/data'; */ import { store as editorStore } from '../../store'; +/** + * This component checks the publishing status of the current post. + * If the post is already published or the user doesn't have the + * capability to publish, it returns null. + * + * @param {Object} props Component properties. + * @param {Element} props.children Children to be rendered. + * + * @return {JSX.Element|null} The rendered child elements or null if the post is already published or the user doesn't have the capability to publish. + */ export function PostPendingStatusCheck( { children } ) { const { hasPublishAction, isPublished } = useSelect( ( select ) => { const { isCurrentPostPublished, getCurrentPost } = diff --git a/packages/editor/src/components/post-pending-status/index.js b/packages/editor/src/components/post-pending-status/index.js index 85557517da6a11..8363ebc715891b 100644 --- a/packages/editor/src/components/post-pending-status/index.js +++ b/packages/editor/src/components/post-pending-status/index.js @@ -11,6 +11,11 @@ import { useDispatch, useSelect } from '@wordpress/data'; import PostPendingStatusCheck from './check'; import { store as editorStore } from '../../store'; +/** + * A component for displaying and toggling the pending status of a post. + * + * @return {JSX.Element} The rendered component. + */ export function PostPendingStatus() { const status = useSelect( ( select ) => select( editorStore ).getEditedPostAttribute( 'status' ), diff --git a/packages/editor/src/components/post-pingbacks/index.js b/packages/editor/src/components/post-pingbacks/index.js index 832088b86364a4..2ec9a4a78c54d6 100644 --- a/packages/editor/src/components/post-pingbacks/index.js +++ b/packages/editor/src/components/post-pingbacks/index.js @@ -42,4 +42,10 @@ function PostPingbacks() { ); } +/** + * Renders a control for enabling or disabling pingbacks and trackbacks + * in a WordPress post. + * + * @module PostPingbacks + */ export default PostPingbacks; diff --git a/packages/editor/src/components/post-preview-button/index.js b/packages/editor/src/components/post-preview-button/index.js index 578cd78351d23a..110de5858af382 100644 --- a/packages/editor/src/components/post-preview-button/index.js +++ b/packages/editor/src/components/post-preview-button/index.js @@ -99,6 +99,21 @@ function writeInterstitialMessage( targetDocument ) { targetDocument.close(); } +/** + * Renders a button that opens a new window or tab for the preview, + * writes the interstitial message to this window, and then navigates + * to the actual preview link. The button is not rendered if the post + * is not viewable and disabled if the post is not saveable. + * + * @param {Object} props The component props. + * @param {string} props.className The class name for the button. + * @param {string} props.textContent The text content for the button. + * @param {boolean} props.forceIsAutosaveable Whether to force autosave. + * @param {string} props.role The role attribute for the button. + * @param {Function} props.onPreview The callback function for preview event. + * + * @return {JSX.Element|null} The rendered button component. + */ export default function PostPreviewButton( { className, textContent, diff --git a/packages/editor/src/components/post-publish-button/index.js b/packages/editor/src/components/post-publish-button/index.js index 355986fdf509dd..8797c9d1428251 100644 --- a/packages/editor/src/components/post-publish-button/index.js +++ b/packages/editor/src/components/post-publish-button/index.js @@ -11,6 +11,7 @@ import { compose } from '@wordpress/compose'; */ import PublishButtonLabel from './label'; import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; const noop = () => {}; @@ -45,14 +46,24 @@ export class PostPublishButton extends Component { createOnClick( callback ) { return ( ...args ) => { - const { hasNonPostEntityChanges, setEntitiesSavedStatesCallback } = - this.props; + const { + hasNonPostEntityChanges, + hasPostMetaChanges, + setEntitiesSavedStatesCallback, + isPublished, + } = this.props; // If a post with non-post entities is published, but the user // elects to not save changes to the non-post entities, those // entities will still be dirty when the Publish button is clicked. // We also need to check that the `setEntitiesSavedStatesCallback` // prop was passed. See https://github.com/WordPress/gutenberg/pull/37383 - if ( hasNonPostEntityChanges && setEntitiesSavedStatesCallback ) { + // + // TODO: Explore how to manage `hasPostMetaChanges` and pre-publish workflow properly. + if ( + ( hasNonPostEntityChanges || + ( hasPostMetaChanges && isPublished ) ) && + setEntitiesSavedStatesCallback + ) { // The modal for multiple entity saving will open, // hold the callback for saving/publishing the post // so that we can call it if the post entity is checked. @@ -212,7 +223,8 @@ export default compose( [ isSavingNonPostEntityChanges, getEditedPostAttribute, getPostEdits, - } = select( editorStore ); + hasPostMetaChanges, + } = unlock( select( editorStore ) ); return { isSaving: isSavingPost(), isAutoSaving: isAutosavingPost(), @@ -229,6 +241,7 @@ export default compose( [ postStatus: getEditedPostAttribute( 'status' ), postStatusHasChanged: getPostEdits()?.status, hasNonPostEntityChanges: hasNonPostEntityChanges(), + hasPostMetaChanges: hasPostMetaChanges(), isSavingNonPostEntityChanges: isSavingNonPostEntityChanges(), }; } ), diff --git a/packages/editor/src/components/post-publish-panel/index.js b/packages/editor/src/components/post-publish-panel/index.js index d9c158ab429a9f..4d59133966759a 100644 --- a/packages/editor/src/components/post-publish-panel/index.js +++ b/packages/editor/src/components/post-publish-panel/index.js @@ -96,6 +96,7 @@ export class PostPublishPanel extends Component { disabled={ isSavingNonPostEntityChanges } onClick={ onClose } variant="secondary" + size="compact" > { __( 'Cancel' ) } diff --git a/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js b/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js index adf02af3134558..ce4ac1713d6389 100644 --- a/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js +++ b/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js @@ -28,7 +28,7 @@ const TagsPanel = () => { 'Tags help users and search engines navigate your site and find your content. Add a few keywords to describe your post.' ) } - + ); }; diff --git a/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap b/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap index 470fadec4be2bd..7f31c97018d092 100644 --- a/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap +++ b/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap @@ -447,7 +447,7 @@ exports[`PostPublishPanel should render the pre-publish panel if post status is class="editor-post-publish-panel__header-cancel-button" > Cancel @@ -600,7 +600,7 @@ exports[`PostPublishPanel should render the pre-publish panel if the post is not class="editor-post-publish-panel__header-cancel-button" > Cancel @@ -797,7 +797,7 @@ exports[`PostPublishPanel should render the spinner if the post is being saved 1 class="editor-post-publish-panel__header-cancel-button" > Cancel diff --git a/packages/editor/src/components/post-schedule/index.js b/packages/editor/src/components/post-schedule/index.js index af32cc46c67ace..87a6e4649f9214 100644 --- a/packages/editor/src/components/post-schedule/index.js +++ b/packages/editor/src/components/post-schedule/index.js @@ -8,7 +8,7 @@ import { parseISO, endOfMonth, startOfMonth } from 'date-fns'; */ import { getSettings } from '@wordpress/date'; import { useDispatch, useSelect } from '@wordpress/data'; -import { __experimentalPublishDateTimePicker as PublishDateTimePicker } from '@wordpress/block-editor'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; import { useState, useMemo } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; @@ -16,6 +16,9 @@ import { store as coreStore } from '@wordpress/core-data'; * Internal dependencies */ import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +const { PrivatePublishDateTimePicker } = unlock( blockEditorPrivateApis ); /** * Renders the PostSchedule component. It allows the user to schedule a post. @@ -25,7 +28,21 @@ import { store as editorStore } from '../../store'; * * @return {Component} The component to be rendered. */ -export default function PostSchedule( { onClose } ) { +export default function PostSchedule( props ) { + return ( + + ); +} + +export function PrivatePostSchedule( { + onClose, + showPopoverHeaderActions, + isCompact, +} ) { const { postDate, postType } = useSelect( ( select ) => ( { postDate: select( editorStore ).getEditedPostAttribute( 'date' ), @@ -77,7 +94,7 @@ export default function PostSchedule( { onClose } ) { ); return ( - ); } diff --git a/packages/editor/src/components/post-schedule/style.scss b/packages/editor/src/components/post-schedule/style.scss index 0b9d64a9ce1407..02ea48016f9696 100644 --- a/packages/editor/src/components/post-schedule/style.scss +++ b/packages/editor/src/components/post-schedule/style.scss @@ -8,14 +8,3 @@ padding: $grid-unit-20; } } - -.editor-post-schedule__dialog-toggle.components-button { - overflow: hidden; - text-align: left; - white-space: unset; - height: auto; - min-height: $button-size-compact; - - // The line height + the padding should be the same as the button size. - line-height: inherit; -} diff --git a/packages/editor/src/components/post-status/index.js b/packages/editor/src/components/post-status/index.js index 642bc059a0b24e..571898779c531b 100644 --- a/packages/editor/src/components/post-status/index.js +++ b/packages/editor/src/components/post-status/index.js @@ -27,6 +27,7 @@ import { NAVIGATION_POST_TYPE, } from '../../store/constants'; import PostPanelRow from '../post-panel-row'; +import { PrivatePostSchedule } from '../post-schedule'; import { store as editorStore } from '../../store'; const labels = { @@ -174,11 +175,6 @@ export default function PostStatus() { let newPassword = password; if ( status === 'future' && new Date( date ) > new Date() ) { newDate = null; - } else if ( value === 'future' ) { - if ( ! date || new Date( date ) < new Date() ) { - newDate = new Date(); - newDate.setDate( newDate.getDate() + 7 ); - } } if ( value === 'private' && password ) { newPassword = ''; @@ -232,6 +228,16 @@ export default function PostStatus() { : status } /> + { status === 'future' && ( + + + + ) } { status !== 'private' && ( { diff --git a/packages/editor/src/components/post-title/index.native.js b/packages/editor/src/components/post-title/index.native.js index dc7534c28a79fe..751f1009ff25e1 100644 --- a/packages/editor/src/components/post-title/index.native.js +++ b/packages/editor/src/components/post-title/index.native.js @@ -18,11 +18,67 @@ import { store as blockEditorStore, RichText } from '@wordpress/block-editor'; import { store as editorStore } from '@wordpress/editor'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; +/** @typedef {import('./types').RichTextValue} RichTextValue */ + /** * Internal dependencies */ import styles from './style.scss'; +/** + * Inserts content with title + * + * This function processes the given content and title, updating the title + * and content based on certain conditions. If the content is an array of + * blocks, it will check the first block for a heading or paragraph to use + * as the title. If the content is a string, it will strip HTML and update + * the title and content accordingly. + * + * @param {string} title The post title. + * @param {Array | string} content The content to be processed. It can be an array of blocks or a string. + * @param {Function} onUpdateTitle Callback function to update the title. + * @param {Function} onUpdateContent Callback function to update the content. + * @param {RichTextValue} value The initial value object, default is an object with empty text. + */ +export function insertContentWithTitle( + title, + content, + onUpdateTitle, + onUpdateContent, + value = create( { text: '' } ) +) { + if ( ! content.length ) { + return; + } + + if ( typeof content !== 'string' ) { + const [ firstBlock ] = content; + + if ( + ! title && + ( firstBlock.name === 'core/heading' || + firstBlock.name === 'core/paragraph' ) + ) { + // Strip HTML to avoid unwanted HTML being added to the title. + // In the majority of cases it is assumed that HTML in the title + // is undesirable. + const contentNoHTML = stripHTML( firstBlock.attributes.content ); + onUpdateTitle( contentNoHTML ); + onUpdateContent( content.slice( 1 ) ); + } else { + onUpdateContent( content ); + } + } else { + // Strip HTML to avoid unwanted HTML being added to the title. + // In the majority of cases it is assumed that HTML in the title + // is undesirable. + const contentNoHTML = stripHTML( content ); + + const newValue = insert( value, create( { html: contentNoHTML } ) ); + onUpdateTitle( toHTMLString( { value: newValue } ) ); + } +} + class PostTitle extends Component { constructor( props ) { super( props ); @@ -59,45 +115,24 @@ class PostTitle extends Component { } onPaste( { value, plainText, html } ) { - const { title, onInsertBlockAfter, onUpdate } = this.props; + const { + title, + onInsertBlockAfter: onInsertBlocks, + onUpdate, + } = this.props; const content = pasteHandler( { HTML: html, plainText, } ); - if ( ! content.length ) { - return; - } - - if ( typeof content !== 'string' ) { - const [ firstBlock ] = content; - - if ( - ! title && - ( firstBlock.name === 'core/heading' || - firstBlock.name === 'core/paragraph' ) - ) { - // Strip HTML to avoid unwanted HTML being added to the title. - // In the majority of cases it is assumed that HTML in the title - // is undesirable. - const contentNoHTML = stripHTML( - firstBlock.attributes.content - ); - onUpdate( contentNoHTML ); - onInsertBlockAfter( content.slice( 1 ) ); - } else { - onInsertBlockAfter( content ); - } - } else { - // Strip HTML to avoid unwanted HTML being added to the title. - // In the majority of cases it is assumed that HTML in the title - // is undesirable. - const contentNoHTML = stripHTML( content ); - - const newValue = insert( value, create( { html: contentNoHTML } ) ); - onUpdate( toHTMLString( { value: newValue } ) ); - } + insertContentWithTitle( + title, + content, + onUpdate, + onInsertBlocks, + value + ); } setRef( richText ) { @@ -166,7 +201,7 @@ class PostTitle extends Component { { this.props.onUpdate( value ); diff --git a/packages/editor/src/components/post-transform-panel/index.js b/packages/editor/src/components/post-transform-panel/index.js index 5ed7d34909f8b7..08139c2c3c6645 100644 --- a/packages/editor/src/components/post-transform-panel/index.js +++ b/packages/editor/src/components/post-transform-panel/index.js @@ -3,7 +3,7 @@ */ import { useSelect, useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; -import { PanelBody, PanelRow } from '@wordpress/components'; +import { PanelBody } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useAsyncList } from '@wordpress/compose'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; @@ -62,17 +62,9 @@ function PostTransform() { return ( - - - { __( - 'Choose a predefined pattern to switch up the look of your template.' // TODO - make this dynamic? - ) } - - - - { decodedSlug } + /{ decodedSlug } ); } diff --git a/packages/editor/src/components/post-url/style.scss b/packages/editor/src/components/post-url/style.scss index c622cfce33f90e..a711402f1a82eb 100644 --- a/packages/editor/src/components/post-url/style.scss +++ b/packages/editor/src/components/post-url/style.scss @@ -2,15 +2,6 @@ width: 100%; } -.components-button.editor-post-url__panel-toggle { - display: block; - max-width: 100%; - overflow: hidden; - text-align: left; - text-overflow: ellipsis; - white-space: nowrap; -} - .editor-post-url__panel-dialog .editor-post-url { // sidebar width - popover padding - form margin min-width: $sidebar-width - $grid-unit-20 - $grid-unit-20; diff --git a/packages/editor/src/components/posts-per-page/index.js b/packages/editor/src/components/posts-per-page/index.js index 625aeba7bd824f..d62d250d9f3d3d 100644 --- a/packages/editor/src/components/posts-per-page/index.js +++ b/packages/editor/src/components/posts-per-page/index.js @@ -82,7 +82,7 @@ export default function PostsPerPage() { + * { children } + * + * + * @return {Object} The rendered ExperimentalEditorProvider component. + */ export const ExperimentalEditorProvider = withRegistryProvider( ( { post, @@ -293,6 +325,36 @@ export const ExperimentalEditorProvider = withRegistryProvider( } ); +/** + * This component establishes a new post editing context, and serves as the entry point for a new post editor (or post with template editor). + * + * It supports a large number of post types, including post, page, templates, + * custom post types, patterns, template parts. + * + * All modification and changes are performed to the `@wordpress/core-data` store. + * + * @param {Object} props The component props. + * @param {Object} [props.post] The post object to edit. This is required. + * @param {Object} [props.__unstableTemplate] The template object wrapper the edited post. + * This is optional and can only be used when the post type supports templates (like posts and pages). + * @param {Object} [props.settings] The settings object to use for the editor. + * This is optional and can be used to override the default settings. + * @param {Element} [props.children] Children elements for which the BlockEditorProvider context should apply. + * This is optional. + * + * @example + * ```jsx + * + * { children } + * + * ``` + * + * @return {JSX.Element} The rendered EditorProvider component. + */ export function EditorProvider( props ) { return ( ( { @@ -200,6 +204,12 @@ class NativeEditorProvider extends Component { this.onHardwareBackPress ); + this.subscriptionOnContentUpdate = subscribeToContentUpdate( + ( data ) => { + this.onContentUpdate( data ); + } + ); + // Request current block impressions from native app. requestBlockTypeImpressions( ( storedImpressions ) => { const impressions = { ...NEW_BLOCK_TYPES, ...storedImpressions }; @@ -263,6 +273,10 @@ class NativeEditorProvider extends Component { if ( this.hardwareBackPressListener ) { this.hardwareBackPressListener.remove(); } + + if ( this.subscriptionOnContentUpdate ) { + this.subscriptionOnContentUpdate.remove(); + } } getThemeColors( { rawStyles, rawFeatures } ) { @@ -303,6 +317,21 @@ class NativeEditorProvider extends Component { return false; } + onContentUpdate( { content: rawContent } ) { + const { + editTitle, + onClearPostTitleSelection, + onInsertBlockAfter: onInsertBlocks, + title, + } = this.props; + const content = pasteHandler( { + plainText: rawContent, + } ); + + insertContentWithTitle( title, content, editTitle, onInsertBlocks ); + onClearPostTitleSelection(); + } + serializeToNativeAction() { const title = this.props.title; let html; @@ -423,11 +452,13 @@ const ComposedNativeProvider = compose( [ resetEditorBlocks, updateEditorSettings, switchEditorMode, + togglePostTitleSelection, } = dispatch( editorStore ); const { clearSelectedBlock, updateSettings, insertBlock, + insertBlocks, replaceBlock, } = dispatch( blockEditorStore ); const { addEntities, receiveEntityRecords } = dispatch( coreStore ); @@ -439,6 +470,7 @@ const ComposedNativeProvider = compose( [ updateEditorSettings, addEntities, insertBlock, + insertBlocks, createSuccessNotice, createErrorNotice, clearSelectedBlock, @@ -454,6 +486,12 @@ const ComposedNativeProvider = compose( [ switchMode( mode ) { switchEditorMode( mode ); }, + onInsertBlockAfter( blocks ) { + insertBlocks( blocks, undefined, undefined, false ); + }, + onClearPostTitleSelection() { + togglePostTitleSelection( false ); + }, replaceBlock, }; } ), diff --git a/packages/editor/src/components/save-publish-panels/index.js b/packages/editor/src/components/save-publish-panels/index.js index 812b1dcc1df41f..3ae871c354bb62 100644 --- a/packages/editor/src/components/save-publish-panels/index.js +++ b/packages/editor/src/components/save-publish-panels/index.js @@ -14,6 +14,7 @@ import PostPublishPanel from '../post-publish-panel'; import PluginPrePublishPanel from '../plugin-pre-publish-panel'; import PluginPostPublishPanel from '../plugin-post-publish-panel'; import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; const { Fill, Slot } = createSlotFill( 'ActionsPanel' ); @@ -27,12 +28,19 @@ export default function SavePublishPanels( { } ) { const { closePublishSidebar, togglePublishSidebar } = useDispatch( editorStore ); - const { publishSidebarOpened, hasNonPostEntityChanges } = useSelect( + const { + publishSidebarOpened, + hasNonPostEntityChanges, + hasPostMetaChanges, + } = useSelect( ( select ) => ( { publishSidebarOpened: select( editorStore ).isPublishSidebarOpened(), hasNonPostEntityChanges: select( editorStore ).hasNonPostEntityChanges(), + hasPostMetaChanges: unlock( + select( editorStore ) + ).hasPostMetaChanges(), } ), [] ); @@ -54,7 +62,7 @@ export default function SavePublishPanels( { PostPublishExtension={ PluginPostPublishPanel.Slot } /> ); - } else if ( hasNonPostEntityChanges ) { + } else if ( hasNonPostEntityChanges || hasPostMetaChanges ) { unmountableContent = ( - { extraPanels } diff --git a/packages/editor/src/components/sidebar/post-summary.js b/packages/editor/src/components/sidebar/post-summary.js index ab2f714027e63f..c3239f36cab778 100644 --- a/packages/editor/src/components/sidebar/post-summary.js +++ b/packages/editor/src/components/sidebar/post-summary.js @@ -12,6 +12,7 @@ import PostActions from '../post-actions'; import PostAuthorPanel from '../post-author/panel'; import PostCardPanel from '../post-card-panel'; import PostContentInformation from '../post-content-information'; +import PageAttributesPanel from '../page-attributes/panel'; import PostDiscussionPanel from '../post-discussion/panel'; import { PrivatePostExcerptPanel as PostExcerptPanel } from '../post-excerpt/panel'; import PostFeaturedImagePanel from '../post-featured-image/panel'; @@ -71,17 +72,18 @@ export default function PostSummary( { onActionPerformed } ) { - + + + - { fills } diff --git a/packages/editor/src/components/text-editor/index.js b/packages/editor/src/components/text-editor/index.js index 5bfb53c574aa30..b65a958249b6f8 100644 --- a/packages/editor/src/components/text-editor/index.js +++ b/packages/editor/src/components/text-editor/index.js @@ -5,6 +5,7 @@ import { Button } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useEffect, useRef } from '@wordpress/element'; /** * Internal dependencies @@ -13,7 +14,7 @@ import { store as editorStore } from '../../store'; import PostTextEditor from '../post-text-editor'; import PostTitleRaw from '../post-title/post-title-raw'; -export default function TextEditor() { +export default function TextEditor( { autoFocus = false } ) { const { switchEditorMode } = useDispatch( editorStore ); const { shortcut, isRichEditingEnabled } = useSelect( ( select ) => { const { getEditorSettings } = select( editorStore ); @@ -25,6 +26,14 @@ export default function TextEditor() { }; }, [] ); + const titleRef = useRef(); + useEffect( () => { + if ( autoFocus ) { + return; + } + titleRef?.current?.focus(); + }, [ autoFocus ] ); + return ( { isRichEditingEnabled && ( @@ -40,7 +49,7 @@ export default function TextEditor() { ) } - + diff --git a/packages/editor/src/components/visual-editor/index.js b/packages/editor/src/components/visual-editor/index.js index 52bc953eff3290..7d8f38ae7a0eb5 100644 --- a/packages/editor/src/components/visual-editor/index.js +++ b/packages/editor/src/components/visual-editor/index.js @@ -363,7 +363,7 @@ function VisualEditor( { const iframeStyles = useMemo( () => { return [ - ...styles, + ...( styles ?? [] ), { css: `.is-root-container{display:flow-root;${ // Some themes will have `min-height: 100vh` for the root container, @@ -376,11 +376,17 @@ function VisualEditor( { return ( ( state, postType, postId ) => { + const { type: currentPostType, id: currentPostId } = + getCurrentPost( state ); + // If no postType or postId is passed, use the current post. + const edits = select( coreStore ).getEntityRecordNonTransientEdits( + 'postType', + postType || currentPostType, + postId || currentPostId + ); + + if ( ! edits?.meta ) { + return false; + } + + // Compare if anything apart from `footnotes` has changed. + const originalPostMeta = select( coreStore ).getEntityRecord( + 'postType', + postType || currentPostType, + postId || currentPostId + )?.meta; + + return ! fastDeepEqual( + { ...originalPostMeta, footnotes: undefined }, + { ...edits.meta, footnotes: undefined } + ); + } +); diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index 21e1d20997fcce..bd8d03568e9d47 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -3,6 +3,7 @@ @import "./components/autocompleters/style.scss"; @import "./components/block-manager/style.scss"; @import "./components/collapsible-block-toolbar/style.scss"; +@import "./components/create-template-part-modal/style.scss"; @import "./components/block-settings-menu/style.scss"; @import "./components/blog-title/style.scss"; @import "./components/document-bar/style.scss"; @@ -15,6 +16,7 @@ @import "./components/inserter-sidebar/style.scss"; @import "./components/keyboard-shortcut-help-modal/style.scss"; @import "./components/list-view-sidebar/style.scss"; +@import "./components/page-attributes/style.scss"; @import "./components/post-author/style.scss"; @import "./components/post-actions/style.scss"; @import "./components/post-card-panel/style.scss"; diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md index 9be466cfa8a13b..2d0c67c22c2847 100644 --- a/packages/element/CHANGELOG.md +++ b/packages/element/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/element/package.json b/packages/element/package.json index 2a66b1201f5d2f..fe9a68695b159c 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/element", - "version": "5.35.0", + "version": "6.0.0", "description": "Element React module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/element/src/test/create-interpolate-element.js b/packages/element/src/test/create-interpolate-element.js index e78d3b1c558441..17b1cc9b3ab2c9 100644 --- a/packages/element/src/test/create-interpolate-element.js +++ b/packages/element/src/test/create-interpolate-element.js @@ -80,7 +80,7 @@ describe( 'createInterpolateElement', () => { ); const component = createInterpolateElement( testString, { // eslint-disable-next-line jsx-a11y/anchor-has-content - a: , + a: , } ); expect( JSON.stringify( component ) ).toEqual( JSON.stringify( expectedElement ) diff --git a/packages/element/src/test/serialize.js b/packages/element/src/test/serialize.js index 7e38b80101bcdc..e2337f8556119e 100644 --- a/packages/element/src/test/serialize.js +++ b/packages/element/src/test/serialize.js @@ -322,12 +322,11 @@ describe( 'renderElement()', () => { { ( context ) => context.value } - { '|' } + | { ( context ) => context.value } - { '|' } - { ( context ) => context.value } + |{ ( context ) => context.value } ); @@ -344,8 +343,7 @@ describe( 'renderElement()', () => { { ( context ) => context.value } - { '|' } - { ( context ) => context.value } + |{ ( context ) => context.value } ); diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 4da081b51eb713..7669cf755943ea 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 10.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/env/package.json b/packages/env/package.json index 9604ededadca45..165ff57790fba2 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "9.10.0", + "version": "10.0.0", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/escape-html/CHANGELOG.md b/packages/escape-html/CHANGELOG.md index 9917b2eeadff4b..2562662aa2ce91 100644 --- a/packages/escape-html/CHANGELOG.md +++ b/packages/escape-html/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index 318bf847b8d292..35cf1e8c286957 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/escape-html", - "version": "2.58.0", + "version": "3.0.0", "description": "Escape HTML utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 6d3791ba7c7a6a..816140ed76900b 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 19.0.0 (2024-05-31) + ### Breaking Changes - `@wordpress/is-gutenberg-plugin` rule has been replaced by `@wordpress/wp-global-usage` ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 0709fdee582c99..8cf48275fbe884 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/eslint-plugin", - "version": "18.1.0", + "version": "19.0.0", "description": "ESLint plugin for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/CHANGELOG.md b/packages/format-library/CHANGELOG.md index 4156282600a4b4..8f73bad3fcad36 100644 --- a/packages/format-library/CHANGELOG.md +++ b/packages/format-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 46d38ee6441128..5582be09668464 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "4.35.0", + "version": "5.0.0", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/src/image/index.js b/packages/format-library/src/image/index.js index 7bc7c954eea183..627e1e966b655c 100644 --- a/packages/format-library/src/image/index.js +++ b/packages/format-library/src/image/index.js @@ -104,56 +104,43 @@ function Edit( { activeObjectAttributes, contentRef, } ) { - const [ isModalOpen, setIsModalOpen ] = useState( false ); - - function openModal() { - setIsModalOpen( true ); - } - - function closeModal() { - setIsModalOpen( false ); - } - return ( - - - - } - title={ title } - onClick={ openModal } - isActive={ isObjectActive } + { + onChange( + insertObject( value, { + type: name, + attributes: { + className: `wp-image-${ id }`, + style: `width: ${ Math.min( + imgWidth, + 150 + ) }px;`, + url, + alt, + }, + } ) + ); + onFocus(); + } } + render={ ( { open } ) => ( + + + + } + title={ title } + onClick={ open } + isActive={ isObjectActive } + /> + ) } /> - { isModalOpen && ( - { - closeModal(); - onChange( - insertObject( value, { - type: name, - attributes: { - className: `wp-image-${ id }`, - style: `width: ${ Math.min( - imgWidth, - 150 - ) }px;`, - url, - alt, - }, - } ) - ); - onFocus(); - } } - onClose={ closeModal } - render={ ( { open } ) => { - open(); - return null; - } } - /> - ) } { isObjectActive && ( > diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md index 2d918d78d571c4..c92c344893f368 100644 --- a/packages/hooks/CHANGELOG.md +++ b/packages/hooks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 7c654b1356a0de..06779db3d042de 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/hooks", - "version": "3.58.0", + "version": "4.0.0", "description": "WordPress hooks library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/html-entities/CHANGELOG.md b/packages/html-entities/CHANGELOG.md index 205d3f03a8ca7c..b970e05f95faa7 100644 --- a/packages/html-entities/CHANGELOG.md +++ b/packages/html-entities/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index 5ef035763394ce..2cb96e17a6270c 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/html-entities", - "version": "3.58.0", + "version": "4.0.0", "description": "HTML entity utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index 993d8758c8d27d..55a6a848950dfc 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/i18n/package.json b/packages/i18n/package.json index da0c793d4f6092..b6ec0f47c1f9b0 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/i18n", - "version": "4.58.0", + "version": "5.0.0", "description": "WordPress internationalization (i18n) library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/icons/CHANGELOG.md b/packages/icons/CHANGELOG.md index ff3eece680666b..485fdaed579965 100644 --- a/packages/icons/CHANGELOG.md +++ b/packages/icons/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 10.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/icons/package.json b/packages/icons/package.json index 0d3024b83557a7..f3912559bb7101 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/icons", - "version": "9.49.0", + "version": "10.0.0", "description": "WordPress Icons package, based on dashicon.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interactivity-router/CHANGELOG.md b/packages/interactivity-router/CHANGELOG.md index d17d6f834b6460..a7e6f5060b6023 100644 --- a/packages/interactivity-router/CHANGELOG.md +++ b/packages/interactivity-router/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/interactivity-router/package.json b/packages/interactivity-router/package.json index a109c3031d2446..a2ed315c7d1856 100644 --- a/packages/interactivity-router/package.json +++ b/packages/interactivity-router/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interactivity-router", - "version": "1.8.0", + "version": "2.0.0", "description": "Package that exposes state and actions from the `core/router` store, part of the Interactivity API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index cf80324d09d35e..6154841c1db2c4 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### New Features - Introduce `wp-on-async` directive as performant alternative over synchronous `wp-on` directive. ([#61885](https://github.com/WordPress/gutenberg/pull/61885)) diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json index 6a5413108768e3..0b18d2ced29197 100644 --- a/packages/interactivity/package.json +++ b/packages/interactivity/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interactivity", - "version": "5.7.0", + "version": "6.0.0", "description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interactivity/src/store.ts b/packages/interactivity/src/store.ts index 9b35192fe8b132..f6366283d2d6a9 100644 --- a/packages/interactivity/src/store.ts +++ b/packages/interactivity/src/store.ts @@ -319,15 +319,15 @@ export function store( } export const parseInitialData = ( dom = document ) => { - const storeTag = dom.querySelector( - `script[type="application/json"]#wp-interactivity-data` - ); - if ( storeTag?.textContent ) { + const jsonDataScriptTag = + // Preferred Script Module data passing form + dom.getElementById( 'wp-scriptmodule-data_@wordpress/interactivity' ) ?? + // Legacy form + dom.getElementById( 'wp-interactivity-data' ); + if ( jsonDataScriptTag?.textContent ) { try { - return JSON.parse( storeTag.textContent ); - } catch ( e ) { - // Do nothing. - } + return JSON.parse( jsonDataScriptTag.textContent ); + } catch {} } return {}; }; diff --git a/packages/interface/CHANGELOG.md b/packages/interface/CHANGELOG.md index c423c206747b83..108bac3e369670 100644 --- a/packages/interface/CHANGELOG.md +++ b/packages/interface/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/interface/package.json b/packages/interface/package.json index 7fe5f3b2686493..a8ad016a5dc21f 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interface", - "version": "5.35.0", + "version": "6.0.0", "description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interface/src/components/interface-skeleton/index.js b/packages/interface/src/components/interface-skeleton/index.js index dc04ccec6fe5e5..ed4d98cdf7637f 100644 --- a/packages/interface/src/components/interface-skeleton/index.js +++ b/packages/interface/src/components/interface-skeleton/index.js @@ -26,6 +26,11 @@ import { import NavigableRegion from '../navigable-region'; const ANIMATION_DURATION = 0.25; +const commonTransition = { + type: 'tween', + duration: ANIMATION_DURATION, + ease: [ 0.6, 0, 0.4, 1 ], +}; function useHTMLClass( className ) { useEffect( () => { @@ -42,12 +47,30 @@ function useHTMLClass( className ) { } const headerVariants = { - hidden: { opacity: 0 }, - hover: { + hidden: { opacity: 1, marginTop: -60 }, + visible: { opacity: 1, marginTop: 0 }, + distractionFreeHover: { opacity: 1, - transition: { type: 'tween', delay: 0.2, delayChildren: 0.2 }, + marginTop: 0, + transition: { + ...commonTransition, + delay: 0.2, + delayChildren: 0.2, + }, + }, + distractionFreeHidden: { + opacity: 0, + marginTop: -60, + }, + distractionFreeDisabled: { + opacity: 0, + marginTop: 0, + transition: { + ...commonTransition, + delay: 0.8, + delayChildren: 0.8, + }, }, - distractionFreeInactive: { opacity: 1, transition: { delay: 0 } }, }; function InterfaceSkeleton( @@ -58,7 +81,6 @@ function InterfaceSkeleton( editorNotices, sidebar, secondarySidebar, - notices, content, actions, labels, @@ -114,36 +136,39 @@ function InterfaceSkeleton( ) } > - { !! header && ( - - { header } - - ) } + + { !! header && ( + + { header } + + ) } + { isDistractionFree && ( { editorNotices } @@ -184,11 +209,6 @@ function InterfaceSkeleton( ) } - { !! notices && ( - - { notices } - - ) } { + if ( isOverridableBlock( block ) ) { + return true; + } + return hasOverridableBlocks( block.innerBlocks ); + } ); +} diff --git a/packages/patterns/src/components/pattern-overrides-controls.js b/packages/patterns/src/components/pattern-overrides-controls.js index 3ece910a0df47c..9869c5b072c856 100644 --- a/packages/patterns/src/components/pattern-overrides-controls.js +++ b/packages/patterns/src/components/pattern-overrides-controls.js @@ -9,66 +9,48 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { - PARTIAL_SYNCING_SUPPORTED_BLOCKS, - PATTERN_OVERRIDES_BINDING_SOURCE, -} from '../constants'; +import { PATTERN_OVERRIDES_BINDING_SOURCE } from '../constants'; import { AllowOverridesModal, DisallowOverridesModal, } from './allow-overrides-modal'; -function removeBindings( bindings, syncedAttributes ) { - let updatedBindings = {}; - for ( const attributeName of syncedAttributes ) { - // Omit any bindings that's not the same source from the `updatedBindings` object. - if ( - bindings?.[ attributeName ]?.source !== - PATTERN_OVERRIDES_BINDING_SOURCE && - bindings?.[ attributeName ]?.source !== undefined - ) { - updatedBindings[ attributeName ] = bindings[ attributeName ]; - } - } +function removeBindings( bindings ) { + let updatedBindings = { ...bindings }; + delete updatedBindings.__default; if ( ! Object.keys( updatedBindings ).length ) { updatedBindings = undefined; } return updatedBindings; } -function addBindings( bindings, syncedAttributes ) { - const updatedBindings = { ...bindings }; - for ( const attributeName of syncedAttributes ) { - if ( ! bindings?.[ attributeName ] ) { - updatedBindings[ attributeName ] = { - source: PATTERN_OVERRIDES_BINDING_SOURCE, - }; - } - } - return updatedBindings; +function addBindings( bindings ) { + return { + ...bindings, + __default: { source: PATTERN_OVERRIDES_BINDING_SOURCE }, + }; } -function PatternOverridesControls( { attributes, name, setAttributes } ) { +function PatternOverridesControls( { attributes, setAttributes } ) { const controlId = useId(); const [ showAllowOverridesModal, setShowAllowOverridesModal ] = useState( false ); const [ showDisallowOverridesModal, setShowDisallowOverridesModal ] = useState( false ); - const syncedAttributes = PARTIAL_SYNCING_SUPPORTED_BLOCKS[ name ]; - const attributeSources = syncedAttributes.map( - ( attributeName ) => - attributes.metadata?.bindings?.[ attributeName ]?.source - ); - const isConnectedToOtherSources = attributeSources.every( - ( source ) => source && source !== 'core/pattern-overrides' - ); + const hasName = !! attributes.metadata?.name; + const defaultBindings = attributes.metadata?.bindings?.__default; + const allowOverrides = + hasName && defaultBindings?.source === PATTERN_OVERRIDES_BINDING_SOURCE; + const isConnectedToOtherSources = + defaultBindings?.source && + defaultBindings.source !== PATTERN_OVERRIDES_BINDING_SOURCE; function updateBindings( isChecked, customName ) { const prevBindings = attributes?.metadata?.bindings; const updatedBindings = isChecked - ? addBindings( prevBindings, syncedAttributes ) - : removeBindings( prevBindings, syncedAttributes ); + ? addBindings( prevBindings ) + : removeBindings( prevBindings ); const updatedMetadata = { ...attributes.metadata, @@ -89,13 +71,6 @@ function PatternOverridesControls( { attributes, name, setAttributes } ) { return null; } - const hasName = !! attributes.metadata?.name; - const allowOverrides = - hasName && - attributeSources.some( - ( source ) => source === PATTERN_OVERRIDES_BINDING_SOURCE - ); - return ( <> diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js index 05417de2b2c669..0553378cb56043 100644 --- a/packages/patterns/src/private-apis.js +++ b/packages/patterns/src/private-apis.js @@ -11,7 +11,7 @@ import { default as DuplicatePatternModal, useDuplicatePatternProps, } from './components/duplicate-pattern-modal'; -import { isOverridableBlock } from './api'; +import { isOverridableBlock, hasOverridableBlocks } from './api'; import RenamePatternModal from './components/rename-pattern-modal'; import PatternsMenuItems from './components'; import RenamePatternCategoryModal from './components/rename-pattern-category-modal'; @@ -34,6 +34,7 @@ lock( privateApis, { CreatePatternModalContents, DuplicatePatternModal, isOverridableBlock, + hasOverridableBlocks, useDuplicatePatternProps, RenamePatternModal, PatternsMenuItems, diff --git a/packages/plugins/CHANGELOG.md b/packages/plugins/CHANGELOG.md index ae572061e64bcc..5ba4ad33c6a745 100644 --- a/packages/plugins/CHANGELOG.md +++ b/packages/plugins/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 43b5877c4448e3..5747c0d6d889cf 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/plugins", - "version": "6.26.0", + "version": "7.0.0", "description": "Plugins module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-plugins-preset/CHANGELOG.md b/packages/postcss-plugins-preset/CHANGELOG.md index 49fc5ca3da5322..35fa13f05ab1df 100644 --- a/packages/postcss-plugins-preset/CHANGELOG.md +++ b/packages/postcss-plugins-preset/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json index d3891d679e04a1..a8954e0ec9cbc1 100644 --- a/packages/postcss-plugins-preset/package.json +++ b/packages/postcss-plugins-preset/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-plugins-preset", - "version": "4.42.0", + "version": "5.0.0", "description": "PostCSS sharable plugins preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-themes/CHANGELOG.md b/packages/postcss-themes/CHANGELOG.md index d6b1cfadd0ca28..cf32c7f744931c 100644 --- a/packages/postcss-themes/CHANGELOG.md +++ b/packages/postcss-themes/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json index 028a1967bbcfb1..eddfdecb3d0522 100644 --- a/packages/postcss-themes/package.json +++ b/packages/postcss-themes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-themes", - "version": "5.41.0", + "version": "6.0.0", "description": "PostCSS plugin to generate theme colors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences-persistence/CHANGELOG.md b/packages/preferences-persistence/CHANGELOG.md index 78035788442653..6c7af3085d8378 100644 --- a/packages/preferences-persistence/CHANGELOG.md +++ b/packages/preferences-persistence/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index a7d73fbc0df100..a250930699aec4 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences-persistence", - "version": "1.50.0", + "version": "2.0.0", "description": "Persistence utilities for `wordpress/preferences`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences/CHANGELOG.md b/packages/preferences/CHANGELOG.md index 70095e3a329f5c..54778a68a2ce1b 100644 --- a/packages/preferences/CHANGELOG.md +++ b/packages/preferences/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/preferences/package.json b/packages/preferences/package.json index c6d2347ba9723f..5b3e520ea96e8c 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences", - "version": "3.35.0", + "version": "4.0.0", "description": "Utilities for managing WordPress preferences.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/prettier-config/CHANGELOG.md b/packages/prettier-config/CHANGELOG.md index 86e0a52d5e518f..a3e735502062d2 100644 --- a/packages/prettier-config/CHANGELOG.md +++ b/packages/prettier-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index dd7604f0c3d61a..56e4a530ec2767 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/prettier-config", - "version": "3.15.0", + "version": "4.0.0", "description": "WordPress Prettier shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/primitives/CHANGELOG.md b/packages/primitives/CHANGELOG.md index 8f5c66d8e810c3..95093af2921921 100644 --- a/packages/primitives/CHANGELOG.md +++ b/packages/primitives/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 96a450e99ecc00..f47b368097470d 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/primitives", - "version": "3.56.0", + "version": "4.0.0", "description": "WordPress cross-platform primitives.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/priority-queue/CHANGELOG.md b/packages/priority-queue/CHANGELOG.md index 3a4df3e85ee90a..ebc0c6501bcdb8 100644 --- a/packages/priority-queue/CHANGELOG.md +++ b/packages/priority-queue/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index 236a48c796dc37..a544c61f217d6b 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/priority-queue", - "version": "2.58.0", + "version": "3.0.0", "description": "Generic browser priority queue.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/private-apis/CHANGELOG.md b/packages/private-apis/CHANGELOG.md index c70924a8a33be5..d10d7d73cd78f9 100644 --- a/packages/private-apis/CHANGELOG.md +++ b/packages/private-apis/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index ea2e33468cd35a..307c4f021e7612 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/private-apis", - "version": "0.40.0", + "version": "1.0.0", "description": "Internal experimental APIs for WordPress core.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/project-management-automation/CHANGELOG.md b/packages/project-management-automation/CHANGELOG.md index b1d801f3f93ae3..6794a20720a5cb 100644 --- a/packages/project-management-automation/CHANGELOG.md +++ b/packages/project-management-automation/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json index 118acb3faadc49..d0741b4c4e094d 100644 --- a/packages/project-management-automation/package.json +++ b/packages/project-management-automation/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/project-management-automation", - "version": "1.57.0", + "version": "2.0.0", "description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/react-i18n/CHANGELOG.md b/packages/react-i18n/CHANGELOG.md index 7b62c13efebfda..2d547d0335f644 100644 --- a/packages/react-i18n/CHANGELOG.md +++ b/packages/react-i18n/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json index 51346a5af7c9f5..89f7a763b4b8df 100644 --- a/packages/react-i18n/package.json +++ b/packages/react-i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-i18n", - "version": "3.56.0", + "version": "4.0.0", "description": "React bindings for @wordpress/i18n.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java index 4f7066a5bd47d0..315765edddf108 100644 --- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java +++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java @@ -66,6 +66,7 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu private static final String EVENT_NAME_ON_UNDO_PRESSED = "onUndoPressed"; private static final String EVENT_NAME_ON_REDO_PRESSED = "onRedoPressed"; + private static final String EVENT_NAME_ON_CONTENT_UPDATE = "onContentUpdate"; private static final String MAP_KEY_UPDATE_HTML = "html"; private static final String MAP_KEY_UPDATE_TITLE = "title"; @@ -91,6 +92,7 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu private static final String MAP_KEY_REPLACE_BLOCK_HTML = "html"; private static final String MAP_KEY_REPLACE_BLOCK_BLOCK_ID = "clientId"; + private static final String MAP_KEY_UPDATE_CONTENT = "content"; public static final String MAP_KEY_FEATURED_IMAGE_ID = "featuredImageId"; public static final String MAP_KEY_IS_CONNECTED = "isConnected"; @@ -214,6 +216,13 @@ public void onRedoPressed() { emitToJS(EVENT_NAME_ON_REDO_PRESSED, null); } + public void onContentUpdate(String content) { + WritableMap writableMap = new WritableNativeMap(); + + writableMap.putString(MAP_KEY_UPDATE_CONTENT, content); + emitToJS(EVENT_NAME_ON_CONTENT_UPDATE, writableMap); + } + @ReactMethod public void addListener(String eventName) { // Keep: Required for RN built in Event Emitter Calls. diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java index 8677c1737c52fb..4477dfc115b7c4 100644 --- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java +++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java @@ -846,6 +846,10 @@ public void onRedoPressed() { mRnReactNativeGutenbergBridgePackage.getRNReactNativeGutenbergBridgeModule().onRedoPressed(); } + public void onContentUpdate(String content) { + mRnReactNativeGutenbergBridgePackage.getRNReactNativeGutenbergBridgeModule().onContentUpdate(content); + } + public void setTitle(String title) { mTitleInitialized = true; mTitle = title; diff --git a/packages/react-native-bridge/index.js b/packages/react-native-bridge/index.js index 50e21fe86a6833..da16f75e161dac 100644 --- a/packages/react-native-bridge/index.js +++ b/packages/react-native-bridge/index.js @@ -202,6 +202,19 @@ export function subscribeConnectionStatus( callback ) { ); } +/** + * Subscribes a callback function to the 'onContentUpdate' event. + * This event is triggered with content that will be passed to the block editor + * to be converted into blocks. + * + * @param {Function} callback The function to be called when the 'onContentUpdate' event is triggered. + * This function receives content plain text/markdown as an argument. + * @return {Object} The listener object that was added to the event. + */ +export function subscribeToContentUpdate( callback ) { + return gutenbergBridgeEvents.addListener( 'onContentUpdate', callback ); +} + export function requestConnectionStatus( callback ) { return RNReactNativeGutenbergBridge.requestConnectionStatus( callback ); } diff --git a/packages/react-native-bridge/ios/Gutenberg.swift b/packages/react-native-bridge/ios/Gutenberg.swift index 2b293e21919795..2273801f1eeb9c 100644 --- a/packages/react-native-bridge/ios/Gutenberg.swift +++ b/packages/react-native-bridge/ios/Gutenberg.swift @@ -221,6 +221,11 @@ public class Gutenberg: UIResponder { var data: [String: Any] = ["isConnected": isConnected] bridgeModule.sendEventIfNeeded(.connectionStatusChange, body: data) } + + public func onContentUpdate(content: String) { + var payload: [String: Any] = ["content": content] + bridgeModule.sendEventIfNeeded(.onContentUpdate, body: payload) + } private func properties(from editorSettings: GutenbergEditorSettings?) -> [String : Any] { var settingsUpdates = [String : Any]() diff --git a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift index aa115331ec2d87..96c3a8f25e0cb8 100644 --- a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift +++ b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift @@ -422,6 +422,7 @@ extension RNReactNativeGutenbergBridge { case onUndoPressed case onRedoPressed case connectionStatusChange + case onContentUpdate } public override func supportedEvents() -> [String]! { diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 1775a02cf5600e..beb32d70e60725 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -10,6 +10,8 @@ For each user feature we should also add a importance categorization label to i --> ## Unreleased +- [*] Prevent deleting content when backspacing in the first Paragraph block [#62069] +- [internal] Adds new bridge functionality for updating content [#61796] ## 1.119.0 - [internal] Remove circular dependencies within the components package [#61102] diff --git a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md index e9be2ae8ae3d4a..121831aa851bf6 100644 --- a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md +++ b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json index 05d962b3708767..29e3ce5562847d 100644 --- a/packages/readable-js-assets-webpack-plugin/package.json +++ b/packages/readable-js-assets-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/readable-js-assets-webpack-plugin", - "version": "2.41.0", + "version": "3.0.0", "description": "Generate a readable JS file for each JS asset.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/redux-routine/CHANGELOG.md b/packages/redux-routine/CHANGELOG.md index af1d1e080c214d..2fc48dbef19025 100644 --- a/packages/redux-routine/CHANGELOG.md +++ b/packages/redux-routine/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 8d13c9dfc18b44..b423accef8f0a8 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/redux-routine", - "version": "4.58.0", + "version": "5.0.0", "description": "Redux middleware for generator coroutines.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/reusable-blocks/CHANGELOG.md b/packages/reusable-blocks/CHANGELOG.md index 6297ea5d7a1b15..5a89e64ba8c46e 100644 --- a/packages/reusable-blocks/CHANGELOG.md +++ b/packages/reusable-blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index ae584e75ed184a..d496eded1a7537 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/reusable-blocks", - "version": "4.35.0", + "version": "5.0.0", "description": "Reusable blocks utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md index 81d23a43f25a36..1fae202db5fc0d 100644 --- a/packages/rich-text/CHANGELOG.md +++ b/packages/rich-text/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 546b5926c65f85..c823d1018009fd 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "6.35.0", + "version": "7.0.0", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index 528201ee2c5664..b05dda0cca2b23 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/router/package.json b/packages/router/package.json index bcdb1fb3d79043..2b0f84aac06cc1 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/router", - "version": "0.27.0", + "version": "1.0.0", "description": "Router API for WordPress pages.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 3e1bfb09f679c9..453760a6ba94bb 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 28.0.0 (2024-05-31) + ### Breaking Changes - Use React's automatic runtime to transform JSX ([#61692](https://github.com/WordPress/gutenberg/pull/61692)). diff --git a/packages/scripts/package.json b/packages/scripts/package.json index e8b03804e8662d..6fe707ec9571c9 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/scripts", - "version": "27.9.0", + "version": "28.0.0", "description": "Collection of reusable scripts for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/server-side-render/CHANGELOG.md b/packages/server-side-render/CHANGELOG.md index 8a2867beb33071..23d8b8aa75353b 100644 --- a/packages/server-side-render/CHANGELOG.md +++ b/packages/server-side-render/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index d042cb4329efa3..e1d3d95e39a23a 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/server-side-render", - "version": "4.35.0", + "version": "5.0.0", "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/server-side-render/src/server-side-render.js b/packages/server-side-render/src/server-side-render.js index 69a2183036dd14..45a651e8cced6c 100644 --- a/packages/server-side-render/src/server-side-render.js +++ b/packages/server-side-render/src/server-side-render.js @@ -98,7 +98,7 @@ export default function ServerSideRender( props ) { LoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder, } = props; - const isMountedRef = useRef( true ); + const isMountedRef = useRef( false ); const [ showLoader, setShowLoader ] = useState( false ); const fetchRequestRef = useRef(); const [ response, setResponse ] = useState( null ); @@ -112,6 +112,11 @@ export default function ServerSideRender( props ) { setIsLoading( true ); + // Schedule showing the Spinner after 1 second. + const timeout = setTimeout( () => { + setShowLoader( true ); + }, 1000 ); + let sanitizedAttributes = attributes && __experimentalSanitizeBlockAttributes( block, attributes ); @@ -165,6 +170,9 @@ export default function ServerSideRender( props ) { fetchRequest === fetchRequestRef.current ) { setIsLoading( false ); + // Cancel the timeout to show the Spinner. + setShowLoader( false ); + clearTimeout( timeout ); } } ) ); @@ -175,12 +183,12 @@ export default function ServerSideRender( props ) { // When the component unmounts, set isMountedRef to false. This will // let the async fetch callbacks know when to stop. - useEffect( - () => () => { + useEffect( () => { + isMountedRef.current = true; + return () => { isMountedRef.current = false; - }, - [] - ); + }; + }, [] ); useEffect( () => { // Don't debounce the first fetch. This ensures that the first render @@ -192,21 +200,6 @@ export default function ServerSideRender( props ) { } } ); - /** - * Effect to handle showing the loading placeholder. - * Show it only if there is no previous response or - * the request takes more than one second. - */ - useEffect( () => { - if ( ! isLoading ) { - return; - } - const timeout = setTimeout( () => { - setShowLoader( true ); - }, 1000 ); - return () => clearTimeout( timeout ); - }, [ isLoading ] ); - const hasResponse = !! response; const hasEmptyResponse = response === ''; const hasError = response?.error; diff --git a/packages/shortcode/CHANGELOG.md b/packages/shortcode/CHANGELOG.md index 4cd5c9c886736e..bcb92c69a7ef4a 100644 --- a/packages/shortcode/CHANGELOG.md +++ b/packages/shortcode/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index 2c227eb976bf1b..baeb4000c0420c 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/shortcode", - "version": "3.58.0", + "version": "4.0.0", "description": "Shortcode module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/style-engine/CHANGELOG.md b/packages/style-engine/CHANGELOG.md index 8e9bcaebfb4509..006fc960398b6a 100644 --- a/packages/style-engine/CHANGELOG.md +++ b/packages/style-engine/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index 008f58cf729e85..9ebe2800e1b411 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/style-engine", - "version": "1.41.0", + "version": "2.0.0", "description": "A suite of parsers and compilers for WordPress styles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/stylelint-config/CHANGELOG.md b/packages/stylelint-config/CHANGELOG.md index ee2aae030222e2..9c26f9efa6e03e 100644 --- a/packages/stylelint-config/CHANGELOG.md +++ b/packages/stylelint-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 22.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json index cae248d0d7164a..dbb0a0899b726b 100644 --- a/packages/stylelint-config/package.json +++ b/packages/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/stylelint-config", - "version": "21.41.0", + "version": "22.0.0", "description": "stylelint config for WordPress development.", "author": "The WordPress Contributors", "license": "MIT", diff --git a/packages/sync/CHANGELOG.md b/packages/sync/CHANGELOG.md index 16abed443cf820..b4c3d770142183 100644 --- a/packages/sync/CHANGELOG.md +++ b/packages/sync/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/sync/package.json b/packages/sync/package.json index f02019326e6649..3bda7acb0e1187 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/sync", - "version": "0.20.0", + "version": "1.0.0", "description": "Sync Data.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/token-list/CHANGELOG.md b/packages/token-list/CHANGELOG.md index df5526e5d14495..3cd9c7e7aa5925 100644 --- a/packages/token-list/CHANGELOG.md +++ b/packages/token-list/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/token-list/package.json b/packages/token-list/package.json index f70b9cba52ae52..ee46c9f269c47e 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/token-list", - "version": "2.58.0", + "version": "3.0.0", "description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/undo-manager/CHANGELOG.md b/packages/undo-manager/CHANGELOG.md index 7882af9711a1a9..36e815bbe8e2ba 100644 --- a/packages/undo-manager/CHANGELOG.md +++ b/packages/undo-manager/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/undo-manager/package.json b/packages/undo-manager/package.json index 1fd6dfcd1a785d..ed4f4a8e6b9204 100644 --- a/packages/undo-manager/package.json +++ b/packages/undo-manager/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/undo-manager", - "version": "0.18.0", + "version": "1.0.0", "description": "A small package to manage undo/redo.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md index 838566e7837ba9..172f881f369223 100644 --- a/packages/url/CHANGELOG.md +++ b/packages/url/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/url/package.json b/packages/url/package.json index cd747ae652239e..89d3e673254894 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "3.59.0", + "version": "4.0.0", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md index 7763a587ea082b..7f3e15b09fc542 100644 --- a/packages/viewport/CHANGELOG.md +++ b/packages/viewport/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 3c6dc5efd540c5..fe54fccb2ca496 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "5.35.0", + "version": "6.0.0", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/warning/CHANGELOG.md b/packages/warning/CHANGELOG.md index a10a7073d0a5a7..cfc8338f86c597 100644 --- a/packages/warning/CHANGELOG.md +++ b/packages/warning/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/warning/package.json b/packages/warning/package.json index 5b4b7234e1f699..6052f207b0d3f9 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/warning", - "version": "2.58.0", + "version": "3.0.0", "description": "Warning utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/widgets/CHANGELOG.md b/packages/widgets/CHANGELOG.md index c4833fefe258cf..3b318660859eb4 100644 --- a/packages/widgets/CHANGELOG.md +++ b/packages/widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/widgets/package.json b/packages/widgets/package.json index d15ccd484e966f..cbbc3a928b9b4e 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/widgets", - "version": "3.35.0", + "version": "4.0.0", "description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/wordcount/CHANGELOG.md b/packages/wordcount/CHANGELOG.md index dd7f233b7cc9bb..73042aef093a31 100644 --- a/packages/wordcount/CHANGELOG.md +++ b/packages/wordcount/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index b450c0ef55ff27..4bbda336f2eb75 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/wordcount", - "version": "3.58.0", + "version": "4.0.0", "description": "WordPress word count utility.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/phpunit/class-wp-theme-json-schema-test.php b/phpunit/class-wp-theme-json-schema-test.php index 4259b8b5de6d4f..24a7cf0e2e43d2 100644 --- a/phpunit/class-wp-theme-json-schema-test.php +++ b/phpunit/class-wp-theme-json-schema-test.php @@ -223,6 +223,20 @@ public function test_migrate_v2_to_latest() { ), ), ), + 'spacing' => array( + 'spacingSizes' => array( + array( + 'name' => 'Small', + 'slug' => 20, + 'size' => '20px', + ), + array( + 'name' => 'Large', + 'slug' => 80, + 'size' => '80px', + ), + ), + ), ), ); @@ -246,6 +260,21 @@ public function test_migrate_v2_to_latest() { ), ), ), + 'spacing' => array( + 'defaultSpacingSizes' => false, + 'spacingSizes' => array( + array( + 'name' => 'Small', + 'slug' => 20, + 'size' => '20px', + ), + array( + 'name' => 'Large', + 'slug' => 80, + 'size' => '80px', + ), + ), + ), ), ); diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 28401bb20d484d..549898b3c8773c 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -503,6 +503,11 @@ public function test_get_stylesheet() { ), ), ), + 'core/media-text' => array( + 'typography' => array( + 'textAlign' => 'center', + ), + ), 'core/post-date' => array( 'color' => array( 'text' => '#123456', @@ -547,7 +552,7 @@ public function test_get_stylesheet() { ); $variables = ':root{--wp--preset--color--grey: grey;--wp--preset--gradient--custom-gradient: linear-gradient(135deg,rgba(0,0,0) 0%,rgb(0,0,0) 100%);--wp--preset--font-size--small: 14px;--wp--preset--font-size--big: 41px;--wp--preset--font-family--arial: Arial, serif;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = static::$base_styles . ':root :where(body){color: var(--wp--preset--color--grey);}:root :where(a:where(:not(.wp-element-button))){background-color: #333;color: #111;}:root :where(.wp-element-button, .wp-block-button__link){box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.66);}:root :where(.wp-block-cover){min-height: unset;aspect-ratio: 16/9;}:root :where(.wp-block-group){background: var(--wp--preset--gradient--custom-gradient);border-radius: 10px;min-height: 50vh;padding: 24px;}:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: #111;}:root :where(.wp-block-heading){color: #123456;}:root :where(.wp-block-heading a:where(:not(.wp-element-button))){background-color: #333;color: #111;font-size: 60px;}:root :where(.wp-block-post-date){color: #123456;}:root :where(.wp-block-post-date a:where(:not(.wp-element-button))){background-color: #777;color: #555;}:root :where(.wp-block-post-excerpt){column-count: 2;}:root :where(.wp-block-image){margin-bottom: 30px;}:root :where(.wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder){border-top-left-radius: 10px;border-bottom-right-radius: 1em;}:root :where(.wp-block-image img, .wp-block-image .components-placeholder){filter: var(--wp--preset--duotone--custom-duotone);}'; + $styles = static::$base_styles . ':root :where(body){color: var(--wp--preset--color--grey);}:root :where(a:where(:not(.wp-element-button))){background-color: #333;color: #111;}:root :where(.wp-element-button, .wp-block-button__link){box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.66);}:root :where(.wp-block-cover){min-height: unset;aspect-ratio: 16/9;}:root :where(.wp-block-group){background: var(--wp--preset--gradient--custom-gradient);border-radius: 10px;min-height: 50vh;padding: 24px;}:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: #111;}:root :where(.wp-block-heading){color: #123456;}:root :where(.wp-block-heading a:where(:not(.wp-element-button))){background-color: #333;color: #111;font-size: 60px;}:root :where(.wp-block-media-text){text-align: center;}:root :where(.wp-block-post-date){color: #123456;}:root :where(.wp-block-post-date a:where(:not(.wp-element-button))){background-color: #777;color: #555;}:root :where(.wp-block-post-excerpt){column-count: 2;}:root :where(.wp-block-image){margin-bottom: 30px;}:root :where(.wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder){border-top-left-radius: 10px;border-bottom-right-radius: 1em;}:root :where(.wp-block-image img, .wp-block-image .components-placeholder){filter: var(--wp--preset--duotone--custom-duotone);}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-custom-gradient-gradient-background{background: var(--wp--preset--gradient--custom-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-big-font-size{font-size: var(--wp--preset--font-size--big) !important;}.has-arial-font-family{font-family: var(--wp--preset--font-family--arial) !important;}'; $all = $variables . $styles . $presets; @@ -4276,10 +4281,10 @@ public function test_set_spacing_sizes( $spacing_scale, $expected_output ) { 'spacingScale' => $spacing_scale, ), ), - ) + ), + 'default' ); - $theme_json->set_spacing_sizes(); $this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) ); } @@ -4300,7 +4305,7 @@ public function data_set_spacing_sizes() { ), 'expected_output' => array( array( - 'name' => '1', + 'name' => 'Medium', 'slug' => '50', 'size' => '4rem', ), @@ -4316,12 +4321,12 @@ public function data_set_spacing_sizes() { ), 'expected_output' => array( array( - 'name' => '1', + 'name' => 'Medium', 'slug' => '50', 'size' => '4rem', ), array( - 'name' => '2', + 'name' => 'Large', 'slug' => '60', 'size' => '5.5rem', ), @@ -4337,17 +4342,17 @@ public function data_set_spacing_sizes() { ), 'expected_output' => array( array( - 'name' => '1', + 'name' => 'Small', 'slug' => '40', 'size' => '2.5rem', ), array( - 'name' => '2', + 'name' => 'Medium', 'slug' => '50', 'size' => '4rem', ), array( - 'name' => '3', + 'name' => 'Large', 'slug' => '60', 'size' => '5.5rem', ), @@ -4363,22 +4368,22 @@ public function data_set_spacing_sizes() { ), 'expected_output' => array( array( - 'name' => '1', + 'name' => 'Small', 'slug' => '40', 'size' => '2.5rem', ), array( - 'name' => '2', + 'name' => 'Medium', 'slug' => '50', 'size' => '4rem', ), array( - 'name' => '3', + 'name' => 'Large', 'slug' => '60', 'size' => '5.5rem', ), array( - 'name' => '4', + 'name' => 'X-Large', 'slug' => '70', 'size' => '7rem', ), @@ -4394,27 +4399,27 @@ public function data_set_spacing_sizes() { ), 'expected_output' => array( array( - 'name' => '1', + 'name' => 'Small', 'slug' => '40', 'size' => '2.5rem', ), array( - 'name' => '2', + 'name' => 'Medium', 'slug' => '50', 'size' => '5rem', ), array( - 'name' => '3', + 'name' => 'Large', 'slug' => '60', 'size' => '7.5rem', ), array( - 'name' => '4', + 'name' => 'X-Large', 'slug' => '70', 'size' => '10rem', ), array( - 'name' => '5', + 'name' => '2X-Large', 'slug' => '80', 'size' => '12.5rem', ), @@ -4430,27 +4435,27 @@ public function data_set_spacing_sizes() { ), 'expected_output' => array( array( - 'name' => '1', + 'name' => 'X-Small', 'slug' => '30', 'size' => '0.67rem', ), array( - 'name' => '2', + 'name' => 'Small', 'slug' => '40', 'size' => '1rem', ), array( - 'name' => '3', + 'name' => 'Medium', 'slug' => '50', 'size' => '1.5rem', ), array( - 'name' => '4', + 'name' => 'Large', 'slug' => '60', 'size' => '2.25rem', ), array( - 'name' => '5', + 'name' => 'X-Large', 'slug' => '70', 'size' => '3.38rem', ), @@ -4466,27 +4471,27 @@ public function data_set_spacing_sizes() { ), 'expected_output' => array( array( - 'name' => '1', + 'name' => 'X-Small', 'slug' => '30', 'size' => '0.09rem', ), array( - 'name' => '2', + 'name' => 'Small', 'slug' => '40', 'size' => '0.38rem', ), array( - 'name' => '3', + 'name' => 'Medium', 'slug' => '50', 'size' => '1.5rem', ), array( - 'name' => '4', + 'name' => 'Large', 'slug' => '60', 'size' => '6rem', ), array( - 'name' => '5', + 'name' => 'X-Large', 'slug' => '70', 'size' => '24rem', ), @@ -4555,9 +4560,6 @@ public function data_set_spacing_sizes() { * @param array $expected_output Expected output from data provider. */ public function test_set_spacing_sizes_when_invalid( $spacing_scale, $expected_output ) { - $this->expectException( Exception::class ); - $this->expectExceptionMessage( 'Some of the theme.json settings.spacing.spacingScale values are invalid' ); - $theme_json = new WP_Theme_JSON_Gutenberg( array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, @@ -4566,19 +4568,10 @@ public function test_set_spacing_sizes_when_invalid( $spacing_scale, $expected_o 'spacingScale' => $spacing_scale, ), ), - ) - ); - - // Ensure PHPUnit 10 compatibility. - set_error_handler( - static function ( $errno, $errstr ) { - restore_error_handler(); - throw new Exception( $errstr, $errno ); - }, - E_ALL + ), + 'default' ); - $theme_json->set_spacing_sizes(); $this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) ); } @@ -4597,7 +4590,7 @@ public function data_set_spacing_sizes_when_invalid() { 'mediumStep' => 4, 'unit' => 'rem', ), - 'expected_output' => null, + 'expected_output' => array(), ), 'non numeric increment' => array( 'spacing_scale' => array( @@ -4607,7 +4600,7 @@ public function data_set_spacing_sizes_when_invalid() { 'mediumStep' => 4, 'unit' => 'rem', ), - 'expected_output' => null, + 'expected_output' => array(), ), 'non numeric steps' => array( 'spacing_scale' => array( @@ -4617,7 +4610,7 @@ public function data_set_spacing_sizes_when_invalid() { 'mediumStep' => 4, 'unit' => 'rem', ), - 'expected_output' => null, + 'expected_output' => array(), ), 'non numeric medium step' => array( 'spacing_scale' => array( @@ -4627,7 +4620,7 @@ public function data_set_spacing_sizes_when_invalid() { 'mediumStep' => 'That which is just right', 'unit' => 'rem', ), - 'expected_output' => null, + 'expected_output' => array(), ), 'missing unit value' => array( 'spacing_scale' => array( @@ -4636,7 +4629,7 @@ public function data_set_spacing_sizes_when_invalid() { 'steps' => 5, 'mediumStep' => 4, ), - 'expected_output' => null, + 'expected_output' => array(), ), ); } diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 6a3ec7e81d3947..bdfea7279f67ac 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -428,6 +428,11 @@ "type": "boolean", "default": true }, + "defaultSpacingSizes": { + "description": "Allow users to choose space sizes from the default space size presets.", + "type": "boolean", + "default": true + }, "spacingSizes": { "description": "Space size presets for the space size selector.\nGenerates a custom property (`--wp--preset--spacing--{slug}`) per preset value.", "type": "array", @@ -439,8 +444,9 @@ "type": "string" }, "slug": { - "description": "Unique identifier for the space size preset. For best cross theme compatibility these should be in the form '10','20','30','40','50','60', etc. with '50' representing the 'Medium' size step.", - "type": "string" + "description": "Unique identifier for the space size preset. Will be sorted numerically. For best cross theme compatibility these should be in the form '10','20','30','40','50','60', etc. with '50' representing the 'Medium' size step.", + "type": "string", + "pattern": "^[0-9].*$" }, "size": { "description": "CSS space-size value, including units.", @@ -463,16 +469,22 @@ "increment": { "description": "The amount to increment each step by.", "type": "number", + "exclusiveMinimum": true, + "minimum": 0, "default": 1.5 }, "steps": { "description": "Number of steps to generate in scale.", "type": "integer", + "minimum": 1, + "maximum": 10, "default": 7 }, "mediumStep": { "description": "The value to medium setting in the scale.", "type": "number", + "exclusiveMinimum": true, + "minimum": 0, "default": 1.5 }, "unit": { diff --git a/storybook/manager-head.html b/storybook/manager-head.html index 08df7dfdb72575..ab9c1a71010718 100644 --- a/storybook/manager-head.html +++ b/storybook/manager-head.html @@ -4,6 +4,7 @@ 'navigation', 'customselectcontrol-v2', 'theme', + 'progressbar', ]; const REDIRECTS = [ { diff --git a/test/e2e/specs/editor/blocks/columns.spec.js b/test/e2e/specs/editor/blocks/columns.spec.js index 8ddf7e9377ff20..e322a52eeba10b 100644 --- a/test/e2e/specs/editor/blocks/columns.spec.js +++ b/test/e2e/specs/editor/blocks/columns.spec.js @@ -40,7 +40,7 @@ test.describe( 'Columns', () => { // Verify Column const inserterOptions = page.locator( - 'role=region[name="Block Library"i] >> role=option' + 'role=region[name="Block Library"i] >> .block-editor-inserter__insertable-blocks-at-selection >> role=option' ); await expect( inserterOptions ).toHaveCount( 1 ); await expect( inserterOptions ).toHaveText( 'Column' ); diff --git a/test/e2e/specs/editor/plugins/child-blocks.spec.js b/test/e2e/specs/editor/plugins/child-blocks.spec.js index b3073b70a5409a..0cd043c6a46105 100644 --- a/test/e2e/specs/editor/plugins/child-blocks.spec.js +++ b/test/e2e/specs/editor/plugins/child-blocks.spec.js @@ -48,9 +48,13 @@ test.describe( 'Child Blocks', () => { const blockInserter = page .getByRole( 'toolbar', { name: 'Document tools' } ) .getByRole( 'button', { name: 'Toggle block inserter' } ); - const blockLibrary = page.getByRole( 'region', { - name: 'Block Library', - } ); + const blockLibrary = page + .getByRole( 'region', { + name: 'Block Library', + } ) + .locator( + '.block-editor-inserter__insertable-blocks-at-selection' + ); await blockInserter.click(); await expect( blockLibrary ).toBeVisible(); @@ -82,9 +86,13 @@ test.describe( 'Child Blocks', () => { const blockInserter = page .getByRole( 'toolbar', { name: 'Document tools' } ) .getByRole( 'button', { name: 'Toggle block inserter' } ); - const blockLibrary = page.getByRole( 'region', { - name: 'Block Library', - } ); + const blockLibrary = page + .getByRole( 'region', { + name: 'Block Library', + } ) + .locator( + '.block-editor-inserter__insertable-blocks-at-selection' + ); await blockInserter.click(); await expect( blockLibrary ).toBeVisible(); diff --git a/test/e2e/specs/editor/plugins/custom-post-types.spec.js b/test/e2e/specs/editor/plugins/custom-post-types.spec.js index 9aabe4e7454f9f..36050415925c19 100644 --- a/test/e2e/specs/editor/plugins/custom-post-types.spec.js +++ b/test/e2e/specs/editor/plugins/custom-post-types.spec.js @@ -36,16 +36,7 @@ test.describe( 'Test Custom Post Types', () => { } ) .click(); - // Open the Document -> Page Attributes panel. - const pageAttributes = page.getByRole( 'button', { - name: 'Page Attributes', - } ); - const isClosed = - ( await pageAttributes.getAttribute( 'aria-expanded' ) ) === - 'false'; - if ( isClosed ) { - await pageAttributes.click(); - } + await page.locator( '.editor-post-parent__panel-toggle' ).click(); const parentPageLocator = page.getByRole( 'combobox', { name: 'Parent', @@ -62,6 +53,8 @@ test.describe( 'Test Custom Post Types', () => { await editor.publishPost(); await page.reload(); + await page.locator( '.editor-post-parent__panel-toggle' ).click(); + // Confirm parent page selection matches after reloading. await expect( parentPageLocator ).toHaveValue( parentPage ); } ); diff --git a/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js b/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js index eaf171adf9313c..d2dc521f0196bd 100644 --- a/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js +++ b/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js @@ -46,9 +46,13 @@ test.describe( 'Allowed Blocks Setting on InnerBlocks', () => { const blockInserter = page .getByRole( 'toolbar', { name: 'Document tools' } ) .getByRole( 'button', { name: 'Toggle block inserter' } ); - const blockLibrary = page.getByRole( 'region', { - name: 'Block Library', - } ); + const blockLibrary = page + .getByRole( 'region', { + name: 'Block Library', + } ) + .locator( + '.block-editor-inserter__insertable-blocks-at-selection' + ); await blockInserter.click(); await expect( blockLibrary ).toBeVisible(); @@ -89,9 +93,13 @@ test.describe( 'Allowed Blocks Setting on InnerBlocks', () => { const blockInserter = page .getByRole( 'toolbar', { name: 'Document tools' } ) .getByRole( 'button', { name: 'Toggle block inserter' } ); - const blockLibrary = page.getByRole( 'region', { - name: 'Block Library', - } ); + const blockLibrary = page + .getByRole( 'region', { + name: 'Block Library', + } ) + .locator( + '.block-editor-inserter__insertable-blocks-at-selection' + ); await blockInserter.click(); await expect( blockLibrary ).toBeVisible(); diff --git a/test/e2e/specs/editor/various/block-bindings.spec.js b/test/e2e/specs/editor/various/block-bindings.spec.js index 97b8579bb07ba6..87e5b2f2e10b11 100644 --- a/test/e2e/specs/editor/various/block-bindings.spec.js +++ b/test/e2e/specs/editor/various/block-bindings.spec.js @@ -1193,11 +1193,6 @@ test.describe( 'Block bindings', () => { await expect( paragraphBlock ).toHaveText( 'Value of the text_custom_field' ); - // Paragraph is not editable. - await expect( paragraphBlock ).toHaveAttribute( - 'contenteditable', - 'false' - ); // Check the frontend shows the value of the custom field. const postId = await editor.publishPost(); @@ -1331,6 +1326,12 @@ test.describe( 'Block bindings', () => { }, }, } ); + // Select the paragraph and press Enter at the end of it. + const paragraph = editor.canvas.getByRole( 'document', { + name: 'Block: Paragraph', + } ); + await editor.selectBlocks( paragraph ); + await page.keyboard.press( 'End' ); await page.keyboard.press( 'Enter' ); const [ initialParagraph, newEmptyParagraph ] = await editor.canvas @@ -1342,6 +1343,70 @@ test.describe( 'Block bindings', () => { await expect( newEmptyParagraph ).toHaveText( '' ); await expect( newEmptyParagraph ).toBeEditable(); } ); + + test( 'should NOT be possible to edit the value of the custom field when it is protected', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { + anchor: 'protected-field-binding', + content: 'fallback value', + metadata: { + bindings: { + content: { + source: 'core/post-meta', + args: { key: '_protected_field' }, + }, + }, + }, + }, + } ); + + const protectedFieldBlock = editor.canvas.getByRole( + 'document', + { + name: 'Block: Paragraph', + } + ); + + await expect( protectedFieldBlock ).toHaveAttribute( + 'contenteditable', + 'false' + ); + } ); + + test( 'should NOT be possible to edit the value of the custom field when it is not shown in the REST API', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { + anchor: 'show-in-rest-false-binding', + content: 'fallback value', + metadata: { + bindings: { + content: { + source: 'core/post-meta', + args: { key: 'show_in_rest_false_field' }, + }, + }, + }, + }, + } ); + + const showInRestFalseBlock = editor.canvas.getByRole( + 'document', + { + name: 'Block: Paragraph', + } + ); + + await expect( showInRestFalseBlock ).toHaveAttribute( + 'contenteditable', + 'false' + ); + } ); } ); test.describe( 'Heading', () => { @@ -1370,11 +1435,6 @@ test.describe( 'Block bindings', () => { await expect( headingBlock ).toHaveText( 'Value of the text_custom_field' ); - // Heading is not editable. - await expect( headingBlock ).toHaveAttribute( - 'contenteditable', - 'false' - ); // Check the frontend shows the value of the custom field. const postId = await editor.publishPost(); @@ -1406,6 +1466,13 @@ test.describe( 'Block bindings', () => { }, }, } ); + + // Select the heading and press Enter at the end of it. + const heading = editor.canvas.getByRole( 'document', { + name: 'Block: Heading', + } ); + await editor.selectBlocks( heading ); + await page.keyboard.press( 'End' ); await page.keyboard.press( 'Enter' ); // Can't use `editor.getBlocks` because it doesn't return the meta value shown in the editor. const [ initialHeading, newEmptyParagraph ] = @@ -1465,12 +1532,6 @@ test.describe( 'Block bindings', () => { 'Value of the text_custom_field' ); - // Button is not editable. - await expect( buttonBlock ).toHaveAttribute( - 'contenteditable', - 'false' - ); - // Check the frontend shows the value of the custom field. const postId = await editor.publishPost(); await page.goto( `/?p=${ postId }` ); @@ -1599,6 +1660,7 @@ test.describe( 'Block bindings', () => { } ) .getByRole( 'textbox' ) .click(); + await page.keyboard.press( 'End' ); await page.keyboard.press( 'Enter' ); const [ initialButton, newEmptyButton ] = await editor.canvas .locator( '[data-type="core/button"]' ) @@ -1723,12 +1785,7 @@ test.describe( 'Block bindings', () => { imagePlaceholderSrc ); - // Alt textarea is disabled and with the custom field value. - await expect( - page - .getByRole( 'tabpanel', { name: 'Settings' } ) - .getByLabel( 'Alternative text' ) - ).toHaveAttribute( 'readonly' ); + // Alt textarea should have the custom field value. const altValue = await page .getByRole( 'tabpanel', { name: 'Settings' } ) .getByLabel( 'Alternative text' ) @@ -1789,7 +1846,7 @@ test.describe( 'Block bindings', () => { imagePlaceholderSrc ); - // Title input is disabled and with the custom field value. + // Title input should have the custom field value. const advancedButton = page .getByRole( 'tabpanel', { name: 'Settings' } ) .getByRole( 'button', { @@ -1800,11 +1857,6 @@ test.describe( 'Block bindings', () => { if ( isAdvancedPanelOpen === 'false' ) { await advancedButton.click(); } - await expect( - page - .getByRole( 'tabpanel', { name: 'Settings' } ) - .getByLabel( 'Title attribute' ) - ).toHaveAttribute( 'readonly' ); const titleValue = await page .getByRole( 'tabpanel', { name: 'Settings' } ) .getByLabel( 'Title attribute' ) @@ -1869,19 +1921,14 @@ test.describe( 'Block bindings', () => { imageCustomFieldSrc ); - // Alt textarea is disabled and with the custom field value. - await expect( - page - .getByRole( 'tabpanel', { name: 'Settings' } ) - .getByLabel( 'Alternative text' ) - ).toHaveAttribute( 'readonly' ); + // Alt textarea should have the custom field value. const altValue = await page .getByRole( 'tabpanel', { name: 'Settings' } ) .getByLabel( 'Alternative text' ) .inputValue(); expect( altValue ).toBe( 'Value of the text_custom_field' ); - // Title input is enabled and with the original value. + // Title input should have the original value. const advancedButton = page .getByRole( 'tabpanel', { name: 'Settings' } ) .getByRole( 'button', { @@ -1892,11 +1939,6 @@ test.describe( 'Block bindings', () => { if ( isAdvancedPanelOpen === 'false' ) { await advancedButton.click(); } - await expect( - page - .getByRole( 'tabpanel', { name: 'Settings' } ) - .getByLabel( 'Title attribute' ) - ).toBeEnabled(); const titleValue = await page .getByRole( 'tabpanel', { name: 'Settings' } ) .getByLabel( 'Title attribute' ) @@ -1922,6 +1964,208 @@ test.describe( 'Block bindings', () => { ); } ); } ); + + test.describe( 'Edit custom fields', () => { + test( 'should be possible to edit the value of the custom field from the paragraph', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { + anchor: 'paragraph-binding', + content: 'paragraph default content', + metadata: { + bindings: { + content: { + source: 'core/post-meta', + args: { key: 'text_custom_field' }, + }, + }, + }, + }, + } ); + const paragraphBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Paragraph', + } ); + + await expect( paragraphBlock ).toHaveAttribute( + 'contenteditable', + 'true' + ); + await paragraphBlock.fill( 'new value' ); + // Check that the paragraph content attribute didn't change. + const [ paragraphBlockObject ] = await editor.getBlocks(); + expect( paragraphBlockObject.attributes.content ).toBe( + 'paragraph default content' + ); + // Check the value of the custom field is being updated by visiting the frontend. + const previewPage = await editor.openPreviewPage(); + await expect( + previewPage.locator( '#paragraph-binding' ) + ).toHaveText( 'new value' ); + } ); + + test( 'should be possible to edit the value of the url custom field from the button', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { + name: 'core/buttons', + innerBlocks: [ + { + name: 'core/button', + attributes: { + anchor: 'button-url-binding', + text: 'button default text', + url: '#default-url', + metadata: { + bindings: { + url: { + source: 'core/post-meta', + args: { key: 'url_custom_field' }, + }, + }, + }, + }, + }, + ], + } ); + + // Edit the url. + const buttonBlock = editor.canvas + .getByRole( 'document', { + name: 'Block: Button', + exact: true, + } ) + .getByRole( 'textbox' ); + await buttonBlock.click(); + await page + .getByRole( 'button', { name: 'Edit link', exact: true } ) + .click(); + await page + .getByPlaceholder( 'Search or type url' ) + .fill( '#url-custom-field-modified' ); + await pageUtils.pressKeys( 'Enter' ); + + // Check that the button url attribute didn't change. + const [ buttonsObject ] = await editor.getBlocks(); + expect( buttonsObject.innerBlocks[ 0 ].attributes.url ).toBe( + '#default-url' + ); + // Check the value of the custom field is being updated by visiting the frontend. + const previewPage = await editor.openPreviewPage(); + await expect( + previewPage.locator( '#button-url-binding a' ) + ).toHaveAttribute( 'href', '#url-custom-field-modified' ); + } ); + + test( 'should be possible to edit the value of the url custom field from the image', async ( { + editor, + page, + pageUtils, + requestUtils, + } ) => { + const customFieldMedia = await requestUtils.uploadMedia( + path.join( + './test/e2e/assets', + '1024x768_e2e_test_image_size.jpeg' + ) + ); + imageCustomFieldSrc = customFieldMedia.source_url; + + await editor.insertBlock( { + name: 'core/image', + attributes: { + anchor: 'image-url-binding', + url: imagePlaceholderSrc, + alt: 'default alt value', + title: 'default title value', + metadata: { + bindings: { + url: { + source: 'core/post-meta', + args: { key: 'url_custom_field' }, + }, + }, + }, + }, + } ); + + // Edit image url. + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { + name: 'Replace', + } ) + .click(); + await page + .getByRole( 'button', { name: 'Edit link', exact: true } ) + .click(); + await page + .getByPlaceholder( 'Search or type url' ) + .fill( imageCustomFieldSrc ); + await pageUtils.pressKeys( 'Enter' ); + + // Check that the image url attribute didn't change and still uses the placeholder. + const [ imageBlockObject ] = await editor.getBlocks(); + expect( imageBlockObject.attributes.url ).toBe( + imagePlaceholderSrc + ); + + // Check the value of the custom field is being updated by visiting the frontend. + const previewPage = await editor.openPreviewPage(); + await expect( + previewPage.locator( '#image-url-binding img' ) + ).toHaveAttribute( 'src', imageCustomFieldSrc ); + } ); + + test( 'should be possible to edit the value of the text custom field from the image alt', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { + name: 'core/image', + attributes: { + anchor: 'image-alt-binding', + url: imagePlaceholderSrc, + alt: 'default alt value', + metadata: { + bindings: { + alt: { + source: 'core/post-meta', + args: { key: 'text_custom_field' }, + }, + }, + }, + }, + } ); + const imageBlockImg = editor.canvas + .getByRole( 'document', { + name: 'Block: Image', + } ) + .locator( 'img' ); + await imageBlockImg.click(); + + // Edit the custom field value in the alt textarea. + const altInputArea = page + .getByRole( 'tabpanel', { name: 'Settings' } ) + .getByLabel( 'Alternative text' ); + await expect( altInputArea ).not.toHaveAttribute( 'readonly' ); + await altInputArea.fill( 'new value' ); + + // Check that the image alt attribute didn't change. + const [ imageBlockObject ] = await editor.getBlocks(); + expect( imageBlockObject.attributes.alt ).toBe( + 'default alt value' + ); + // Check the value of the custom field is being updated by visiting the frontend. + const previewPage = await editor.openPreviewPage(); + await expect( + previewPage.locator( '#image-alt-binding img' ) + ).toHaveAttribute( 'alt', 'new value' ); + } ); + } ); } ); } ); diff --git a/test/e2e/specs/editor/various/copy-cut-paste.spec.js b/test/e2e/specs/editor/various/copy-cut-paste.spec.js index 539791d39e45f0..48392170e66dde 100644 --- a/test/e2e/specs/editor/various/copy-cut-paste.spec.js +++ b/test/e2e/specs/editor/various/copy-cut-paste.spec.js @@ -640,4 +640,41 @@ test.describe( 'Copy/cut/paste', () => { }, ] ); } ); + + // See https://github.com/WordPress/gutenberg/pull/61900 + test( 'should inherit heading attributes on paste split', async ( { + pageUtils, + editor, + page, + } ) => { + await editor.insertBlock( { + name: 'core/heading', + attributes: { + content: 'AB', + }, + } ); + await page.keyboard.press( 'ArrowRight' ); + + pageUtils.setClipboardData( { + html: 'ab', + } ); + await pageUtils.pressKeys( 'primary+v' ); + + expect( await editor.getBlocks() ).toMatchObject( [ + { + name: 'core/heading', + attributes: { + content: 'Aa', + level: 2, + }, + }, + { + name: 'core/heading', + attributes: { + content: 'bB', + level: 2, + }, + }, + ] ); + } ); } ); diff --git a/test/e2e/specs/editor/various/new-post.spec.js b/test/e2e/specs/editor/various/new-post.spec.js index b3591db1ec50b9..68d983e91045b7 100644 --- a/test/e2e/specs/editor/various/new-post.spec.js +++ b/test/e2e/specs/editor/various/new-post.spec.js @@ -40,7 +40,7 @@ test.describe( 'new editor state', () => { // Should display the Post Formats UI. await editor.openDocumentSettingsSidebar(); await expect( - page.locator( 'role=combobox[name="Post Format"i]' ) + page.locator( 'role=button[name="Change Format: Standard"i]' ) ).toBeVisible(); } ); diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js index 976f1c378daa97..f4648a03efe956 100644 --- a/test/e2e/specs/editor/various/pattern-overrides.spec.js +++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js @@ -105,7 +105,7 @@ test.describe( 'Pattern Overrides', () => { metadata: { name: editableParagraphName, bindings: { - content: { + __default: { source: 'core/pattern-overrides', }, }, @@ -234,7 +234,7 @@ test.describe( 'Pattern Overrides', () => { const paragraphName = 'paragraph-name'; const { id } = await requestUtils.createBlock( { title: 'Pattern', - content: ` + content: ` Editable `, status: 'publish', @@ -324,7 +324,7 @@ test.describe( 'Pattern Overrides', () => { const { id } = await requestUtils.createBlock( { title: 'Button with target', content: ` - + Button `, @@ -434,14 +434,14 @@ test.describe( 'Pattern Overrides', () => { const headingName = 'Editable heading'; const innerPattern = await requestUtils.createBlock( { title: 'Inner Pattern', - content: ` + content: ` Inner paragraph `, status: 'publish', } ); const outerPattern = await requestUtils.createBlock( { title: 'Outer Pattern', - content: ` + content: ` Outer heading `, @@ -535,10 +535,10 @@ test.describe( 'Pattern Overrides', () => { const paragraphName = 'Editable paragraph'; const { id } = await requestUtils.createBlock( { title: 'Pattern', - content: ` + content: ` Heading - + Paragraph `, status: 'publish', @@ -694,7 +694,7 @@ test.describe( 'Pattern Overrides', () => { ); const { id } = await requestUtils.createBlock( { title: 'Pattern', - content: ` + content: ` `, status: 'publish', @@ -878,4 +878,82 @@ test.describe( 'Pattern Overrides', () => { editorSettings.getByRole( 'button', { name: 'Enable overrides' } ) ).toBeHidden(); } ); + + // @see https://github.com/WordPress/gutenberg/pull/60694 + test( 'handles back-compat from individual attributes to __default', async ( { + page, + admin, + requestUtils, + editor, + } ) => { + const imageName = 'Editable image'; + const TEST_IMAGE_FILE_PATH = path.resolve( + __dirname, + '../../../assets/10x10_e2e_test_image_z9T8jK.png' + ); + const { id } = await requestUtils.createBlock( { + title: 'Pattern', + content: ` + +`, + status: 'publish', + } ); + + await admin.createNewPost(); + + await editor.insertBlock( { + name: 'core/block', + attributes: { ref: id }, + } ); + + const blocks = await editor.getBlocks( { full: true } ); + expect( blocks ).toMatchObject( [ + { + name: 'core/block', + attributes: { ref: id }, + }, + ] ); + expect( + await editor.getBlocks( { clientId: blocks[ 0 ].clientId } ) + ).toMatchObject( [ + { + name: 'core/image', + attributes: { + metadata: { + name: imageName, + bindings: { + __default: { + source: 'core/pattern-overrides', + }, + }, + }, + }, + }, + ] ); + + const imageBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Image', + } ); + await editor.selectBlocks( imageBlock ); + await imageBlock + .getByTestId( 'form-file-upload-input' ) + .setInputFiles( TEST_IMAGE_FILE_PATH ); + await expect( imageBlock.getByRole( 'img' ) ).toHaveCount( 1 ); + await expect( imageBlock.getByRole( 'img' ) ).toHaveAttribute( + 'src', + /\/wp-content\/uploads\// + ); + await editor.showBlockToolbar(); + await editor.clickBlockToolbarButton( 'Alt' ); + await page + .getByRole( 'textbox', { name: 'alternative text' } ) + .fill( 'Test Image' ); + + const postId = await editor.publishPost(); + + await page.goto( `/?p=${ postId }` ); + await expect( + page.getByRole( 'img', { name: 'Test Image' } ) + ).toHaveAttribute( 'src', /\/wp-content\/uploads\// ); + } ); } ); diff --git a/test/e2e/specs/editor/various/patterns.spec.js b/test/e2e/specs/editor/various/patterns.spec.js index 77f97ee3004a9e..781d4657a20b90 100644 --- a/test/e2e/specs/editor/various/patterns.spec.js +++ b/test/e2e/specs/editor/various/patterns.spec.js @@ -388,6 +388,13 @@ test.describe( 'Synced pattern', () => { .getByRole( 'button', { name: 'Add' } ) .click(); + // Wait until the pattern is created. + await expect( + editor.canvas.getByRole( 'document', { + name: 'Block: Pattern', + } ) + ).toBeVisible(); + await admin.createNewPost(); await editor.canvas .getByRole( 'button', { name: 'Add default block' } ) @@ -432,11 +439,11 @@ test.describe( 'Synced pattern', () => { .click(); // Wait until the pattern is created. - await editor.canvas - .getByRole( 'document', { + await expect( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern', } ) - .waitFor(); + ).toBeVisible(); // Check that only the pattern block is present. const existingBlocks = await editor.getBlocks(); diff --git a/test/e2e/specs/editor/various/publish-panel.spec.js b/test/e2e/specs/editor/various/publish-panel.spec.js index 534fea5289c9e1..c70a7b85cd70fe 100644 --- a/test/e2e/specs/editor/various/publish-panel.spec.js +++ b/test/e2e/specs/editor/various/publish-panel.spec.js @@ -113,4 +113,98 @@ test.describe( 'Post publish panel', () => { } ) ).toBeFocused(); } ); + + test( 'should show panel and indicator when metadata has been modified', async ( { + admin, + editor, + page, + } ) => { + await admin.createNewPost( { + title: 'Test metadata changes with save panel', + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { + content: 'paragraph default content', + metadata: { + bindings: { + content: { + source: 'core/post-meta', + args: { key: 'text_custom_field' }, + }, + }, + }, + }, + } ); + const postId = await editor.publishPost(); + const metadataUtils = new MetadataUtils( page ); + await metadataUtils.modifyPostMetadata( + 'post', + postId, + 'text_custom_field', + 'test value' + ); + const editorTopBar = page.getByRole( 'region', { + name: 'Editor top bar', + } ); + + const saveButton = editorTopBar.getByRole( 'button', { + name: 'Save', + exact: true, + } ); + + await expect( saveButton ).toBeVisible(); + + await saveButton.click(); + + const publishPanel = page.getByRole( 'region', { + name: 'Editor publish', + } ); + + await expect( publishPanel ).toBeVisible(); + + const postMetaPanel = publishPanel.locator( + '.entities-saved-states__post-meta' + ); + + await expect( postMetaPanel ).toBeVisible(); + } ); } ); + +/** + * Utilities for working with metadata. + * + * @param postType + * @param postId + * @param metaKey + * @param metaValue + */ +class MetadataUtils { + constructor( page ) { + this.page = page; + } + + async modifyPostMetadata( postType, postId, metaKey, metaValue ) { + const parameters = { + postType, + postId, + metaKey, + metaValue, + }; + + await this.page.evaluate( ( _parameters ) => { + window.wp.data + .dispatch( 'core' ) + .editEntityRecord( + 'postType', + _parameters.postType, + _parameters.postId, + { + meta: { + [ _parameters.metaKey ]: _parameters.metaValue, + }, + } + ); + }, parameters ); + } +} diff --git a/test/e2e/specs/editor/various/writing-flow.spec.js b/test/e2e/specs/editor/various/writing-flow.spec.js index 2dee26255c103b..2eee3cad2b73ff 100644 --- a/test/e2e/specs/editor/various/writing-flow.spec.js +++ b/test/e2e/specs/editor/various/writing-flow.spec.js @@ -595,6 +595,37 @@ test.describe( 'Writing Flow (@firefox, @webkit)', () => { ] ); } ); + test( 'should remove first empty paragraph on Backspace', async ( { + editor, + page, + } ) => { + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'ArrowUp' ); + + // Ensure setup is correct. + expect( await editor.getBlocks() ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '' }, + }, + { + name: 'core/paragraph', + attributes: { content: '2' }, + }, + ] ); + + await page.keyboard.press( 'Backspace' ); + + expect( await editor.getBlocks() ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '2' }, + }, + ] ); + } ); + test( 'should merge paragraphs', async ( { editor, page } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '1' ); diff --git a/test/e2e/specs/site-editor/block-removal.spec.js b/test/e2e/specs/site-editor/block-removal.spec.js index 14f0ae422cc988..7fc547c19e59e3 100644 --- a/test/e2e/specs/site-editor/block-removal.spec.js +++ b/test/e2e/specs/site-editor/block-removal.spec.js @@ -30,7 +30,9 @@ test.describe( 'Site editor block removal prompt', () => { .click(); // Select and try to remove Query Loop block - const listView = page.getByRole( 'region', { name: 'List View' } ); + const listView = page.getByRole( 'region', { + name: 'Document Overview', + } ); await listView.getByRole( 'link', { name: 'Query Loop' } ).click(); await page.keyboard.press( 'Backspace' ); @@ -52,7 +54,9 @@ test.describe( 'Site editor block removal prompt', () => { .click(); // Select and open child blocks of Query Loop block - const listView = page.getByRole( 'region', { name: 'List View' } ); + const listView = page.getByRole( 'region', { + name: 'Document Overview', + } ); await listView.getByRole( 'link', { name: 'Query Loop' } ).click(); await page.keyboard.press( 'ArrowRight' ); @@ -79,7 +83,9 @@ test.describe( 'Site editor block removal prompt', () => { .click(); // Select Query Loop list item - const listView = page.getByRole( 'region', { name: 'List View' } ); + const listView = page.getByRole( 'region', { + name: 'Document Overview', + } ); await listView.getByRole( 'link', { name: 'Query Loop' } ).click(); // Reveal its inner blocks in the list view diff --git a/test/e2e/specs/site-editor/list-view.spec.js b/test/e2e/specs/site-editor/list-view.spec.js index ba8a316ee10c4d..db514463a73d76 100644 --- a/test/e2e/specs/site-editor/list-view.spec.js +++ b/test/e2e/specs/site-editor/list-view.spec.js @@ -26,7 +26,7 @@ test.describe( 'Site Editor List View', () => { editor, } ) => { await expect( - page.locator( 'role=region[name="List View"i]' ) + page.locator( 'role=region[name="Document Overview"i]' ) ).toBeHidden(); // Turn on block list view by default. @@ -37,7 +37,7 @@ test.describe( 'Site Editor List View', () => { await page.reload(); await expect( - page.locator( 'role=region[name="List View"i]' ) + page.locator( 'role=region[name="Document Overview"i]' ) ).toBeVisible(); // The preferences cleanup. @@ -121,7 +121,7 @@ test.describe( 'Site Editor List View', () => { await pageUtils.pressKeys( 'shift+Tab' ); await expect( page - .getByRole( 'region', { name: 'List View' } ) + .getByRole( 'region', { name: 'Document Overview' } ) .getByRole( 'button', { name: 'Close', } ) diff --git a/test/e2e/specs/site-editor/navigation-editor.spec.js b/test/e2e/specs/site-editor/navigation-editor.spec.js index 47519dc3ba6138..2813ceb13748a5 100644 --- a/test/e2e/specs/site-editor/navigation-editor.spec.js +++ b/test/e2e/specs/site-editor/navigation-editor.spec.js @@ -45,7 +45,7 @@ test.describe( 'Editing Navigation Menus', () => { const listView = page .getByRole( 'region', { - name: 'List View', + name: 'Document Overview', } ) .getByRole( 'treegrid', { name: 'Block navigation structure', @@ -99,7 +99,7 @@ test.describe( 'Editing Navigation Menus', () => { // Check the standard tabs are not present. await expect( - sidebar.getByRole( 'tab', { name: 'List View' } ) + sidebar.getByRole( 'tab', { name: 'Document Overview' } ) ).toBeHidden(); await expect( sidebar.getByRole( 'tab', { name: 'Settings' } ) diff --git a/test/e2e/specs/site-editor/style-variations.spec.js b/test/e2e/specs/site-editor/style-variations.spec.js index f991b512e840df..c4dad7c7d7546c 100644 --- a/test/e2e/specs/site-editor/style-variations.spec.js +++ b/test/e2e/specs/site-editor/style-variations.spec.js @@ -157,7 +157,7 @@ test.describe( 'Global styles variations', () => { await page.click( 'role=button[name="pink"i]' ); await page.click( 'role=button[name="Back"i]' ); await page.click( 'role=button[name="Colors styles"i]' ); - await page.click( 'role=button[name="Color palettes"i]' ); + await page.click( 'role=button[name="Edit palette"i]' ); await expect( page.locator( 'role=option[name="Color: Foreground"i]' ) diff --git a/test/e2e/specs/site-editor/styles.spec.js b/test/e2e/specs/site-editor/styles.spec.js index 66ddf0b7faa1b5..8da9aa95c67567 100644 --- a/test/e2e/specs/site-editor/styles.spec.js +++ b/test/e2e/specs/site-editor/styles.spec.js @@ -45,15 +45,14 @@ test.describe( 'Styles', () => { .getByRole( 'button', { name: 'Social Icons block styles' } ) .click(); - // find the second padding control - const paddingControl = page - .locator( '[aria-label="padding"]' ) - .nth( 1 ); - - // Change the padding value - await paddingControl.fill( '2' ); + // Find the second padding control and change the padding value + await page + .getByRole( 'button', { name: 'Set custom size' } ) + .nth( 1 ) + .click(); + await page.getByRole( 'spinbutton', { name: 'padding' } ).fill( '35' ); // Check the padding value - await expect( block ).toHaveCSS( 'padding-left', '35.4644px' ); + await expect( block ).toHaveCSS( 'padding-left', '35px' ); } ); } ); diff --git a/test/native/setup.js b/test/native/setup.js index 12b61d15553cae..e93d2248bd03cb 100644 --- a/test/native/setup.js +++ b/test/native/setup.js @@ -109,6 +109,7 @@ jest.mock( '@wordpress/react-native-bridge', () => { subscribeOnUndoPressed: jest.fn(), subscribeOnRedoPressed: jest.fn(), subscribeConnectionStatus: jest.fn( () => ( { remove: jest.fn() } ) ), + subscribeToContentUpdate: jest.fn(), requestConnectionStatus: jest.fn( ( callback ) => callback( true ) ), editorDidMount: jest.fn(), showAndroidSoftKeyboard: jest.fn(), diff --git a/test/performance/specs/site-editor.spec.js b/test/performance/specs/site-editor.spec.js index dd052db5035dbe..4b31b24460dc3d 100644 --- a/test/performance/specs/site-editor.spec.js +++ b/test/performance/specs/site-editor.spec.js @@ -333,7 +333,7 @@ test.describe( 'Site Editor Performance', () => { .click(); } else { await page - .getByRole( 'button', { name: 'Transform into:' } ) + .getByRole( 'button', { name: 'Design' } ) .click(); }
A quick brown fox jumps over the lazy dog.
+
{ convertDescription }
{ `No default animation. Use one of type = "appear", "slide-in", or "loading".` }
+ { /* eslint-disable react/no-unescaped-entities */ } + No default animation. Use one of type = "appear", "slide-in", or + "loading". + { /* eslint-enable react/no-unescaped-entities */ } +
{ `paddingSize="medium"` }
paddingSize="medium"
{ `paddingSize="none"` }
paddingSize="none"
{ `Hit the "a" or "b" key in this textarea:` }
Hit the "a" or "b" key in this textarea:
Selected tab: Tab 3
Select a tab:
This panel item will not be displayed in the demo as its diff --git a/packages/components/src/tools-panel/tools-panel-header/component.tsx b/packages/components/src/tools-panel/tools-panel-header/component.tsx index b08981f94949a6..a30a8aa1c2f4b4 100644 --- a/packages/components/src/tools-panel/tools-panel-header/component.tsx +++ b/packages/components/src/tools-panel/tools-panel-header/component.tsx @@ -224,7 +224,7 @@ const ToolsPanelHeader = ( // @ts-expect-error - TODO: If this "tertiary" style is something we really want to allow on MenuItem, // we should rename it and explicitly allow it as an official API. All the other Button variants // don't make sense in a MenuItem context, and should be disallowed. - variant={ 'tertiary' } + variant="tertiary" onClick={ () => { if ( canResetAll ) { resetAll(); diff --git a/packages/components/src/unit-control/test/index.tsx b/packages/components/src/unit-control/test/index.tsx index 777004a6e8ae27..d91498d46478b3 100644 --- a/packages/components/src/unit-control/test/index.tsx +++ b/packages/components/src/unit-control/test/index.tsx @@ -138,7 +138,7 @@ describe( 'UnitControl', () => { const user = userEvent.setup(); const onChangeSpy = jest.fn(); - render( ); + render( ); const input = getInput(); await user.clear( input ); @@ -159,7 +159,7 @@ describe( 'UnitControl', () => { const user = userEvent.setup(); const onChangeSpy = jest.fn(); - render( ); + render( ); const input = getInput(); await user.type( input, '{ArrowUp}' ); @@ -175,7 +175,7 @@ describe( 'UnitControl', () => { const user = userEvent.setup(); const onChangeSpy = jest.fn(); - render( ); + render( ); const input = getInput(); await user.type( input, '{Shift>}{ArrowUp}{/Shift}' ); @@ -253,7 +253,7 @@ describe( 'UnitControl', () => { render( @@ -282,7 +282,7 @@ describe( 'UnitControl', () => { render( @@ -348,7 +348,7 @@ describe( 'UnitControl', () => { render( @@ -468,7 +468,7 @@ describe( 'UnitControl', () => { render( @@ -570,19 +570,19 @@ describe( 'UnitControl', () => { describe( 'Unit Parser', () => { it( 'should update unit after initial render and with new unit prop', async () => { - const { rerender } = render( ); + const { rerender } = render( ); const select = getSelect(); expect( select.value ).toBe( '%' ); - rerender( ); + rerender( ); expect( select.value ).toBe( 'vh' ); } ); it( 'should fallback to default unit if parsed unit is invalid', () => { - render( ); + render( ); expect( getSelect().value ).toBe( 'px' ); } ); @@ -590,7 +590,7 @@ describe( 'UnitControl', () => { it( 'should display valid CSS unit when not explicitly included in units list', () => { render( { render( diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md index 3ebf8f4b32e33b..702df49b9a28bb 100644 --- a/packages/compose/CHANGELOG.md +++ b/packages/compose/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/compose/README.md b/packages/compose/README.md index c60d202f59aacf..f4f0631ba36a51 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -69,7 +69,7 @@ This is inspired by `lodash`'s `flowRight` function. _Related_ -- +- ### createHigherOrderComponent @@ -145,7 +145,7 @@ This is inspired by `lodash`'s `flow` function. _Related_ -- +- ### pure @@ -247,7 +247,7 @@ Debounces a function similar to Lodash's `debounce`. A new debounced function wi _Related_ -- +- _Parameters_ @@ -535,7 +535,7 @@ Throttles a function similar to Lodash's `throttle`. A new throttled function wi _Related_ -- +- _Parameters_ diff --git a/packages/compose/package.json b/packages/compose/package.json index 0a3308e7adb0ac..7568f2f26f08dc 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "6.35.0", + "version": "7.0.0", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/compose/src/higher-order/compose.ts b/packages/compose/src/higher-order/compose.ts index 2b7b598c70c958..1fcaa55eb4512b 100644 --- a/packages/compose/src/higher-order/compose.ts +++ b/packages/compose/src/higher-order/compose.ts @@ -9,7 +9,7 @@ import { basePipe } from './pipe'; * * This is inspired by `lodash`'s `flowRight` function. * - * @see https://docs-lodash.com/v4/flow-right/ + * @see https://lodash.com/docs/4#flow-right */ const compose = basePipe( true ); diff --git a/packages/compose/src/higher-order/pipe.ts b/packages/compose/src/higher-order/pipe.ts index ced7618ad81bfd..29003d818ccc3a 100644 --- a/packages/compose/src/higher-order/pipe.ts +++ b/packages/compose/src/higher-order/pipe.ts @@ -43,7 +43,7 @@ * * Allows to choose whether to perform left-to-right or right-to-left composition. * - * @see https://docs-lodash.com/v4/flow/ + * @see https://lodash.com/docs/4#flow * * @param {boolean} reverse True if right-to-left, false for left-to-right composition. */ @@ -67,7 +67,7 @@ const basePipe = * * This is inspired by `lodash`'s `flow` function. * - * @see https://docs-lodash.com/v4/flow/ + * @see https://lodash.com/docs/4#flow */ const pipe = basePipe(); diff --git a/packages/compose/src/hooks/use-debounce/index.js b/packages/compose/src/hooks/use-debounce/index.js index 5d48dba91bde6e..6da42f159a8640 100644 --- a/packages/compose/src/hooks/use-debounce/index.js +++ b/packages/compose/src/hooks/use-debounce/index.js @@ -19,7 +19,7 @@ import { debounce } from '../../utils/debounce'; * including the function to debounce, so please wrap functions created on * render in components in `useCallback`. * - * @see https://docs-lodash.com/v4/debounce/ + * @see https://lodash.com/docs/4#debounce * * @template {(...args: any[]) => void} TFunc * diff --git a/packages/compose/src/hooks/use-throttle/index.js b/packages/compose/src/hooks/use-throttle/index.js index 8cade9a8442ac8..4bf6cc6e85555a 100644 --- a/packages/compose/src/hooks/use-throttle/index.js +++ b/packages/compose/src/hooks/use-throttle/index.js @@ -19,7 +19,7 @@ import { throttle } from '../../utils/throttle'; * including the function to throttle, so please wrap functions created on * render in components in `useCallback`. * - * @see https://docs-lodash.com/v4/throttle/ + * @see https://lodash.com/docs/4#throttle * * @template {(...args: any[]) => void} TFunc * diff --git a/packages/core-commands/CHANGELOG.md b/packages/core-commands/CHANGELOG.md index 528201ee2c5664..b05dda0cca2b23 100644 --- a/packages/core-commands/CHANGELOG.md +++ b/packages/core-commands/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json index 3efea9abf58e48..f79adf41f48984 100644 --- a/packages/core-commands/package.json +++ b/packages/core-commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-commands", - "version": "0.27.0", + "version": "1.0.0", "description": "WordPress core reusable commands.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index bce1d9e9d520bf..810e8ab00d21c9 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/core-data/package.json b/packages/core-data/package.json index ac77ad911becbe..984de2f7628ec4 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "6.35.0", + "version": "7.0.0", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index be4d12f0cb9ef6..36b0ba5f84c9d1 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -598,10 +598,14 @@ export const saveEntityRecord = return acc; }, { + // Do not update the `status` if we have edited it when auto saving. + // It's very important to let the user explicitly save this change, + // because it can lead to unexpected results. An example would be to + // have a draft post and change the status to publish. status: data.status === 'auto-draft' ? 'draft' - : data.status, + : undefined, } ); updatedRecord = await __unstableFetch( { diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md index a3e1cba44c0881..223f5de7796c62 100644 --- a/packages/create-block-interactive-template/CHANGELOG.md +++ b/packages/create-block-interactive-template/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/create-block-interactive-template/package.json b/packages/create-block-interactive-template/package.json index 337b950b0ac499..0e11939d484025 100644 --- a/packages/create-block-interactive-template/package.json +++ b/packages/create-block-interactive-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-interactive-template", - "version": "1.21.0", + "version": "2.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", diff --git a/packages/create-block-tutorial-template/CHANGELOG.md b/packages/create-block-tutorial-template/CHANGELOG.md index 125c54356e63bd..00537097ff945a 100644 --- a/packages/create-block-tutorial-template/CHANGELOG.md +++ b/packages/create-block-tutorial-template/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json index e03355e6e6a2a4..1b2c920457641d 100644 --- a/packages/create-block-tutorial-template/package.json +++ b/packages/create-block-tutorial-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-tutorial-template", - "version": "3.12.0", + "version": "4.0.0", "description": "This is a template for @wordpress/create-block that creates an example 'Copyright Date' block. This block is used in the official WordPress block development Quick Start Guide.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md index 409c2fbcc8ec50..0362b2a6f13412 100644 --- a/packages/create-block/CHANGELOG.md +++ b/packages/create-block/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.43.0 (2024-05-31) + ## 4.42.0 (2024-05-16) ## 4.41.0 (2024-05-02) diff --git a/packages/create-block/package.json b/packages/create-block/package.json index 8771a1064f3288..00acf80f0ac9d1 100644 --- a/packages/create-block/package.json +++ b/packages/create-block/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block", - "version": "4.42.0", + "version": "4.43.0", "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/customize-widgets/CHANGELOG.md b/packages/customize-widgets/CHANGELOG.md index 499363d1920ef2..aec25853b2914b 100644 --- a/packages/customize-widgets/CHANGELOG.md +++ b/packages/customize-widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index c3fe2dbd5e0bbf..ec320eee22a43a 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/customize-widgets", - "version": "4.35.0", + "version": "5.0.0", "description": "Widgets blocks in Customizer Module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data-controls/CHANGELOG.md b/packages/data-controls/CHANGELOG.md index f90608d4d8c0cd..6b94332e70db8b 100644 --- a/packages/data-controls/CHANGELOG.md +++ b/packages/data-controls/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index f4e1f9beda2a98..88fa9ae4faf264 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "3.27.0", + "version": "4.0.0", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 200d37efa904d9..0e72225f04c80e 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 10.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/data/package.json b/packages/data/package.json index b289f599fad018..3246870cbe52fb 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "9.28.0", + "version": "10.0.0", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index 1b772a002ff7be..8b3b3656258c9e 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Legacy support for `in` and `notIn` operators introduced in 0.8 .0 has been removed and they no longer work. Please, convert them to `is` and `isNot` respectively. diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 5fb67307954014..723441e0e8b81f 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dataviews", - "version": "1.2.0", + "version": "2.0.0", "description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dataviews/src/bulk-actions-toolbar.tsx b/packages/dataviews/src/bulk-actions-toolbar.tsx index 38e1f799491866..3cce31c5139047 100644 --- a/packages/dataviews/src/bulk-actions-toolbar.tsx +++ b/packages/dataviews/src/bulk-actions-toolbar.tsx @@ -250,9 +250,9 @@ export default function BulkActionsToolbar< Item extends AnyItem >( { diff --git a/packages/dataviews/src/pagination.tsx b/packages/dataviews/src/pagination.tsx index da37eb99313799..4020e78525b984 100644 --- a/packages/dataviews/src/pagination.tsx +++ b/packages/dataviews/src/pagination.tsx @@ -74,7 +74,7 @@ const Pagination = memo( function Pagination( { page: +newValue, } ); } } - size={ 'compact' } + size="compact" __nextHasNoMarginBottom /> ), diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index 66ba61147bc882..bfff3786b1112c 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -119,7 +119,8 @@ th:first-child { padding-left: $grid-unit-60; - .dataviews-view-table-header-button { + .dataviews-view-table-header-button, + .dataviews-view-table-header { margin-left: - #{$grid-unit-10}; } } @@ -235,6 +236,10 @@ } } + .dataviews-view-table-header { + padding-left: $grid-unit-05; + } + .dataviews-view-table__actions-column { width: 1%; } diff --git a/packages/dataviews/src/view-grid.tsx b/packages/dataviews/src/view-grid.tsx index 5c6b8ba5bc3b50..77ac3c92738523 100644 --- a/packages/dataviews/src/view-grid.tsx +++ b/packages/dataviews/src/view-grid.tsx @@ -131,7 +131,7 @@ function GridItem< Item extends AnyItem >( { return ( { renderedValue } diff --git a/packages/dataviews/src/view-table.tsx b/packages/dataviews/src/view-table.tsx index dcfb8a67bf3636..075a60fccd129c 100644 --- a/packages/dataviews/src/view-table.tsx +++ b/packages/dataviews/src/view-table.tsx @@ -15,7 +15,6 @@ import { privateApis as componentsPrivateApis, CheckboxControl, Spinner, - VisuallyHidden, } from '@wordpress/components'; import { forwardRef, @@ -549,9 +548,9 @@ function ViewTable< Item extends AnyItem >( { data-field-id="actions" className="dataviews-view-table__actions-column" > - + { __( 'Actions' ) } - + ) } diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md index 0bdf48871145b6..b66b2983b20be8 100644 --- a/packages/date/CHANGELOG.md +++ b/packages/date/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/date/package.json b/packages/date/package.json index a3790d910e3b2d..23c66ba25823d0 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "4.58.0", + "version": "5.0.0", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 27d099308df2ad..32c012ea3b6447 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index 3ee7f0bdb7deac..a551439c6caeb8 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "5.9.0", + "version": "6.0.0", "description": "Extract WordPress script dependencies from webpack bundles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/deprecated/CHANGELOG.md b/packages/deprecated/CHANGELOG.md index a38f511de523dc..99322e16983dc3 100644 --- a/packages/deprecated/CHANGELOG.md +++ b/packages/deprecated/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 656787a875e66b..4ef4b237980024 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "3.58.0", + "version": "4.0.0", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/docgen/CHANGELOG.md b/packages/docgen/CHANGELOG.md index 492b7035e959c5..daffb59b72f5e9 100644 --- a/packages/docgen/CHANGELOG.md +++ b/packages/docgen/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 0582121d16d74b..832b0fb6a2a2aa 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/docgen", - "version": "1.67.0", + "version": "2.0.0", "description": "Autogenerate public API documentation from exports and JSDoc comments.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom-ready/CHANGELOG.md b/packages/dom-ready/CHANGELOG.md index 67d4e465a65edc..b83d377c11db03 100644 --- a/packages/dom-ready/CHANGELOG.md +++ b/packages/dom-ready/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 54f4842c2d93e2..bc189f6e387b98 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "3.58.0", + "version": "4.0.0", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md index dc504c46551367..878205fae89e90 100644 --- a/packages/dom/CHANGELOG.md +++ b/packages/dom/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/dom/package.json b/packages/dom/package.json index 9b8c0b8ded4ae1..e059d757b2c5ca 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "3.58.0", + "version": "4.0.0", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils-playwright/CHANGELOG.md b/packages/e2e-test-utils-playwright/CHANGELOG.md index f327489308b648..4fe11b5ea95014 100644 --- a/packages/e2e-test-utils-playwright/CHANGELOG.md +++ b/packages/e2e-test-utils-playwright/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json index 5cee2f8ee1b30d..c630ac1869c7f9 100644 --- a/packages/e2e-test-utils-playwright/package.json +++ b/packages/e2e-test-utils-playwright/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils-playwright", - "version": "0.26.0", + "version": "1.0.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md index cb853af4cdb213..696b6e56b02dd1 100644 --- a/packages/e2e-test-utils/CHANGELOG.md +++ b/packages/e2e-test-utils/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 11.0.0 (2024-05-31) + ### Breaking Changes - Increase the minimum required Node.js version to v18.12.0 matching long-term support releases ([#31270](https://github.com/WordPress/gutenberg/pull/61930)). Learn more about [Node.js releases](https://nodejs.org/en/about/previous-releases). diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index 1faa1848272ab2..d791e6e37d775f 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "10.29.0", + "version": "11.0.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/CHANGELOG.md b/packages/e2e-tests/CHANGELOG.md index c377e0db8a79fc..7dcde1e1cfefef 100644 --- a/packages/e2e-tests/CHANGELOG.md +++ b/packages/e2e-tests/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index d678f568a92a5d..312a60d670c66a 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "7.29.0", + "version": "8.0.0", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/plugins/interactive-blocks.php b/packages/e2e-tests/plugins/interactive-blocks.php index ab2f85156e3b3e..54bd4325bb29b1 100644 --- a/packages/e2e-tests/plugins/interactive-blocks.php +++ b/packages/e2e-tests/plugins/interactive-blocks.php @@ -29,11 +29,7 @@ function () { // Ensure the interactivity API is loaded. wp_interactivity(); // But remove the server directive processing. - remove_filter( - 'render_block_data', - 'wp_interactivity_process_directives_of_interactive_blocks', - 100 - ); + add_filter( 'interactivity_process_directives', '__return_false' ); } } ); diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index 7e6b861f8183ba..3934ab8cda8848 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.0.0 (2024-05-31) + ### Breaking Changes - Variables like `process.env.IS_GUTENBERG_PLUGIN` have been replaced by `globalThis.IS_GUTENBERG_PLUGIN`. Build systems using `process.env` should be updated ([#61486](https://github.com/WordPress/gutenberg/pull/61486)). diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index a968caadb3191d..b3327218dfc873 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "7.35.0", + "version": "8.0.0", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/src/commands/use-commands.js b/packages/edit-post/src/commands/use-commands.js new file mode 100644 index 00000000000000..edd2a6a5b84042 --- /dev/null +++ b/packages/edit-post/src/commands/use-commands.js @@ -0,0 +1,48 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { fullscreen } from '@wordpress/icons'; +import { useCommand } from '@wordpress/commands'; +import { store as preferencesStore } from '@wordpress/preferences'; +import { store as noticesStore } from '@wordpress/notices'; + +export default function useCommands() { + const { isFullscreen } = useSelect( ( select ) => { + const { get } = select( preferencesStore ); + + return { + isFullscreen: get( 'core/edit-post', 'fullscreenMode' ), + }; + }, [] ); + const { toggle } = useDispatch( preferencesStore ); + const { createInfoNotice } = useDispatch( noticesStore ); + + useCommand( { + name: 'core/toggle-fullscreen-mode', + label: isFullscreen + ? __( 'Exit fullscreen' ) + : __( 'Enter fullscreen' ), + icon: fullscreen, + callback: ( { close } ) => { + toggle( 'core/edit-post', 'fullscreenMode' ); + close(); + createInfoNotice( + isFullscreen ? __( 'Fullscreen off.' ) : __( 'Fullscreen on.' ), + { + id: 'core/edit-post/toggle-fullscreen-mode/notice', + type: 'snackbar', + actions: [ + { + label: __( 'Undo' ), + onClick: () => { + toggle( 'core/edit-post', 'fullscreenMode' ); + }, + }, + ], + } + ); + }, + } ); +} diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js b/packages/edit-post/src/components/back-button/fullscreen-mode-close.js similarity index 97% rename from packages/edit-post/src/components/header/fullscreen-mode-close/index.js rename to packages/edit-post/src/components/back-button/fullscreen-mode-close.js index 839802f380e1f3..59694de16cb925 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js +++ b/packages/edit-post/src/components/back-button/fullscreen-mode-close.js @@ -22,7 +22,7 @@ import { useReducedMotion } from '@wordpress/compose'; /** * Internal dependencies */ -import { store as editPostStore } from '../../../store'; +import { store as editPostStore } from '../../store'; function FullscreenModeClose( { showTooltip, icon, href, initialPost } ) { const { isActive, isRequestingSiteIcon, postType, siteIconUrl } = useSelect( diff --git a/packages/edit-post/src/components/back-button/index.js b/packages/edit-post/src/components/back-button/index.js new file mode 100644 index 00000000000000..ecce4623cea493 --- /dev/null +++ b/packages/edit-post/src/components/back-button/index.js @@ -0,0 +1,34 @@ +/** + * WordPress dependencies + */ +import { privateApis as editorPrivateApis } from '@wordpress/editor'; +import { __unstableMotion as motion } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import FullscreenModeClose from './fullscreen-mode-close'; +import { unlock } from '../../lock-unlock'; + +const { BackButton: BackButtonFill } = unlock( editorPrivateApis ); + +const slideX = { + hidden: { x: '-100%' }, + distractionFreeInactive: { x: 0 }, + hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, +}; + +function BackButton( { initialPost } ) { + return ( + + + + + + ); +} + +export default BackButton; diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss b/packages/edit-post/src/components/back-button/style.scss similarity index 87% rename from packages/edit-post/src/components/header/fullscreen-mode-close/style.scss rename to packages/edit-post/src/components/back-button/style.scss index 911445344febcd..d6455c707fb1d9 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss +++ b/packages/edit-post/src/components/back-button/style.scss @@ -14,10 +14,9 @@ background: $gray-900; color: $white; border-radius: 0; - height: $header-height + $border-width; + height: $header-height; width: $header-height; position: relative; - margin-bottom: - $border-width; &:active { color: $white; @@ -65,3 +64,18 @@ margin-top: -($border-width); } } + +/** + * Show icon label overrides. + */ +.show-icon-labels .editor-header { + .edit-post-fullscreen-mode-close.has-icon { + width: $header-height; + svg { + display: block; + } + &::after { + content: none; + } + } +} diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/back-button/test/__snapshots__/fullscreen-mode-close.js.snap similarity index 100% rename from packages/edit-post/src/components/header/fullscreen-mode-close/test/__snapshots__/index.js.snap rename to packages/edit-post/src/components/back-button/test/__snapshots__/fullscreen-mode-close.js.snap diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/test/index.js b/packages/edit-post/src/components/back-button/test/fullscreen-mode-close.js similarity index 97% rename from packages/edit-post/src/components/header/fullscreen-mode-close/test/index.js rename to packages/edit-post/src/components/back-button/test/fullscreen-mode-close.js index b66d548045ad42..4a549370d0f056 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/test/index.js +++ b/packages/edit-post/src/components/back-button/test/fullscreen-mode-close.js @@ -11,7 +11,7 @@ import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import FullscreenModeClose from '../'; +import FullscreenModeClose from '../fullscreen-mode-close'; jest.mock( '@wordpress/data/src/components/use-select', () => { // This allows us to tweak the returned value on each test. diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js deleted file mode 100644 index 311279292d8f6a..00000000000000 --- a/packages/edit-post/src/components/header/index.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * WordPress dependencies - */ -import { privateApis as editorPrivateApis } from '@wordpress/editor'; -import { useSelect } from '@wordpress/data'; -import { __unstableMotion as motion } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import FullscreenModeClose from './fullscreen-mode-close'; -import PostEditorMoreMenu from './more-menu'; -import MainDashboardButton from './main-dashboard-button'; -import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -const { Header: EditorHeader } = unlock( editorPrivateApis ); - -const slideX = { - hidden: { x: '-100%' }, - distractionFreeInactive: { x: 0 }, - hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, -}; - -function Header( { setEntitiesSavedStatesCallback, initialPost } ) { - const { hasActiveMetaboxes } = useSelect( ( select ) => { - return { - hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - }; - }, [] ); - - return ( - - - - - - - - - ); -} - -export default Header; diff --git a/packages/edit-post/src/components/header/style.scss b/packages/edit-post/src/components/header/style.scss deleted file mode 100644 index 53672eb09e701f..00000000000000 --- a/packages/edit-post/src/components/header/style.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Show icon label overrides. - */ -.show-icon-labels .editor-header { - .edit-post-fullscreen-mode-close.has-icon { - width: $header-height; - svg { - display: block; - } - &::after { - content: none; - } - } -} diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index fde9319a348daf..6603b1f6851522 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -10,7 +10,6 @@ import { AutosaveMonitor, LocalAutosaveMonitor, UnsavedChangesWarning, - EditorNotices, EditorKeyboardShortcutsRegister, EditorSnackbars, store as editorStore, @@ -18,16 +17,13 @@ import { } from '@wordpress/editor'; import { useSelect, useDispatch } from '@wordpress/data'; import { - BlockBreadcrumb, - BlockToolbar, privateApis as blockEditorPrivateApis, store as blockEditorStore, } from '@wordpress/block-editor'; import { useViewportMatch } from '@wordpress/compose'; import { PluginArea } from '@wordpress/plugins'; -import { __, _x, sprintf } from '@wordpress/i18n'; -import { useState, useCallback, useMemo } from '@wordpress/element'; -import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { __, sprintf } from '@wordpress/i18n'; +import { useCallback, useMemo } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { store as preferencesStore } from '@wordpress/preferences'; import { privateApis as commandsPrivateApis } from '@wordpress/commands'; @@ -38,55 +34,54 @@ import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies */ -import VisualEditor from '../visual-editor'; +import BackButton from '../back-button'; import EditPostKeyboardShortcuts from '../keyboard-shortcuts'; import InitPatternModal from '../init-pattern-modal'; import BrowserURL from '../browser-url'; -import Header from '../header'; import MetaBoxes from '../meta-boxes'; +import PostEditorMoreMenu from '../more-menu'; import WelcomeGuide from '../welcome-guide'; import { store as editPostStore } from '../../store'; import { unlock } from '../../lock-unlock'; -import useCommonCommands from '../../hooks/commands/use-common-commands'; +import useEditPostCommands from '../../commands/use-commands'; +import { usePaddingAppender } from './use-padding-appender'; +import { useShouldIframe } from './use-should-iframe'; const { getLayoutStyles } = unlock( blockEditorPrivateApis ); const { useCommands } = unlock( coreCommandsPrivateApis ); const { useCommandContext } = unlock( commandsPrivateApis ); -const { - InserterSidebar, - ListViewSidebar, - ComplementaryArea, - FullscreenMode, - SavePublishPanels, - InterfaceSkeleton, - interfaceStore, - Sidebar, - TextEditor, -} = unlock( editorPrivateApis ); +const { EditorInterface, FullscreenMode, Sidebar } = + unlock( editorPrivateApis ); const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis ); - -const interfaceLabels = { - /* translators: accessibility text for the editor top bar landmark region. */ - header: __( 'Editor top bar' ), - /* translators: accessibility text for the editor content landmark region. */ - body: __( 'Editor content' ), - /* translators: accessibility text for the editor settings landmark region. */ - sidebar: __( 'Editor settings' ), - /* translators: accessibility text for the editor publish landmark region. */ - actions: __( 'Editor publish' ), - /* translators: accessibility text for the editor footer landmark region. */ - footer: __( 'Editor footer' ), -}; +const DESIGN_POST_TYPES = [ + 'wp_template', + 'wp_template_part', + 'wp_block', + 'wp_navigation', +]; function useEditorStyles() { - const { hasThemeStyleSupport, editorSettings } = useSelect( - ( select ) => ( { + const { + hasThemeStyleSupport, + editorSettings, + isZoomedOutView, + hasMetaBoxes, + renderingMode, + postType, + } = useSelect( ( select ) => { + const { __unstableGetEditorMode } = select( blockEditorStore ); + const { getCurrentPostType, getRenderingMode } = select( editorStore ); + const _postType = getCurrentPostType(); + return { hasThemeStyleSupport: select( editPostStore ).isFeatureActive( 'themeStyles' ), editorSettings: select( editorStore ).getEditorSettings(), - } ), - [] - ); + isZoomedOutView: __unstableGetEditorMode() === 'zoom-out', + hasMetaBoxes: select( editPostStore ).hasMetaBoxes(), + renderingMode: getRenderingMode(), + postType: _postType, + }; + }, [] ); // Compute the default styles. return useMemo( () => { @@ -120,7 +115,24 @@ function useEditorStyles() { } ); } - return hasThemeStyles ? editorSettings.styles : defaultEditorStyles; + const baseStyles = hasThemeStyles + ? editorSettings.styles ?? [] + : defaultEditorStyles; + + // Add a constant padding for the typewritter effect. When typing at the + // bottom, there needs to be room to scroll up. + if ( + ! isZoomedOutView && + ! hasMetaBoxes && + renderingMode === 'post-only' && + ! DESIGN_POST_TYPES.includes( postType ) + ) { + baseStyles.push( { + css: 'body{padding-bottom: 40vh}', + } ); + } + + return baseStyles; }, [ editorSettings.defaultEditorStyles, editorSettings.disableLayoutStyles, @@ -131,73 +143,45 @@ function useEditorStyles() { function Layout( { initialPost } ) { useCommands(); - useCommonCommands(); - - const isMobileViewport = useViewportMatch( 'medium', '<' ); + useEditPostCommands(); const isWideViewport = useViewportMatch( 'large' ); - const isLargeViewport = useViewportMatch( 'medium' ); - - const { closeGeneralSidebar } = useDispatch( editPostStore ); + const paddingAppenderRef = usePaddingAppender(); + const shouldIframe = useShouldIframe(); const { createErrorNotice } = useDispatch( noticesStore ); const { mode, isFullscreenActive, - isRichEditingEnabled, sidebarIsOpened, hasActiveMetaboxes, - previousShortcut, - nextShortcut, hasBlockSelected, - isInserterOpened, - isListViewOpened, showIconLabels, isDistractionFree, showBlockBreadcrumbs, showMetaBoxes, - documentLabel, hasHistory, - hasBlockBreadcrumbs, - blockEditorMode, isEditingTemplate, + isWelcomeGuideVisible, } = useSelect( ( select ) => { const { get } = select( preferencesStore ); - const { getEditorSettings, getPostTypeLabel } = select( editorStore ); - const editorSettings = getEditorSettings(); - const postTypeLabel = getPostTypeLabel(); + const { getEditorSettings } = select( editorStore ); + const { isFeatureActive } = select( editPostStore ); return { - showMetaBoxes: - select( editorStore ).getRenderingMode() === 'post-only', - sidebarIsOpened: !! ( - select( interfaceStore ).getActiveComplementaryArea( 'core' ) || - select( editorStore ).isPublishSidebarOpened() - ), + mode: select( editorStore ).getEditorMode(), isFullscreenActive: select( editPostStore ).isFeatureActive( 'fullscreenMode' ), - isInserterOpened: select( editorStore ).isInserterOpened(), - isListViewOpened: select( editorStore ).isListViewOpened(), - mode: select( editorStore ).getEditorMode(), - isRichEditingEnabled: editorSettings.richEditingEnabled, hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - previousShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( 'core/editor/previous-region' ), - nextShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( 'core/editor/next-region' ), + hasBlockSelected: + !! select( blockEditorStore ).getBlockSelectionStart(), showIconLabels: get( 'core', 'showIconLabels' ), isDistractionFree: get( 'core', 'distractionFree' ), showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), - // translators: Default label for the Document in the Block Breadcrumb. - documentLabel: postTypeLabel || _x( 'Document', 'noun' ), - hasBlockSelected: - !! select( blockEditorStore ).getBlockSelectionStart(), + showMetaBoxes: + select( editorStore ).getRenderingMode() === 'post-only', hasHistory: !! getEditorSettings().onNavigateToPreviousEntityRecord, - hasBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), - blockEditorMode: - select( blockEditorStore ).__unstableGetEditorMode(), isEditingTemplate: select( editorStore ).getCurrentPostType() === 'wp_template', + isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ), }; }, [] ); @@ -209,21 +193,6 @@ function Layout( { initialPost } ) { const styles = useEditorStyles(); - // Local state for save panel. - // Note 'truthy' callback implies an open panel. - const [ entitiesSavedStatesCallback, setEntitiesSavedStatesCallback ] = - useState( false ); - - const closeEntitiesSavedStates = useCallback( - ( arg ) => { - if ( typeof entitiesSavedStatesCallback === 'function' ) { - entitiesSavedStatesCallback( arg ); - } - setEntitiesSavedStatesCallback( false ); - }, - [ entitiesSavedStatesCallback ] - ); - // We need to add the show-icon-labels class to the body element so it is applied to modals. if ( showIconLabels ) { document.body.classList.add( 'show-icon-labels' ); @@ -235,31 +204,10 @@ function Layout( { initialPost } ) { 'is-sidebar-opened': sidebarIsOpened, 'has-metaboxes': hasActiveMetaboxes, 'is-distraction-free': isDistractionFree && isWideViewport, - 'is-entity-save-view-open': !! entitiesSavedStatesCallback, 'has-block-breadcrumbs': - hasBlockBreadcrumbs && ! isDistractionFree && isWideViewport, + showBlockBreadcrumbs && ! isDistractionFree && isWideViewport, } ); - const secondarySidebarLabel = isListViewOpened - ? __( 'Document Overview' ) - : __( 'Block Library' ); - - const secondarySidebar = () => { - if ( mode === 'visual' && isInserterOpened ) { - return ( - - ); - } - if ( mode === 'visual' && isListViewOpened ) { - return ; - } - - return null; - }; - function onPluginAreaError( name ) { createErrorNotice( sprintf( @@ -334,79 +282,6 @@ function Layout( { initialPost } ) { - - - } - editorNotices={ } - secondarySidebar={ secondarySidebar() } - sidebar={ - ! isDistractionFree && ( - - ) - } - notices={ } - content={ - <> - { ! isDistractionFree && } - { ( mode === 'text' || ! isRichEditingEnabled ) && ( - - ) } - { ! isLargeViewport && mode === 'visual' && ( - - ) } - { isRichEditingEnabled && mode === 'visual' && ( - - ) } - { ! isDistractionFree && showMetaBoxes && ( - - - - - ) } - > - } - footer={ - ! isDistractionFree && - ! isMobileViewport && - showBlockBreadcrumbs && - isRichEditingEnabled && - blockEditorMode !== 'zoom-out' && - mode === 'visual' && ( - - - - ) - } - actions={ - - } - shortcuts={ { - previous: previousShortcut, - next: nextShortcut, - } } - /> @@ -418,6 +293,26 @@ function Layout( { initialPost } ) { } /> ) } + + + + + { ! isDistractionFree && showMetaBoxes && ( + + + + + ) } + > ); } diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index bb0ce2e38732ea..2c5a035cad1447 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -1,4 +1,4 @@ -.edit-post-visual-editor { +.editor-visual-editor { flex: 1 0 auto; height: auto; } @@ -9,25 +9,12 @@ } // Adjust the position of the notices -.edit-post-layout .components-editor-notices__snackbar { +.components-editor-notices__snackbar { position: fixed; right: 0; - bottom: 16px; - padding-left: 16px; - padding-right: 16px; -} - -.is-distraction-free { - .components-editor-notices__snackbar { - bottom: 16px; - } -} - -// Adjust the position of the notices when breadcrumbs are present -.has-block-breadcrumbs { - .components-editor-notices__snackbar { - bottom: 40px; - } + bottom: 24px; + padding-left: 24px; + padding-right: 24px; } @include editor-left(".edit-post-layout .components-editor-notices__snackbar"); diff --git a/packages/edit-post/src/components/visual-editor/use-padding-appender.js b/packages/edit-post/src/components/layout/use-padding-appender.js similarity index 100% rename from packages/edit-post/src/components/visual-editor/use-padding-appender.js rename to packages/edit-post/src/components/layout/use-padding-appender.js diff --git a/packages/edit-post/src/components/layout/use-should-iframe.js b/packages/edit-post/src/components/layout/use-should-iframe.js new file mode 100644 index 00000000000000..03efae92c72f7d --- /dev/null +++ b/packages/edit-post/src/components/layout/use-should-iframe.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { store as editorStore } from '@wordpress/editor'; +import { useSelect } from '@wordpress/data'; +import { store as blocksStore } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + +const isGutenbergPlugin = globalThis.IS_GUTENBERG_PLUGIN ? true : false; + +export function useShouldIframe() { + const { + isBlockBasedTheme, + hasV3BlocksOnly, + isEditingTemplate, + hasMetaBoxes, + } = useSelect( ( select ) => { + const { getEditorSettings, getCurrentPostType } = select( editorStore ); + const { getBlockTypes } = select( blocksStore ); + const editorSettings = getEditorSettings(); + return { + isBlockBasedTheme: editorSettings.__unstableIsBlockBasedTheme, + hasV3BlocksOnly: getBlockTypes().every( ( type ) => { + return type.apiVersion >= 3; + } ), + isEditingTemplate: getCurrentPostType() === 'wp_template', + hasMetaBoxes: select( editPostStore ).hasMetaBoxes(), + }; + }, [] ); + + return ( + ( ( hasV3BlocksOnly || ( isGutenbergPlugin && isBlockBasedTheme ) ) && + ! hasMetaBoxes ) || + isEditingTemplate + ); +} diff --git a/packages/edit-post/src/components/header/more-menu/index.js b/packages/edit-post/src/components/more-menu/index.js similarity index 92% rename from packages/edit-post/src/components/header/more-menu/index.js rename to packages/edit-post/src/components/more-menu/index.js index c9f1c50251ca21..136742a19dddd4 100644 --- a/packages/edit-post/src/components/header/more-menu/index.js +++ b/packages/edit-post/src/components/more-menu/index.js @@ -10,10 +10,10 @@ import { displayShortcut } from '@wordpress/keycodes'; /** * Internal dependencies */ -import { unlock } from '../../../lock-unlock'; +import { unlock } from '../../lock-unlock'; import ManagePatternsMenuItem from './manage-patterns-menu-item'; import WelcomeGuideMenuItem from './welcome-guide-menu-item'; -import EditPostPreferencesModal from '../../preferences-modal'; +import EditPostPreferencesModal from '../preferences-modal'; const { ToolsMoreMenuGroup, ViewMoreMenuGroup } = unlock( editorPrivateApis ); diff --git a/packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js b/packages/edit-post/src/components/more-menu/manage-patterns-menu-item.js similarity index 100% rename from packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js rename to packages/edit-post/src/components/more-menu/manage-patterns-menu-item.js diff --git a/packages/edit-post/src/components/header/more-menu/welcome-guide-menu-item.js b/packages/edit-post/src/components/more-menu/welcome-guide-menu-item.js similarity index 100% rename from packages/edit-post/src/components/header/more-menu/welcome-guide-menu-item.js rename to packages/edit-post/src/components/more-menu/welcome-guide-menu-item.js diff --git a/packages/edit-post/src/components/visual-editor/block-inspector-button.js b/packages/edit-post/src/components/visual-editor/block-inspector-button.js deleted file mode 100644 index 87b55d4eb3fdca..00000000000000 --- a/packages/edit-post/src/components/visual-editor/block-inspector-button.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { MenuItem } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { speak } from '@wordpress/a11y'; -import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; - -/** - * Internal dependencies - */ -import { store as editPostStore } from '../../store'; - -const noop = () => {}; - -export function BlockInspectorButton( { onClick = noop, small = false } ) { - const { shortcut, areAdvancedSettingsOpened } = useSelect( - ( select ) => ( { - shortcut: select( - keyboardShortcutsStore - ).getShortcutRepresentation( 'core/editor/toggle-sidebar' ), - areAdvancedSettingsOpened: - select( editPostStore ).getActiveGeneralSidebarName() === - 'edit-post/block', - } ), - [] - ); - const { openGeneralSidebar, closeGeneralSidebar } = - useDispatch( editPostStore ); - - const label = areAdvancedSettingsOpened - ? __( 'Hide more settings' ) - : __( 'Show more settings' ); - - return ( - { - if ( areAdvancedSettingsOpened ) { - closeGeneralSidebar(); - speak( __( 'Block settings closed' ) ); - } else { - openGeneralSidebar( 'edit-post/block' ); - speak( - __( - 'Additional settings are now available in the Editor block settings sidebar' - ) - ); - } - onClick(); - } } - shortcut={ shortcut } - > - { ! small && label } - - ); -} - -export default BlockInspectorButton; diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js deleted file mode 100644 index 24a50cb7ba69a3..00000000000000 --- a/packages/edit-post/src/components/visual-editor/index.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * WordPress dependencies - */ -import { - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; -import { useMemo } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; -import { store as blocksStore } from '@wordpress/blocks'; -import { store as blockEditorStore } from '@wordpress/block-editor'; - -/** - * Internal dependencies - */ -import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; -import { usePaddingAppender } from './use-padding-appender'; - -const { VisualEditor: VisualEditorRoot } = unlock( editorPrivateApis ); - -const isGutenbergPlugin = globalThis.IS_GUTENBERG_PLUGIN ? true : false; -const DESIGN_POST_TYPES = [ - 'wp_template', - 'wp_template_part', - 'wp_block', - 'wp_navigation', -]; - -export default function VisualEditor( { styles } ) { - const { - isWelcomeGuideVisible, - renderingMode, - isBlockBasedTheme, - hasV3BlocksOnly, - isEditingTemplate, - isZoomedOutView, - postType, - } = useSelect( ( select ) => { - const { isFeatureActive } = select( editPostStore ); - const { getEditorSettings, getRenderingMode, getCurrentPostType } = - select( editorStore ); - const { getBlockTypes } = select( blocksStore ); - const { __unstableGetEditorMode } = select( blockEditorStore ); - const editorSettings = getEditorSettings(); - const _postType = getCurrentPostType(); - return { - isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ), - renderingMode: getRenderingMode(), - isBlockBasedTheme: editorSettings.__unstableIsBlockBasedTheme, - hasV3BlocksOnly: getBlockTypes().every( ( type ) => { - return type.apiVersion >= 3; - } ), - isEditingTemplate: _postType === 'wp_template', - isZoomedOutView: __unstableGetEditorMode() === 'zoom-out', - postType: _postType, - }; - }, [] ); - const hasMetaBoxes = useSelect( - ( select ) => select( editPostStore ).hasMetaBoxes(), - [] - ); - - const paddingAppenderRef = usePaddingAppender(); - - let paddingBottom; - - // Add a constant padding for the typewritter effect. When typing at the - // bottom, there needs to be room to scroll up. - if ( - ! isZoomedOutView && - ! hasMetaBoxes && - renderingMode === 'post-only' && - ! DESIGN_POST_TYPES.includes( postType ) - ) { - paddingBottom = '40vh'; - } - - styles = useMemo( - () => [ - ...styles, - { - // We should move this in to future to the body. - css: paddingBottom - ? `body{padding-bottom:${ paddingBottom }}` - : '', - }, - ], - [ styles, paddingBottom ] - ); - - const isToBeIframed = - ( ( hasV3BlocksOnly || ( isGutenbergPlugin && isBlockBasedTheme ) ) && - ! hasMetaBoxes ) || - isEditingTemplate; - - return ( - - ); -} diff --git a/packages/edit-post/src/hooks/commands/use-common-commands.js b/packages/edit-post/src/hooks/commands/use-common-commands.js deleted file mode 100644 index bd2036eb85e748..00000000000000 --- a/packages/edit-post/src/hooks/commands/use-common-commands.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect, useDispatch } from '@wordpress/data'; -import { __, isRTL } from '@wordpress/i18n'; -import { - drawerLeft, - drawerRight, - blockDefault, - fullscreen, - formatListBullets, -} from '@wordpress/icons'; -import { useCommand } from '@wordpress/commands'; -import { store as preferencesStore } from '@wordpress/preferences'; -import { - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; -import { store as noticesStore } from '@wordpress/notices'; - -/** - * Internal dependencies - */ -import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -const { interfaceStore } = unlock( editorPrivateApis ); - -export default function useCommonCommands() { - const { openGeneralSidebar, closeGeneralSidebar } = - useDispatch( editPostStore ); - const { activeSidebar, isFullscreen, isPublishSidebarEnabled } = useSelect( - ( select ) => { - const { get } = select( preferencesStore ); - - return { - activeSidebar: - select( interfaceStore ).getActiveComplementaryArea( - 'core' - ), - isPublishSidebarEnabled: - select( editorStore ).isPublishSidebarEnabled(), - isFullscreen: get( 'core/edit-post', 'fullscreenMode' ), - }; - }, - [] - ); - const { toggle } = useDispatch( preferencesStore ); - const { createInfoNotice } = useDispatch( noticesStore ); - - useCommand( { - name: 'core/open-settings-sidebar', - label: __( 'Toggle settings sidebar' ), - icon: isRTL() ? drawerLeft : drawerRight, - callback: ( { close } ) => { - close(); - if ( activeSidebar === 'edit-post/document' ) { - closeGeneralSidebar(); - } else { - openGeneralSidebar( 'edit-post/document' ); - } - }, - } ); - - useCommand( { - name: 'core/open-block-inspector', - label: __( 'Toggle block inspector' ), - icon: blockDefault, - callback: ( { close } ) => { - close(); - if ( activeSidebar === 'edit-post/block' ) { - closeGeneralSidebar(); - } else { - openGeneralSidebar( 'edit-post/block' ); - } - }, - } ); - - useCommand( { - name: 'core/toggle-fullscreen-mode', - label: isFullscreen - ? __( 'Exit fullscreen' ) - : __( 'Enter fullscreen' ), - icon: fullscreen, - callback: ( { close } ) => { - toggle( 'core/edit-post', 'fullscreenMode' ); - close(); - createInfoNotice( - isFullscreen ? __( 'Fullscreen off.' ) : __( 'Fullscreen on.' ), - { - id: 'core/edit-post/toggle-fullscreen-mode/notice', - type: 'snackbar', - actions: [ - { - label: __( 'Undo' ), - onClick: () => { - toggle( 'core/edit-post', 'fullscreenMode' ); - }, - }, - ], - } - ); - }, - } ); - - useCommand( { - name: 'core/toggle-publish-sidebar', - label: isPublishSidebarEnabled - ? __( 'Disable pre-publish checks' ) - : __( 'Enable pre-publish checks' ), - icon: formatListBullets, - callback: ( { close } ) => { - close(); - toggle( 'core', 'isPublishSidebarEnabled' ); - createInfoNotice( - isPublishSidebarEnabled - ? __( 'Pre-publish checks disabled.' ) - : __( 'Pre-publish checks enabled.' ), - { - id: 'core/editor/publish-sidebar/notice', - type: 'snackbar', - } - ); - }, - } ); -} diff --git a/packages/edit-post/src/hooks/index.js b/packages/edit-post/src/hooks/index.js deleted file mode 100644 index 0397f1fee936c3..00000000000000 --- a/packages/edit-post/src/hooks/index.js +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Internal dependencies - */ -import './validate-multiple-use'; diff --git a/packages/edit-post/src/hooks/validate-multiple-use/index.js b/packages/edit-post/src/hooks/validate-multiple-use/index.js deleted file mode 100644 index bf771f45220362..00000000000000 --- a/packages/edit-post/src/hooks/validate-multiple-use/index.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * WordPress dependencies - */ -import { - createBlock, - findTransform, - getBlockTransforms, - getBlockType, - hasBlockSupport, -} from '@wordpress/blocks'; -import { Button } from '@wordpress/components'; -import { withSelect, withDispatch } from '@wordpress/data'; -import { Warning, store as blockEditorStore } from '@wordpress/block-editor'; -import { addFilter } from '@wordpress/hooks'; -import { __ } from '@wordpress/i18n'; -import { compose, createHigherOrderComponent } from '@wordpress/compose'; - -/** - * Recursively find very first block of an specific block type. - * - * @param {Object[]} blocks List of blocks. - * @param {string} name Block name to search. - * - * @return {Object|undefined} Return block object or undefined. - */ -function findFirstOfSameType( blocks, name ) { - if ( ! Array.isArray( blocks ) || ! blocks.length ) { - return; - } - - for ( const block of blocks ) { - if ( block.name === name ) { - return block; - } - - // Search inside innerBlocks. - const firstBlock = findFirstOfSameType( block.innerBlocks, name ); - - if ( firstBlock ) { - return firstBlock; - } - } -} - -const enhance = compose( - /** - * For blocks whose block type doesn't support `multiple`, provides the - * wrapped component with `originalBlockClientId` -- a reference to the - * first block of the same type in the content -- if and only if that - * "original" block is not the current one. Thus, an inexisting - * `originalBlockClientId` prop signals that the block is valid. - * - * @param {Component} WrappedBlockEdit A filtered BlockEdit instance. - * - * @return {Component} Enhanced component with merged state data props. - */ - withSelect( ( select, block ) => { - const multiple = hasBlockSupport( block.name, 'multiple', true ); - - // For block types with `multiple` support, there is no "original - // block" to be found in the content, as the block itself is valid. - if ( multiple ) { - return {}; - } - - // Otherwise, only pass `originalBlockClientId` if it refers to a different - // block from the current one. - const blocks = select( blockEditorStore ).getBlocks(); - const firstOfSameType = findFirstOfSameType( blocks, block.name ); - const isInvalid = - firstOfSameType && firstOfSameType.clientId !== block.clientId; - return { - originalBlockClientId: isInvalid && firstOfSameType.clientId, - }; - } ), - withDispatch( ( dispatch, { originalBlockClientId } ) => ( { - selectFirst: () => - dispatch( blockEditorStore ).selectBlock( originalBlockClientId ), - } ) ) -); - -const withMultipleValidation = createHigherOrderComponent( ( BlockEdit ) => { - return enhance( ( { originalBlockClientId, selectFirst, ...props } ) => { - if ( ! originalBlockClientId ) { - return ; - } - - const blockType = getBlockType( props.name ); - const outboundType = getOutboundType( props.name ); - - return [ - - - , - - { __( 'Find original' ) } -
Lorem ipsum dolor sit amet, consectetur adipiscingelit.
## Mixed Lists
+ { __( + 'Pages with the same order value will sorted alphabetically. Negative order values are also supported.' + ) } +
+ { __( + 'They also show up as sub-items in the default navigation menu. ' + ) } + + { __( 'Learn more' ) } + +
diff --git a/packages/editor/src/components/post-format/panel.js b/packages/editor/src/components/post-format/panel.js index cbd065183eefab..faaf88b785a4b2 100644 --- a/packages/editor/src/components/post-format/panel.js +++ b/packages/editor/src/components/post-format/panel.js @@ -1,20 +1,85 @@ /** * WordPress dependencies */ -import { PanelRow } from '@wordpress/components'; +import { Button, Dropdown } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; /** * Internal dependencies */ -import PostFormatForm from './'; +import { default as PostFormatForm, POST_FORMATS } from './'; import PostFormatCheck from './check'; +import PostPanelRow from '../post-panel-row'; +import { store as editorStore } from '../../store'; -export function PostFormat() { +/** + * Renders the Post Author Panel component. + * + * @return {Component} The component to be rendered. + */ +function PostFormat() { + const { postFormat } = useSelect( ( select ) => { + const { getEditedPostAttribute } = select( editorStore ); + const _postFormat = getEditedPostAttribute( 'format' ); + return { + postFormat: _postFormat ?? 'standard', + }; + }, [] ); + const activeFormat = POST_FORMATS.find( + ( format ) => format.id === postFormat + ); + + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); return ( - - - + + ( + + { activeFormat?.caption } + + ) } + renderContent={ ( { onClose } ) => ( + + + + + ) } + /> + ); } diff --git a/packages/editor/src/components/post-format/style.scss b/packages/editor/src/components/post-format/style.scss index 135ee7f3579029..ec3a8b1626b543 100644 --- a/packages/editor/src/components/post-format/style.scss +++ b/packages/editor/src/components/post-format/style.scss @@ -2,8 +2,14 @@ margin: $grid-unit-05 0 0 0; } -.editor-post-format__panel { - display: flex; - flex-direction: column; - align-items: stretch; +.editor-post-format__dialog .editor-post-format__dialog-content { + // sidebar width - popover padding - form margin + min-width: $sidebar-width - $grid-unit-20 - $grid-unit-20; + margin: $grid-unit-10; +} + +.editor-post-format__options { + .components-base-control__field > .components-v-stack { + gap: $grid-unit-15; + } } diff --git a/packages/editor/src/components/post-panel-row/style.scss b/packages/editor/src/components/post-panel-row/style.scss index 22d0cbbb644d8a..baa7d7dd98977d 100644 --- a/packages/editor/src/components/post-panel-row/style.scss +++ b/packages/editor/src/components/post-panel-row/style.scss @@ -11,6 +11,9 @@ min-height: $grid-unit-40; display: flex; align-items: center; + padding: 6px 0; // Matches button to ensure alignment + line-height: $grid-unit-05 * 5; + hyphens: auto; } .editor-post-panel__row-control { @@ -18,4 +21,16 @@ min-height: $grid-unit-40; display: flex; align-items: center; + + .components-button { + max-width: 100%; + text-align: left; + text-wrap: pretty; + height: auto; + min-height: $button-size-compact; + } + + .components-dropdown { + max-width: 100%; + } } diff --git a/packages/editor/src/components/post-pending-status/check.js b/packages/editor/src/components/post-pending-status/check.js index 347cb6d7272276..7a4ff5195041c6 100644 --- a/packages/editor/src/components/post-pending-status/check.js +++ b/packages/editor/src/components/post-pending-status/check.js @@ -8,6 +8,16 @@ import { useSelect } from '@wordpress/data'; */ import { store as editorStore } from '../../store'; +/** + * This component checks the publishing status of the current post. + * If the post is already published or the user doesn't have the + * capability to publish, it returns null. + * + * @param {Object} props Component properties. + * @param {Element} props.children Children to be rendered. + * + * @return {JSX.Element|null} The rendered child elements or null if the post is already published or the user doesn't have the capability to publish. + */ export function PostPendingStatusCheck( { children } ) { const { hasPublishAction, isPublished } = useSelect( ( select ) => { const { isCurrentPostPublished, getCurrentPost } = diff --git a/packages/editor/src/components/post-pending-status/index.js b/packages/editor/src/components/post-pending-status/index.js index 85557517da6a11..8363ebc715891b 100644 --- a/packages/editor/src/components/post-pending-status/index.js +++ b/packages/editor/src/components/post-pending-status/index.js @@ -11,6 +11,11 @@ import { useDispatch, useSelect } from '@wordpress/data'; import PostPendingStatusCheck from './check'; import { store as editorStore } from '../../store'; +/** + * A component for displaying and toggling the pending status of a post. + * + * @return {JSX.Element} The rendered component. + */ export function PostPendingStatus() { const status = useSelect( ( select ) => select( editorStore ).getEditedPostAttribute( 'status' ), diff --git a/packages/editor/src/components/post-pingbacks/index.js b/packages/editor/src/components/post-pingbacks/index.js index 832088b86364a4..2ec9a4a78c54d6 100644 --- a/packages/editor/src/components/post-pingbacks/index.js +++ b/packages/editor/src/components/post-pingbacks/index.js @@ -42,4 +42,10 @@ function PostPingbacks() { ); } +/** + * Renders a control for enabling or disabling pingbacks and trackbacks + * in a WordPress post. + * + * @module PostPingbacks + */ export default PostPingbacks; diff --git a/packages/editor/src/components/post-preview-button/index.js b/packages/editor/src/components/post-preview-button/index.js index 578cd78351d23a..110de5858af382 100644 --- a/packages/editor/src/components/post-preview-button/index.js +++ b/packages/editor/src/components/post-preview-button/index.js @@ -99,6 +99,21 @@ function writeInterstitialMessage( targetDocument ) { targetDocument.close(); } +/** + * Renders a button that opens a new window or tab for the preview, + * writes the interstitial message to this window, and then navigates + * to the actual preview link. The button is not rendered if the post + * is not viewable and disabled if the post is not saveable. + * + * @param {Object} props The component props. + * @param {string} props.className The class name for the button. + * @param {string} props.textContent The text content for the button. + * @param {boolean} props.forceIsAutosaveable Whether to force autosave. + * @param {string} props.role The role attribute for the button. + * @param {Function} props.onPreview The callback function for preview event. + * + * @return {JSX.Element|null} The rendered button component. + */ export default function PostPreviewButton( { className, textContent, diff --git a/packages/editor/src/components/post-publish-button/index.js b/packages/editor/src/components/post-publish-button/index.js index 355986fdf509dd..8797c9d1428251 100644 --- a/packages/editor/src/components/post-publish-button/index.js +++ b/packages/editor/src/components/post-publish-button/index.js @@ -11,6 +11,7 @@ import { compose } from '@wordpress/compose'; */ import PublishButtonLabel from './label'; import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; const noop = () => {}; @@ -45,14 +46,24 @@ export class PostPublishButton extends Component { createOnClick( callback ) { return ( ...args ) => { - const { hasNonPostEntityChanges, setEntitiesSavedStatesCallback } = - this.props; + const { + hasNonPostEntityChanges, + hasPostMetaChanges, + setEntitiesSavedStatesCallback, + isPublished, + } = this.props; // If a post with non-post entities is published, but the user // elects to not save changes to the non-post entities, those // entities will still be dirty when the Publish button is clicked. // We also need to check that the `setEntitiesSavedStatesCallback` // prop was passed. See https://github.com/WordPress/gutenberg/pull/37383 - if ( hasNonPostEntityChanges && setEntitiesSavedStatesCallback ) { + // + // TODO: Explore how to manage `hasPostMetaChanges` and pre-publish workflow properly. + if ( + ( hasNonPostEntityChanges || + ( hasPostMetaChanges && isPublished ) ) && + setEntitiesSavedStatesCallback + ) { // The modal for multiple entity saving will open, // hold the callback for saving/publishing the post // so that we can call it if the post entity is checked. @@ -212,7 +223,8 @@ export default compose( [ isSavingNonPostEntityChanges, getEditedPostAttribute, getPostEdits, - } = select( editorStore ); + hasPostMetaChanges, + } = unlock( select( editorStore ) ); return { isSaving: isSavingPost(), isAutoSaving: isAutosavingPost(), @@ -229,6 +241,7 @@ export default compose( [ postStatus: getEditedPostAttribute( 'status' ), postStatusHasChanged: getPostEdits()?.status, hasNonPostEntityChanges: hasNonPostEntityChanges(), + hasPostMetaChanges: hasPostMetaChanges(), isSavingNonPostEntityChanges: isSavingNonPostEntityChanges(), }; } ), diff --git a/packages/editor/src/components/post-publish-panel/index.js b/packages/editor/src/components/post-publish-panel/index.js index d9c158ab429a9f..4d59133966759a 100644 --- a/packages/editor/src/components/post-publish-panel/index.js +++ b/packages/editor/src/components/post-publish-panel/index.js @@ -96,6 +96,7 @@ export class PostPublishPanel extends Component { disabled={ isSavingNonPostEntityChanges } onClick={ onClose } variant="secondary" + size="compact" > { __( 'Cancel' ) } diff --git a/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js b/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js index adf02af3134558..ce4ac1713d6389 100644 --- a/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js +++ b/packages/editor/src/components/post-publish-panel/maybe-tags-panel.js @@ -28,7 +28,7 @@ const TagsPanel = () => { 'Tags help users and search engines navigate your site and find your content. Add a few keywords to describe your post.' ) }
- { __( - 'Choose a predefined pattern to switch up the look of your template.' // TODO - make this dynamic? - ) } -
a
b
Editable
Inner paragraph
Paragraph