diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index ce830c04f651d..0f813267b586b 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -72,7 +72,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -168,13 +168,13 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: ref: ${{ needs.bump-version.outputs.release_branch || github.ref }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Use desired version of Node.js - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version-file: '.nvmrc' check-latest: true @@ -225,7 +225,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 2 ref: ${{ needs.bump-version.outputs.release_branch }} @@ -314,14 +314,14 @@ jobs: if: ${{ endsWith( needs.bump-version.outputs.new_version, '-rc.1' ) }} steps: - name: Checkout (for CLI) - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: path: main ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Checkout (for publishing) - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: path: publish # Later, we switch this branch in the script that publishes packages. @@ -336,7 +336,7 @@ jobs: git config user.email gutenberg@wordpress.org - name: Setup Node.js - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version-file: 'main/.nvmrc' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 499a2c020255c..e24b30eea7ba7 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -37,13 +37,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 1 show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Use desired version of Node.js - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version-file: '.nvmrc' check-latest: true diff --git a/.github/workflows/check-backport-changelog.yml b/.github/workflows/check-backport-changelog.yml index 366bad9fdbc24..cf07b1a3936b9 100644 --- a/.github/workflows/check-backport-changelog.yml +++ b/.github/workflows/check-backport-changelog.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest if: ${{ !contains(github.event.pull_request.labels.*.name, 'No Core Sync Required') && !contains(github.event.pull_request.labels.*.name, 'Backport from WordPress Core') }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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 1f6863b4a486e..40fbfe22bea56 100644 --- a/.github/workflows/check-components-changelog.yml +++ b/.github/workflows/check-components-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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.github/workflows/cherry-pick-wp-release.yml b/.github/workflows/cherry-pick-wp-release.yml index b43b0cc267314..11688a7cfba98 100644 --- a/.github/workflows/cherry-pick-wp-release.yml +++ b/.github/workflows/cherry-pick-wp-release.yml @@ -70,7 +70,7 @@ jobs: - name: Checkout repository if: env.cherry_pick == 'true' - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: token: ${{ secrets.GUTENBERG_TOKEN }} fetch-depth: 0 diff --git a/.github/workflows/create-block.yml b/.github/workflows/create-block.yml index 0de1b9ee6566a..d20b3e353c31e 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index 99166989cd68c..bbf033222a4b3 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -27,7 +27,7 @@ jobs: totalParts: [8] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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 8cc11b9bd913b..4715e1e09c2b8 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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 98615b93b8a17..9c4bee3af473c 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml index a24e501247402..66f8130ece2f0 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: path: cli ref: trunk @@ -39,7 +39,7 @@ jobs: - name: Checkout (for publishing) if: ${{ github.event.inputs.release_type != 'wp' }} - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: path: publish ref: wp/${{ github.event.inputs.wp_version }} @@ -67,7 +67,7 @@ jobs: - name: Setup Node.js if: ${{ github.event.inputs.release_type != 'wp' }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version-file: 'cli/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -75,7 +75,7 @@ jobs: - name: Setup Node.js (for WP major version) if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version-file: 'publish/.nvmrc' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml index 996bb1667ada5..2006eafd81cc7 100644 --- a/.github/workflows/pull-request-automation.yml +++ b/.github/workflows/pull-request-automation.yml @@ -12,13 +12,13 @@ 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Use desired version of Node.js - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version-file: '.nvmrc' check-latest: true diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index 2c3998c295280..917ee6144087e 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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 2926e494b09f8..cf065ad1cdf7d 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 + - uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1.194.0 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 1efd248bf7f30..1af2bb0ec7927 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -22,12 +22,12 @@ jobs: if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Use desired version of Node.js - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version-file: '.nvmrc' check-latest: true diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml index 65dd46b3a7610..83f7fdb96f926 100644 --- a/.github/workflows/storybook-pages.yml +++ b/.github/workflows/storybook-pages.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/sync-backport-changelog.yml b/.github/workflows/sync-backport-changelog.yml index bbc5663cf715b..b71d9440c38a1 100644 --- a/.github/workflows/sync-backport-changelog.yml +++ b/.github/workflows/sync-backport-changelog.yml @@ -20,7 +20,7 @@ jobs: ) steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 2 # Fetch the last two commits to compare changes - name: Check for changes in backport-changelog diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index b80804b824e3c..bfa35492589a4 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -70,7 +70,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -155,6 +155,7 @@ jobs: - '8.0' - '8.1' - '8.2' + - '8.3' multisite: [false, true] wordpress: [''] # Latest WordPress version. include: @@ -163,7 +164,7 @@ jobs: wordpress: 'previous major version' - php: '7.4' wordpress: 'previous major version' - - php: '8.2' + - php: '8.3' wordpress: 'previous major version' env: @@ -172,7 +173,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -197,12 +198,6 @@ jobs: - name: Override PHP version in composer.json run: composer config platform.php ${{ matrix.php }} - # The spatie/phpunit-watcher package is not compatible with PHP < 7.2. - # It must be removed before running the tests. - - name: Remove incompatible Composer packages - if: ${{ matrix.php < '7.2' }} - run: composer remove spatie/phpunit-watcher --dev --no-update - # Since Composer dependencies are installed using `composer update` and no lock file is in version control, # passing a custom cache suffix ensures that the cache is flushed at least once per week. - name: Install Composer dependencies @@ -283,7 +278,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -353,7 +348,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 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 81a9c4739ac19..d09e2af3dd213 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: ref: ${{ matrix.branch }} token: ${{ secrets.GUTENBERG_TOKEN }} diff --git a/composer.json b/composer.json index 3571377bd58bd..982a71a975223 100644 --- a/composer.json +++ b/composer.json @@ -23,14 +23,15 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, "composer/installers": true - } + }, + "lock": false }, "require-dev": { "phpcompatibility/phpcompatibility-wp": "^2.1.3", "wp-coding-standards/wpcs": "^3.0", "sirbrillig/phpcs-variable-analysis": "^2.8", "spatie/phpunit-watcher": "^1.23", - "yoast/phpunit-polyfills": "^1.0", + "yoast/phpunit-polyfills": "^1.1.0", "gutenberg/gutenberg-coding-standards": "@dev" }, "repositories": [ @@ -43,7 +44,7 @@ } ], "require": { - "composer/installers": "~1.0" + "composer/installers": "^1.0 || ^2.0" }, "scripts": { "format": "phpcbf --standard=phpcs.xml.dist --report-summary --report-source", diff --git a/docs/manifest.json b/docs/manifest.json index d76717fbdedfc..8387b9079694c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1098,33 +1098,9 @@ "parent": "components" }, { - "title": "NavigatorBackButton", - "slug": "navigator-back-button", - "markdown_source": "../packages/components/src/navigator/navigator-back-button/README.md", - "parent": "components" - }, - { - "title": "NavigatorButton", - "slug": "navigator-button", - "markdown_source": "../packages/components/src/navigator/navigator-button/README.md", - "parent": "components" - }, - { - "title": "NavigatorProvider", - "slug": "navigator-provider", - "markdown_source": "../packages/components/src/navigator/navigator-provider/README.md", - "parent": "components" - }, - { - "title": "NavigatorScreen", - "slug": "navigator-screen", - "markdown_source": "../packages/components/src/navigator/navigator-screen/README.md", - "parent": "components" - }, - { - "title": "NavigatorToParentButton", - "slug": "navigator-to-parent-button", - "markdown_source": "../packages/components/src/navigator/navigator-to-parent-button/README.md", + "title": "Navigator", + "slug": "navigator", + "markdown_source": "../packages/components/src/navigator/README.md", "parent": "components" }, { diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 4b3ca78f74d29..956e8dd010581 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -262,7 +262,7 @@ _Returns_ ### getBlockInsertionPoint -Returns the insertion point, the index at which the new inserted block would be placed. Defaults to the last index. +Returns the location of the insertion cue. Defaults to the last index. _Parameters_ @@ -982,7 +982,7 @@ _Returns_ ### isBlockInsertionPointVisible -Returns true if we should show the block insertion point. +Returns true if the block insertion point is visible. _Parameters_ diff --git a/docs/reference-guides/data/data-core-editor.md b/docs/reference-guides/data/data-core-editor.md index 4fea2c51fa54f..a4c1a59f0c423 100644 --- a/docs/reference-guides/data/data-core-editor.md +++ b/docs/reference-guides/data/data-core-editor.md @@ -1422,6 +1422,10 @@ _Parameters_ - _value_ `boolean|Object`: Whether the inserter should be opened (true) or closed (false). To specify an insertion point, use an object. - _value.rootClientId_ `string`: The root client ID to insert at. - _value.insertionIndex_ `number`: The index to insert at. +- _value.filterValue_ `string`: A query to filter the inserter results. +- _value.onSelect_ `Function`: A callback when an item is selected. +- _value.tab_ `string`: The tab to open in the inserter. +- _value.category_ `string`: The category to initialize in the inserter. _Returns_ diff --git a/lib/compat/wordpress-6.7/script-modules.php b/lib/compat/wordpress-6.7/script-modules.php index 0a440ec81688d..2282a3d4bd5ac 100644 --- a/lib/compat/wordpress-6.7/script-modules.php +++ b/lib/compat/wordpress-6.7/script-modules.php @@ -102,3 +102,46 @@ function () { }, 20 ); + +/** + * Prints HTML for the a11y Script Module. + * + * a11y relies on some DOM elements to use as ARIA live regions. + * Ideally, these elements are part of the initial HTML of the page + * so that accessibility tools can find them and observe updates. + */ +function gutenberg_a11y_script_module_html() { + $a11y_module_available = false; + + $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 ); + + foreach ( array_keys( $get_marked_for_enqueue->invoke( wp_script_modules() ) ) as $id ) { + if ( '@wordpress/a11y' === $id ) { + $a11y_module_available = true; + break; + } + } + if ( ! $a11y_module_available ) { + foreach ( array_keys( $get_import_map->invoke( wp_script_modules() )['imports'] ) as $id ) { + if ( '@wordpress/a11y' === $id ) { + $a11y_module_available = true; + break; + } + } + } + if ( ! $a11y_module_available ) { + return; + } + echo '
' + . '' + . '
' + . '
' + . '
'; +} +if ( ! method_exists( 'WP_Script_Modules', 'print_a11y_script_module_html' ) ) { + add_action( 'wp_footer', 'gutenberg_a11y_script_module_html' ); + add_action( 'admin_footer', 'gutenberg_a11y_script_module_html' ); +} diff --git a/lib/experimental/media/load.php b/lib/experimental/media/load.php index 5cb16d84e1d8d..bcb02accf62a6 100644 --- a/lib/experimental/media/load.php +++ b/lib/experimental/media/load.php @@ -186,6 +186,24 @@ function gutenberg_rest_get_attachment_filesize( array $post ): ?int { return null; } +/** + * Filters the list of rewrite rules formatted for output to an .htaccess file. + * + * Adds support for serving wasm-vips locally. + * + * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess. + * @return string Filtered rewrite rules. + */ +function gutenberg_filter_mod_rewrite_rules( string $rules ): string { + $rules .= "\n# BEGIN Gutenberg client-side media processing experiment\n" . + "AddType application/wasm wasm\n" . + "# END Gutenberg client-side media processing experiment\n"; + + return $rules; +} + +add_filter( 'mod_rewrite_rules', 'gutenberg_filter_mod_rewrite_rules' ); + /** * Enables cross-origin isolation in the block editor. * @@ -236,16 +254,11 @@ function gutenberg_start_cross_origin_isolation_output_buffer(): void { $coep = $is_safari ? 'require-corp' : 'credentialless'; ob_start( - function ( string $output, ?int $phase ) use ( $coep ): string { - // Only send the header when the buffer is not being cleaned. - if ( ( $phase & PHP_OUTPUT_HANDLER_CLEAN ) === 0 ) { - header( 'Cross-Origin-Opener-Policy: same-origin' ); - header( "Cross-Origin-Embedder-Policy: $coep" ); - - $output = gutenberg_add_crossorigin_attributes( $output ); - } + function ( string $output ) use ( $coep ): string { + header( 'Cross-Origin-Opener-Policy: same-origin' ); + header( "Cross-Origin-Embedder-Policy: $coep" ); - return $output; + return gutenberg_add_crossorigin_attributes( $output ); } ); } diff --git a/lib/experimental/script-modules.php b/lib/experimental/script-modules.php index fe23786fc0362..5a14e1418ed6d 100644 --- a/lib/experimental/script-modules.php +++ b/lib/experimental/script-modules.php @@ -200,44 +200,3 @@ function gutenberg_dequeue_module( $module_identifier ) { _deprecated_function( __FUNCTION__, 'Gutenberg 17.6.0', 'wp_dequeue_script_module' ); wp_script_modules()->dequeue( $module_identifier ); } - -/** - * Prints HTML for the a11y Script Module. - * - * a11y relies on some DOM elements to use as ARIA live regions. - * Ideally, these elements are part of the initial HTML of the page - * so that accessibility tools can find them and observe updates. - */ -function gutenberg_a11y_script_module_html() { - $a11y_module_available = false; - - $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 ); - - foreach ( array_keys( $get_marked_for_enqueue->invoke( wp_script_modules() ) ) as $id ) { - if ( '@wordpress/a11y' === $id ) { - $a11y_module_available = true; - break; - } - } - if ( ! $a11y_module_available ) { - foreach ( array_keys( $get_import_map->invoke( wp_script_modules() )['imports'] ) as $id ) { - if ( '@wordpress/a11y' === $id ) { - $a11y_module_available = true; - break; - } - } - } - if ( ! $a11y_module_available ) { - return; - } - echo '
' - . '' - . '
' - . '
' - . '
'; -} -add_action( 'wp_footer', 'gutenberg_a11y_script_module_html' ); -add_action( 'admin_footer', 'gutenberg_a11y_script_module_html' ); diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss index 91017c8bb9932..65f98bf6f15bf 100644 --- a/packages/base-styles/_mixins.scss +++ b/packages/base-styles/_mixins.scss @@ -1,6 +1,74 @@ @import "./functions"; @import "./long-content-fade"; +/** + * Typography + */ + +@mixin _text-heading() { + font-family: $font-family-headings; + font-weight: $font-weight-medium; +} + +@mixin _text-body() { + font-family: $font-family-body; + font-weight: $font-weight-regular; +} + +@mixin heading-small() { + @include _text-heading(); + font-size: $font-size-x-small; + line-height: $line-height-x-small; +} + +@mixin heading-medium() { + @include _text-heading(); + font-size: $font-size-medium; + line-height: $line-height-small; +} + +@mixin heading-large() { + @include _text-heading(); + font-size: $font-size-large; + line-height: $line-height-small; +} + +@mixin heading-x-large() { + @include _text-heading(); + font-size: $font-size-x-large; + line-height: $line-height-medium; +} + +@mixin heading-2x-large() { + @include _text-heading(); + font-size: $font-size-2x-large; + line-height: $font-line-height-2x-large; +} + +@mixin body-small() { + @include _text-body(); + font-size: $font-size-small; + line-height: $line-height-x-small; +} + +@mixin body-medium() { + @include _text-body(); + font-size: $font-size-medium; + line-height: $line-height-small; +} + +@mixin body-large() { + @include _text-body(); + font-size: $font-size-large; + line-height: $line-height-medium; +} + +@mixin body-x-large() { + @include _text-body(); + font-size: $font-size-x-large; + line-height: $line-height-x-large; +} + /** * Breakpoint mixins */ diff --git a/packages/base-styles/_variables.scss b/packages/base-styles/_variables.scss index 35092033c552b..ec0bdf91f2489 100644 --- a/packages/base-styles/_variables.scss +++ b/packages/base-styles/_variables.scss @@ -12,15 +12,37 @@ * Fonts & basic variables. */ -$default-font: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif; -$default-font-size: 13px; -$default-line-height: 1.4; -$editor-html-font: Menlo, Consolas, monaco, monospace; -$editor-font-size: 16px; -$default-block-margin: 28px; // This value provides a consistent, contiguous spacing between blocks. -$text-editor-font-size: 15px; -$editor-line-height: 1.8; -$mobile-text-min-font-size: 16px; // Any font size below 16px will cause Mobile Safari to "zoom in". +$default-font: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif; // Todo: deprecate in favor of $family variables +$default-line-height: 1.4; // Todo: deprecate in favor of $line-height tokens + +/** + * Typography + */ + +// Sizes +$font-size-x-small: 11px; +$font-size-small: 12px; +$font-size-medium: 13px; +$font-size-large: 15px; +$font-size-x-large: 20px; +$font-size-2x-large: 32px; + +// Line heights +$font-line-height-x-small: 16px; +$font-line-height-small: 20px; +$font-line-height-medium: 24px; +$font-line-height-large: 28px; +$font-line-height-x-large: 32px; +$font-line-height-2x-large: 40px; + +// Weights +$font-weight-regular: 400; +$font-weight-medium: 500; + +// Families +$font-family-headings: -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +$font-family-body: -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +$font-family-mono: Menlo, Consolas, monaco, monospace; /** * Grid System. @@ -91,7 +113,12 @@ $spinner-size: 16px; $canvas-padding: $grid-unit-20; /** - * Editor widths. + * Mobile specific styles + */ +$mobile-text-min-font-size: 16px; // Any font size below 16px will cause Mobile Safari to "zoom in". + +/** + * Editor styles. */ $sidebar-width: 280px; @@ -99,6 +126,11 @@ $content-width: 840px; $wide-content-width: 1100px; $widget-area-width: 700px; $secondary-sidebar-width: 350px; +$editor-font-size: 16px; +$default-block-margin: 28px; // This value provides a consistent, contiguous spacing between blocks. +$text-editor-font-size: 15px; +$editor-line-height: 1.8; +$editor-html-font: $font-family-mono; /** * Block & Editor UI. @@ -117,7 +149,7 @@ $block-padding: 14px; // Used to define space between block footprint and surrou $radius-block-ui: $radius-small; $shadow-popover: $elevation-x-small; $shadow-modal: $elevation-large; - +$default-font-size: $font-size-medium; /** * Block paddings. diff --git a/packages/block-directory/src/components/downloadable-block-list-item/style.scss b/packages/block-directory/src/components/downloadable-block-list-item/style.scss index fa5e5906cb1b9..6fce5e1b5b32a 100644 --- a/packages/block-directory/src/components/downloadable-block-list-item/style.scss +++ b/packages/block-directory/src/components/downloadable-block-list-item/style.scss @@ -69,6 +69,11 @@ align-items: center; justify-content: center; } + + .is-installing & { + // Adding an extra 6px to avoid the UI from jumping when the rating bar gets hidden + margin-right: $grid-unit-20 + 6px; + } } .block-directory-block-ratings { diff --git a/packages/block-editor/src/components/block-draggable/style.scss b/packages/block-editor/src/components/block-draggable/style.scss index 349afa2c3563c..f716f2d32a1d4 100644 --- a/packages/block-editor/src/components/block-draggable/style.scss +++ b/packages/block-editor/src/components/block-draggable/style.scss @@ -8,7 +8,7 @@ .block-editor-block-draggable-chip { background-color: $gray-900; border-radius: $radius-small; - box-shadow: 0 6px 8px rgba($black, 0.3); + box-shadow: $elevation-small; color: $white; cursor: grabbing; display: inline-flex; @@ -77,6 +77,6 @@ .block-editor-block-draggable-chip__disabled.block-editor-block-draggable-chip__disabled { background-color: $gray-700; opacity: 1; - box-shadow: 0 4px 8px rgba($black, 0.2); + box-shadow: $elevation-small; } } diff --git a/packages/block-editor/src/components/block-heading-level-dropdown/index.js b/packages/block-editor/src/components/block-heading-level-dropdown/index.js index 71cb2ede989b5..6b9bad09978a5 100644 --- a/packages/block-editor/src/components/block-heading-level-dropdown/index.js +++ b/packages/block-editor/src/components/block-heading-level-dropdown/index.js @@ -40,12 +40,18 @@ export default function HeadingLevelDropdown( { value, onChange, } ) { + const validOptions = options + .filter( + ( option ) => option === 0 || HEADING_LEVELS.includes( option ) + ) + .sort( ( a, b ) => a - b ); // Sorts numerically in ascending order; + return ( } label={ __( 'Change level' ) } - controls={ options.map( ( targetLevel ) => { + controls={ validOptions.map( ( targetLevel ) => { const isActive = targetLevel === value; return { icon: , diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 90c39649319dc..2cecd941dfa3b 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -25,6 +25,7 @@ import { getBlockDefaultClassName, hasBlockSupport, store as blocksStore, + privateApis as blocksPrivateApis, } from '@wordpress/blocks'; import { withFilters } from '@wordpress/components'; import { withDispatch, useDispatch, useSelect } from '@wordpress/data'; @@ -46,6 +47,8 @@ import { PrivateBlockContext } from './private-block-context'; import { unlock } from '../../lock-unlock'; +const { isUnmodifiedBlockContent } = unlock( blocksPrivateApis ); + /** * Merges wrapper props with special handling for classNames and styles. * @@ -350,12 +353,48 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { removeBlock( _clientId ); } else { registry.batch( () => { + const firstBlock = getBlock( firstClientId ); + const isFirstBlockContentUnmodified = + isUnmodifiedBlockContent( firstBlock ); + const defaultBlockName = getDefaultBlockName(); + const replacement = switchToBlockType( + firstBlock, + defaultBlockName + ); + const canTransformToDefaultBlock = + !! replacement?.length && + replacement.every( ( block ) => + canInsertBlockType( block.name, _clientId ) + ); + if ( + isFirstBlockContentUnmodified && + canTransformToDefaultBlock + ) { + // Step 1: If the block is empty and can be transformed to the default block type. + replaceBlocks( + firstClientId, + replacement, + changeSelection + ); + } else if ( + isFirstBlockContentUnmodified && + firstBlock.name === defaultBlockName + ) { + // Step 2: If the block is empty and is already the default block type. + removeBlock( firstClientId ); + const nextBlockClientId = + getNextBlockClientId( clientId ); + if ( nextBlockClientId ) { + selectBlock( nextBlockClientId ); + } + } else if ( canInsertBlockType( - getBlockName( firstClientId ), + firstBlock.name, targetRootClientId ) ) { + // Step 3: If the block can be moved up. moveBlocksToPosition( [ firstClientId ], _clientId, @@ -363,21 +402,17 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { getBlockIndex( _clientId ) ); } else { - const replacement = switchToBlockType( - getBlock( firstClientId ), - getDefaultBlockName() - ); - - if ( - replacement && - replacement.length && + const canLiftAndTransformToDefaultBlock = + !! replacement?.length && replacement.every( ( block ) => canInsertBlockType( block.name, targetRootClientId ) - ) - ) { + ); + + if ( canLiftAndTransformToDefaultBlock ) { + // Step 4: If the block can be transformed to the default block type and moved up. insertBlocks( replacement, getBlockIndex( _clientId ), @@ -386,6 +421,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { ); removeBlock( firstClientId, false ); } else { + // Step 5: Continue the default behavior. switchToDefaultOrRemove(); } } diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss index 3e3865e689bea..c5fda109d8b67 100644 --- a/packages/block-editor/src/components/block-list/content.scss +++ b/packages/block-editor/src/components/block-list/content.scss @@ -411,6 +411,18 @@ _::-webkit-full-page-media, _:future, :root .has-multi-selection .block-editor-b margin-left: -1px; margin-right: -1px; transition: background-color 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + font-size: $default-font-size; + font-family: $default-font; + color: $black; + font-weight: normal; + + .is-zoomed-out & { + // Scale the font size based on the zoom level. + font-size: calc(#{$default-font-size} * ( 2 - var(--wp-block-editor-iframe-zoom-out-scale) )); + } &.is-dragged-over { background: $gray-400; diff --git a/packages/block-editor/src/components/block-list/zoom-out-separator.js b/packages/block-editor/src/components/block-list/zoom-out-separator.js index be5af54963060..9e0d087c2267c 100644 --- a/packages/block-editor/src/components/block-list/zoom-out-separator.js +++ b/packages/block-editor/src/components/block-list/zoom-out-separator.js @@ -13,6 +13,7 @@ import { import { useReducedMotion } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -29,14 +30,16 @@ export function ZoomOutSeparator( { const { sectionRootClientId, sectionClientIds, - blockInsertionPoint, + insertionPoint, blockInsertionPointVisible, + blockInsertionPoint, } = useSelect( ( select ) => { const { - getBlockInsertionPoint, + getInsertionPoint, getBlockOrder, - isBlockInsertionPointVisible, getSectionRootClientId, + isBlockInsertionPointVisible, + getBlockInsertionPoint, } = unlock( select( blockEditorStore ) ); const root = getSectionRootClientId(); @@ -45,6 +48,7 @@ export function ZoomOutSeparator( { sectionRootClientId: root, sectionClientIds: sectionRootClientIds, blockOrder: getBlockOrder( root ), + insertionPoint: getInsertionPoint(), blockInsertionPoint: getBlockInsertionPoint(), blockInsertionPointVisible: isBlockInsertionPointVisible(), }; @@ -67,17 +71,30 @@ export function ZoomOutSeparator( { return null; } + const hasTopInsertionPoint = + insertionPoint?.index === 0 && + clientId === sectionClientIds[ insertionPoint.index ]; + const hasBottomInsertionPoint = + insertionPoint && + insertionPoint.hasOwnProperty( 'index' ) && + clientId === sectionClientIds[ insertionPoint.index - 1 ]; + // We want to show the zoom out separator in either of these conditions: + // 1. If the inserter has an insertion index set + // 2. We are dragging a pattern over an insertion point if ( position === 'top' ) { isVisible = - blockInsertionPointVisible && - blockInsertionPoint.index === 0 && - clientId === sectionClientIds[ blockInsertionPoint.index ]; + hasTopInsertionPoint || + ( blockInsertionPointVisible && + blockInsertionPoint.index === 0 && + clientId === sectionClientIds[ blockInsertionPoint.index ] ); } if ( position === 'bottom' ) { isVisible = - blockInsertionPointVisible && - clientId === sectionClientIds[ blockInsertionPoint.index - 1 ]; + hasBottomInsertionPoint || + ( blockInsertionPointVisible && + clientId === + sectionClientIds[ blockInsertionPoint.index - 1 ] ); } return ( @@ -103,7 +120,19 @@ export function ZoomOutSeparator( { data-is-insertion-point="true" onDragOver={ () => setIsDraggedOver( true ) } onDragLeave={ () => setIsDraggedOver( false ) } - > + > + + { __( 'Drop pattern.' ) } + + ) } ); diff --git a/packages/block-editor/src/components/block-navigation/dropdown.js b/packages/block-editor/src/components/block-navigation/dropdown.js index 035a38604b029..0bf8fd0532018 100644 --- a/packages/block-editor/src/components/block-navigation/dropdown.js +++ b/packages/block-editor/src/components/block-navigation/dropdown.js @@ -27,8 +27,7 @@ function BlockNavigationDropdownToggle( { } ) { return ( @@ -74,13 +74,13 @@ export default function Pagination( { className="block-editor-patterns__grid-pagination-next" > @@ -89,8 +89,9 @@ export default function Pagination( { onClick={ () => changePage( numPages ) } disabled={ currentPage === numPages } aria-label={ __( 'Last page' ) } - size="default" + size="compact" accessibleWhenDisabled + className="block-editor-patterns__grid-pagination-button" > » diff --git a/packages/block-editor/src/components/block-patterns-paging/style.scss b/packages/block-editor/src/components/block-patterns-paging/style.scss index f5f34d821233a..85d39f9a36577 100644 --- a/packages/block-editor/src/components/block-patterns-paging/style.scss +++ b/packages/block-editor/src/components/block-patterns-paging/style.scss @@ -4,37 +4,20 @@ border-top: 1px solid $gray-800; padding: $grid-unit-05; justify-content: center; - .components-button.is-tertiary { - width: auto; - height: $button-size-compact; - justify-content: center; - - &:disabled { - color: $gray-600; - background: none; - } - - &:hover:not(:disabled) { - color: $white; - background-color: $gray-700; - } - } } } .show-icon-labels { - .block-editor-patterns__grid-pagination { - .components-button { - width: auto; - // Hide the button icons when labels are set to display... - span { - display: none; - } - // ... and display labels. - // Uses ::before as ::after is already used for active tab styling. - &::before { - content: attr(aria-label); - } + .block-editor-patterns__grid-pagination-button { + width: auto; + // Hide the button icons when labels are set to display... + span { + display: none; + } + // ... and display labels. + // Uses ::before as ::after is already used for active tab styling. + &::before { + content: attr(aria-label); } } } diff --git a/packages/block-editor/src/components/block-quick-navigation/index.js b/packages/block-editor/src/components/block-quick-navigation/index.js index 4f22c2a266722..fdb3475b3e180 100644 --- a/packages/block-editor/src/components/block-quick-navigation/index.js +++ b/packages/block-editor/src/components/block-quick-navigation/index.js @@ -59,8 +59,7 @@ function BlockQuickNavigationItem( { clientId, onSelect } ) { return ( diff --git a/packages/components/src/border-control/styles.ts b/packages/components/src/border-control/styles.ts index 2c77a2d21465d..a678b6f362308 100644 --- a/packages/components/src/border-control/styles.ts +++ b/packages/components/src/border-control/styles.ts @@ -156,7 +156,6 @@ export const resetButton = css` border-top: ${ CONFIG.borderWidth } solid ${ COLORS.gray[ 400 ] }; border-top-left-radius: 0; border-top-right-radius: 0; - height: 40px; } `; diff --git a/packages/components/src/guide/index.tsx b/packages/components/src/guide/index.tsx index 0ca5957fd3a65..121c9f22330e8 100644 --- a/packages/components/src/guide/index.tsx +++ b/packages/components/src/guide/index.tsx @@ -164,6 +164,7 @@ function Guide( { className="components-guide__finish-button" variant="primary" onClick={ onFinish } + __next40pxDefaultSize > { finishButtonText } diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 200225148a0a4..cc3c0265c4220 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -131,7 +131,8 @@ export { NavigatorBackButton as __experimentalNavigatorBackButton, NavigatorToParentButton as __experimentalNavigatorToParentButton, useNavigator as __experimentalUseNavigator, -} from './navigator'; +} from './navigator/legacy'; +export { Navigator, useNavigator } from './navigator'; export { default as Notice } from './notice'; export { default as __experimentalNumberControl } from './number-control'; export { default as NoticeList } from './notice/list'; diff --git a/packages/components/src/navigator/README.md b/packages/components/src/navigator/README.md new file mode 100644 index 0000000000000..00b1cfaeebe0f --- /dev/null +++ b/packages/components/src/navigator/README.md @@ -0,0 +1,168 @@ +# `Navigator` + +`Navigator` is a collection components that allow rendering nested views/panels/menus (via the `Navigator.Screen` component) and navigate between them (via the `Navigator.Button` and `Navigator.BackButton` components). + +## Usage + +```jsx +import { Navigator } from '@wordpress/components'; + +const MyNavigation = () => ( + + +

This is the home screen.

+ + Navigate to child screen. + +
+ +

This is the child screen.

+ Go back +
+
+); +``` + +### Hierarchical `path`s + +`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. + +`Navigator` will treat "back" navigations as going to the parent screen — it is, therefore, the responsibility of the consumer of the component to create the correct screen hierarchy. + +For example: + +- `/` is the root of all paths. There should always be a screen with `path="/"`; +- `/parent/child` is a child of `/parent`; +- `/parent/child/grand-child` is a child of `/parent/child`; +- `/parent/:param` is a child of `/parent` as well; +- if the current screen has a `path="/parent/child/grand-child"`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. + +### Height and animations + +Due to how `Navigator.Screen` animations work, it is recommended that the `Navigator` component is assigned a `height` to prevent some potential UI jumps while moving across screens. + +### Individual components + +`Navigator` is comprised of four individual components: + +- `Navigator`: a wrapper component and context provider. It holds the main logic for hiding and showing screens. +- `Navigator.Screen`: represents a single view/screen/panel; +- `Navigator.Button`: renders a button that allows navigating to a different `Navigator.Screen`; +- `Navigator.BackButton`: renders a button that allows navigating to the parent `Navigator.Screen` (see the section above about hierarchical paths). + +For advanced usages, consumers can use the `useNavigator` hook. + +#### `Navigator` + +##### Props + +###### `initialPath`: `string` + +The initial active path. + +- Required: Yes + +###### `children`: `string` + +The children elements. + +- Required: Yes + +#### `Navigator.Screen` + +###### `path`: `string` + +The screen's path, matched against the current path stored in the navigator. + +`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. + +`Navigator` will treat "back" navigations as going to the parent screen — it is, therefore, the responsibility of the consumer of the component to create the correct screen hierarchy. + +For example: + +- `/` is the root of all paths. There should always be a screen with `path="/"`. +- `/parent/child` is a child of `/parent`. +- `/parent/child/grand-child` is a child of `/parent/child`. +- `/parent/:param` is a child of `/parent` as well. +- if the current screen has a `path` with value `/parent/child/grand-child`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. + +- Required: Yes + +###### `children`: `string` + +The children elements. + +- Required: Yes + +##### `Navigator.Button` + +###### `path`: `string` + +The path of the screen to navigate to. The value of this prop needs to be [a valid value for an HTML attribute](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2). + +- Required: Yes + +###### `attributeName`: `string` + +The HTML attribute used to identify the `Navigator.Button`, which is used by `Navigator` to restore focus. + +- Required: No +- Default: `id` + +###### `children`: `string` + +The children elements. + +- Required: No + +###### Inherited props + +`Navigator.Button` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. + +##### `Navigator.BackButton` + +###### `children`: `string` + +The children elements. + +- Required: No + +###### Inherited props + +`Navigator.BackButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. + +###### `useNavigator` + +You can retrieve a `navigator` instance by using the `useNavigator` hook. + +The `navigator` instance has a few properties: + +###### `goTo`: `( path: string, options: NavigateOptions ) => void` + +The `goTo` function allows navigating to a given path. The second argument can augment the navigation operations with different options. + +The available options are: + +- `focusTargetSelector`: `string`. An optional property used to specify the CSS selector used to restore focus on the matching element when navigating back; +- `isBack`: `boolean`. An optional property used to specify whether the navigation should be considered as backwards (thus enabling focus restoration when possible, and causing the animation to be backwards too); +- `skipFocus`: `boolean`. An optional property used to opt out of `Navigator`'s focus management, useful when the consumer of the component wants to manage focus themselves; + +###### `goBack`: `( path: string, options: NavigateOptions ) => void` + +The `goBack` function allows navigating to the parent screen. Parent/child navigation only works if the paths you define are hierarchical (see note above). + +When a match is not found, the function will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. + +The available options are the same as for the `goTo` method, except for the `isBack` property, which is not available for the `goBack` method. + +###### `location`: `NavigatorLocation` + +The `location` object represents the current location, and has a few properties: + +- `path`: `string`. The path associated to the location. +- `isBack`: `boolean`. A flag that is `true` when the current location was reached by navigating backwards. +- `isInitial`: `boolean`. A flag that is `true` only for the initial location. + +###### `params`: `Record< string, string | string[] >` + +The parsed record of parameters from the current location. For example if the current screen path is `/product/:productId` and the location is `/product/123`, then `params` will be `{ productId: '123' }`. diff --git a/packages/components/src/navigator/index.ts b/packages/components/src/navigator/index.ts deleted file mode 100644 index 130edc2ae52eb..0000000000000 --- a/packages/components/src/navigator/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { NavigatorProvider } from './navigator-provider/component'; -export { NavigatorScreen } from './navigator-screen/component'; -export { NavigatorButton } from './navigator-button/component'; -export { NavigatorBackButton } from './navigator-back-button/component'; -export { NavigatorToParentButton } from './navigator-to-parent-button/component'; -export { useNavigator } from './use-navigator'; diff --git a/packages/components/src/navigator/index.tsx b/packages/components/src/navigator/index.tsx new file mode 100644 index 0000000000000..1d9ae95441e01 --- /dev/null +++ b/packages/components/src/navigator/index.tsx @@ -0,0 +1,131 @@ +/** + * Internal dependencies + */ +import { Navigator as TopLevelNavigator } from './navigator/component'; +import { NavigatorScreen } from './navigator-screen/component'; +import { NavigatorButton } from './navigator-button/component'; +import { NavigatorBackButton } from './navigator-back-button/component'; +export { useNavigator } from './use-navigator'; + +/** + * The `Navigator` component allows rendering nested views/panels/menus + * (via the `Navigator.Screen` component) and navigate between them + * (via the `Navigator.Button` and `Navigator.BackButton` components). + * + * ```jsx + * import { Navigator } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ +export const Navigator = Object.assign( TopLevelNavigator, { + /** + * The `Navigator.Screen` component represents a single view/screen/panel and + * should be used in combination with the `Navigator`, the `Navigator.Button` + * and the `Navigator.BackButton` components. + * + * @example + * ```jsx + * import { Navigator } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ + Screen: Object.assign( NavigatorScreen, { + displayName: 'Navigator.Screen', + } ), + /** + * The `Navigator.Button` component can be used to navigate to a screen and + * should be used in combination with the `Navigator`, the `Navigator.Screen` + * and the `Navigator.BackButton` components. + * + * @example + * ```jsx + * import { Navigator } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ + Button: Object.assign( NavigatorButton, { + displayName: 'Navigator.Button', + } ), + /** + * The `Navigator.BackButton` component can be used to navigate to a screen and + * should be used in combination with the `Navigator`, the `Navigator.Screen` + * and the `Navigator.Button` components. + * + * @example + * ```jsx + * import { Navigator } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ + BackButton: Object.assign( NavigatorBackButton, { + displayName: 'Navigator.BackButton', + } ), +} ); diff --git a/packages/components/src/navigator/legacy.ts b/packages/components/src/navigator/legacy.ts new file mode 100644 index 0000000000000..1caa5380fc049 --- /dev/null +++ b/packages/components/src/navigator/legacy.ts @@ -0,0 +1,169 @@ +/** + * Internal dependencies + */ +import { Navigator as InternalNavigator } from './navigator/component'; +import { NavigatorScreen as InternalNavigatorScreen } from './navigator-screen/component'; +import { NavigatorButton as InternalNavigatorButton } from './navigator-button/component'; +import { NavigatorBackButton as InternalNavigatorBackButton } from './navigator-back-button/component'; +import { NavigatorToParentButton as InternalNavigatorToParentButton } from './navigator-to-parent-button/component'; +export { useNavigator } from './use-navigator'; + +/** + * The `NavigatorProvider` component allows rendering nested views/panels/menus + * (via the `NavigatorScreen` component and navigate between them + * (via the `NavigatorButton` and `NavigatorBackButton` components). + * + * ```jsx + * import { + * __experimentalNavigatorProvider as NavigatorProvider, + * __experimentalNavigatorScreen as NavigatorScreen, + * __experimentalNavigatorButton as NavigatorButton, + * __experimentalNavigatorBackButton as NavigatorBackButton, + * } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ +export const NavigatorProvider = Object.assign( InternalNavigator, { + displayName: 'NavigatorProvider', +} ); + +/** + * The `NavigatorScreen` component represents a single view/screen/panel and + * should be used in combination with the `NavigatorProvider`, the + * `NavigatorButton` and the `NavigatorBackButton` components. + * + * @example + * ```jsx + * import { + * __experimentalNavigatorProvider as NavigatorProvider, + * __experimentalNavigatorScreen as NavigatorScreen, + * __experimentalNavigatorButton as NavigatorButton, + * __experimentalNavigatorBackButton as NavigatorBackButton, + * } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ +export const NavigatorScreen = Object.assign( InternalNavigatorScreen, { + displayName: 'NavigatorScreen', +} ); + +/** + * The `NavigatorButton` component can be used to navigate to a screen and should + * be used in combination with the `NavigatorProvider`, the `NavigatorScreen` + * and the `NavigatorBackButton` components. + * + * @example + * ```jsx + * import { + * __experimentalNavigatorProvider as NavigatorProvider, + * __experimentalNavigatorScreen as NavigatorScreen, + * __experimentalNavigatorButton as NavigatorButton, + * __experimentalNavigatorBackButton as NavigatorBackButton, + * } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back + * + *
+ *
+ * ); + * ``` + */ +export const NavigatorButton = Object.assign( InternalNavigatorButton, { + displayName: 'NavigatorButton', +} ); + +/** + * The `NavigatorBackButton` component can be used to navigate to a screen and + * should be used in combination with the `NavigatorProvider`, the + * `NavigatorScreen` and the `NavigatorButton` components. + * + * @example + * ```jsx + * import { + * __experimentalNavigatorProvider as NavigatorProvider, + * __experimentalNavigatorScreen as NavigatorScreen, + * __experimentalNavigatorButton as NavigatorButton, + * __experimentalNavigatorBackButton as NavigatorBackButton, + * } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + *

This is the home screen.

+ * + * Navigate to child screen. + * + *
+ * + * + *

This is the child screen.

+ * + * Go back (to parent) + * + *
+ *
+ * ); + * ``` + */ +export const NavigatorBackButton = Object.assign( InternalNavigatorBackButton, { + displayName: 'NavigatorBackButton', +} ); + +/** + * _Note: this component is deprecated. Please use the `NavigatorBackButton` + * component instead._ + * + * @deprecated + */ +export const NavigatorToParentButton = Object.assign( + InternalNavigatorToParentButton, + { + displayName: 'NavigatorToParentButton', + } +); diff --git a/packages/components/src/navigator/navigator-back-button/README.md b/packages/components/src/navigator/navigator-back-button/README.md deleted file mode 100644 index 01d4221be536e..0000000000000 --- a/packages/components/src/navigator/navigator-back-button/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# `NavigatorBackButton` - -
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. -
- -The `NavigatorBackButton` component can be used to navigate to a screen and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorScreen`](/packages/components/src/navigator/navigator-screen/README.md) and the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md) components (or the `useNavigator` hook). - -## Usage - -Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. - -### Inherited props - -`NavigatorBackButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. diff --git a/packages/components/src/navigator/navigator-back-button/component.tsx b/packages/components/src/navigator/navigator-back-button/component.tsx index 88ed45b643a13..b5c4de7df78a8 100644 --- a/packages/components/src/navigator/navigator-back-button/component.tsx +++ b/packages/components/src/navigator/navigator-back-button/component.tsx @@ -21,43 +21,7 @@ function UnconnectedNavigatorBackButton( return ; } -/** - * The `NavigatorBackButton` component can be used to navigate to a screen and - * should be used in combination with the `NavigatorProvider`, the - * `NavigatorScreen` and the `NavigatorButton` components (or the `useNavigator` - * hook). - * - * @example - * ```jsx - * import { - * __experimentalNavigatorProvider as NavigatorProvider, - * __experimentalNavigatorScreen as NavigatorScreen, - * __experimentalNavigatorButton as NavigatorButton, - * __experimentalNavigatorBackButton as NavigatorBackButton, - * } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back (to parent) - * - *
- *
- * ); - * ``` - */ export const NavigatorBackButton = contextConnect( UnconnectedNavigatorBackButton, - 'NavigatorBackButton' + 'Navigator.BackButton' ); - -export default NavigatorBackButton; diff --git a/packages/components/src/navigator/navigator-back-button/hook.ts b/packages/components/src/navigator/navigator-back-button/hook.ts index 9ddc095292190..d6fcd39647bff 100644 --- a/packages/components/src/navigator/navigator-back-button/hook.ts +++ b/packages/components/src/navigator/navigator-back-button/hook.ts @@ -20,7 +20,7 @@ export function useNavigatorBackButton( as = Button, ...otherProps - } = useContextSystem( props, 'NavigatorBackButton' ); + } = useContextSystem( props, 'Navigator.BackButton' ); const { goBack } = useNavigator(); const handleClick: React.MouseEventHandler< HTMLButtonElement > = diff --git a/packages/components/src/navigator/navigator-button/README.md b/packages/components/src/navigator/navigator-button/README.md deleted file mode 100644 index 72154ec317da4..0000000000000 --- a/packages/components/src/navigator/navigator-button/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# `NavigatorButton` - -
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. -
- -The `NavigatorButton` component can be used to navigate to a screen and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorScreen`](/packages/components/src/navigator/navigator-screen/README.md) and the [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) components (or the `useNavigator` hook). - -## Usage - -Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. - -## Props - -The component accepts the following props: - -### `attributeName`: `string` - -The HTML attribute used to identify the `NavigatorButton`, which is used by `Navigator` to restore focus. - -- Required: No -- Default: `id` - -### `onClick`: `React.MouseEventHandler< HTMLElement >` - -The callback called in response to a `click` event. - -- Required: No - -### `path`: `string` - -The path of the screen to navigate to. The value of this prop needs to be [a valid value for an HTML attribute](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2). - -- Required: Yes - -### Inherited props - -`NavigatorButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. diff --git a/packages/components/src/navigator/navigator-button/component.tsx b/packages/components/src/navigator/navigator-button/component.tsx index 1b84a2315c04d..a6dc796337672 100644 --- a/packages/components/src/navigator/navigator-button/component.tsx +++ b/packages/components/src/navigator/navigator-button/component.tsx @@ -21,42 +21,7 @@ function UnconnectedNavigatorButton( return ; } -/** - * The `NavigatorButton` component can be used to navigate to a screen and should - * be used in combination with the `NavigatorProvider`, the `NavigatorScreen` - * and the `NavigatorBackButton` components (or the `useNavigator` hook). - * - * @example - * ```jsx - * import { - * __experimentalNavigatorProvider as NavigatorProvider, - * __experimentalNavigatorScreen as NavigatorScreen, - * __experimentalNavigatorButton as NavigatorButton, - * __experimentalNavigatorBackButton as NavigatorBackButton, - * } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back - * - *
- *
- * ); - * ``` - */ export const NavigatorButton = contextConnect( UnconnectedNavigatorButton, - 'NavigatorButton' + 'Navigator.Button' ); - -export default NavigatorButton; diff --git a/packages/components/src/navigator/navigator-button/hook.ts b/packages/components/src/navigator/navigator-button/hook.ts index 3e39b05661e1b..59d2aaa65662d 100644 --- a/packages/components/src/navigator/navigator-button/hook.ts +++ b/packages/components/src/navigator/navigator-button/hook.ts @@ -25,7 +25,7 @@ export function useNavigatorButton( as = Button, attributeName = 'id', ...otherProps - } = useContextSystem( props, 'NavigatorButton' ); + } = useContextSystem( props, 'Navigator.Button' ); const escapedPath = escapeAttribute( path ); diff --git a/packages/components/src/navigator/navigator-provider/README.md b/packages/components/src/navigator/navigator-provider/README.md deleted file mode 100644 index b9bc8f0c6bcdc..0000000000000 --- a/packages/components/src/navigator/navigator-provider/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# `NavigatorProvider` - -
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. -
- -The `NavigatorProvider` component allows rendering nested views/panels/menus (via the [`NavigatorScreen` component](/packages/components/src/navigator/navigator-screen/README.md)) and navigate between these different states (via the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md), [`NavigatorToParentButton`](/packages/components/src/navigator/navigator-to-parent-button/README.md) and [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) components or the `useNavigator` hook). The Global Styles sidebar is an example of this. - -## Usage - -```jsx -import { - __experimentalNavigatorProvider as NavigatorProvider, - __experimentalNavigatorScreen as NavigatorScreen, - __experimentalNavigatorButton as NavigatorButton, - __experimentalNavigatorBackButton as NavigatorBackButton, -} from '@wordpress/components'; - -const MyNavigation = () => ( - - -

This is the home screen.

- - Navigate to child screen. - -
- - -

This is the child screen.

- Go back -
-
-); -``` - -### Hierarchical `path`s - -`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. - -`Navigator` will treat "back" navigations as going to the parent screen — it is therefore responsibility of the consumer of the component to create the correct screen hierarchy. - -For example: - -- `/` is the root of all paths. There should always be a screen with `path="/"`. -- `/parent/child` is a child of `/parent`. -- `/parent/child/grand-child` is a child of `/parent/child`. -- `/parent/:param` is a child of `/parent` as well. -- if the current screen has a `path` with value `/parent/child/grand-child`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. - -### Height and animations - -Due to how `NavigatorScreen` animations work, it is recommended that the `NavigatorProvider` component is assigned a `height` to prevent some potential UI jumps while moving across screens. - -## Props - -The component accepts the following props: - -### `initialPath`: `string` - -The initial active path. - -- Required: No - -## The `navigator` object - -You can retrieve a `navigator` instance by using the `useNavigator` hook. - -The `navigator` instance has a few properties: - -### `goTo`: `( path: string, options: NavigateOptions ) => void` - -The `goTo` function allows navigating to a given path. The second argument can augment the navigation operations with different options. - -The available options are: - -- `focusTargetSelector`: `string`. An optional property used to specify the CSS selector used to restore focus on the matching element when navigating back; -- `isBack`: `boolean`. An optional property used to specify whether the navigation should be considered as backwards (thus enabling focus restoration when possible, and causing the animation to be backwards too); -- `skipFocus`: `boolean`. An optional property used to opt out of `Navigator`'s focus management, useful when the consumer of the component wants to manage focus themselves; - -### `goBack`: `( path: string, options: NavigateOptions ) => void` - -The `goBack` function allows navigating to the parent screen. Parent/child navigation only works if the paths you define are hierarchical (see note above). - -When a match is not found, the function will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) are found. - -The available options are the same as for the `goTo` method, except for the `isBack` property, which is not available for the `goBack` method. - -### `location`: `NavigatorLocation` - -The `location` object represent the current location, and has a few properties: - -- `path`: `string`. The path associated to the location. -- `isBack`: `boolean`. A flag that is `true` when the current location was reached by navigating backwards. -- `isInitial`: `boolean`. A flag that is `true` only for the initial location. - -### `params`: `Record< string, string | string[] >` - -The parsed record of parameters from the current location. For example if the current screen path is `/product/:productId` and the location is `/product/123`, then `params` will be `{ productId: '123' }`. diff --git a/packages/components/src/navigator/navigator-screen/README.md b/packages/components/src/navigator/navigator-screen/README.md deleted file mode 100644 index 583da461cd3c2..0000000000000 --- a/packages/components/src/navigator/navigator-screen/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# `NavigatorScreen` - -
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. -
- -The `NavigatorScreen` component represents a single view/screen/panel and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md) and the [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) components (or the `useNavigator` hook). - -## Usage - -Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. - -## Props - -The component accepts the following props: - -### `path`: `string` - -The screen"s path, matched against the current path stored in the navigator. - -`Navigator` assumes that screens are organized hierarchically according to their `path`, which should follow a URL-like scheme where each path segment starts with and is separated by the `/` character. - -`Navigator` will treat "back" navigations as going to the parent screen — it is therefore responsibility of the consumer of the component to create the correct screen hierarchy. - -For example: - -- `/` is the root of all paths. There should always be a screen with `path="/"`. -- `/parent/child` is a child of `/parent`. -- `/parent/child/grand-child` is a child of `/parent/child`. -- `/parent/:param` is a child of `/parent` as well. -- if the current screen has a `path` with value `/parent/child/grand-child`, when going "back" `Navigator` will try to recursively navigate the path hierarchy until a matching screen (or the root `/`) is found. - -- Required: Yes diff --git a/packages/components/src/navigator/navigator-screen/component.tsx b/packages/components/src/navigator/navigator-screen/component.tsx index f2d2c26e576c2..fe0d81b90a17b 100644 --- a/packages/components/src/navigator/navigator-screen/component.tsx +++ b/packages/components/src/navigator/navigator-screen/component.tsx @@ -36,7 +36,7 @@ function UnconnectedNavigatorScreen( ) { if ( ! /^\//.test( props.path ) ) { warning( - 'wp.components.NavigatorScreen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' + 'wp.components.Navigator.Screen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' ); } @@ -48,7 +48,7 @@ function UnconnectedNavigatorScreen( path, onAnimationEnd: onAnimationEndProp, ...otherProps - } = useContextSystem( props, 'NavigatorScreen' ); + } = useContextSystem( props, 'Navigator.Screen' ); const { location, match, addScreen, removeScreen } = useContext( NavigatorContext ); @@ -153,43 +153,7 @@ function UnconnectedNavigatorScreen( ) : null; } -/** - * The `NavigatorScreen` component represents a single view/screen/panel and - * should be used in combination with the `NavigatorProvider`, the - * `NavigatorButton` and the `NavigatorBackButton` components (or the `useNavigator` - * hook). - * - * @example - * ```jsx - * import { - * __experimentalNavigatorProvider as NavigatorProvider, - * __experimentalNavigatorScreen as NavigatorScreen, - * __experimentalNavigatorButton as NavigatorButton, - * __experimentalNavigatorBackButton as NavigatorBackButton, - * } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back - * - *
- *
- * ); - * ``` - */ export const NavigatorScreen = contextConnect( UnconnectedNavigatorScreen, - 'NavigatorScreen' + 'Navigator.Screen' ); - -export default NavigatorScreen; diff --git a/packages/components/src/navigator/navigator-to-parent-button/README.md b/packages/components/src/navigator/navigator-to-parent-button/README.md deleted file mode 100644 index 0100ea9b8d2e1..0000000000000 --- a/packages/components/src/navigator/navigator-to-parent-button/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# `NavigatorToParentButton` - -
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. -
- -This component is deprecated. Please use the [`NavigatorBackButton`](/packages/components/src/navigator/navigator-back-button/README.md) component instead. - -The `NavigatorToParentButton` component can be used to navigate to a screen and should be used in combination with the [`NavigatorProvider`](/packages/components/src/navigator/navigator-provider/README.md), the [`NavigatorScreen`](/packages/components/src/navigator/navigator-screen/README.md) and the [`NavigatorButton`](/packages/components/src/navigator/navigator-button/README.md) components (or the `useNavigator` hook). - -## Usage - -Refer to [the `NavigatorProvider` component](/packages/components/src/navigator/navigator-provider/README.md#usage) for a usage example. - -### Inherited props - -`NavigatorToParentButton` also inherits all of the [`Button` props](/packages/components/src/button/README.md#props), except for `href` and `target`. diff --git a/packages/components/src/navigator/navigator-to-parent-button/component.tsx b/packages/components/src/navigator/navigator-to-parent-button/component.tsx index fcbadea03cf7b..f1c2d27e2284a 100644 --- a/packages/components/src/navigator/navigator-to-parent-button/component.tsx +++ b/packages/components/src/navigator/navigator-to-parent-button/component.tsx @@ -17,21 +17,16 @@ function UnconnectedNavigatorToParentButton( ) { deprecated( 'wp.components.NavigatorToParentButton', { since: '6.7', - alternative: 'wp.components.NavigatorBackButton', + alternative: 'wp.components.Navigator.BackButton', } ); return ; } /** - * _Note: this component is deprecated. Please use the `NavigatorBackButton` - * component instead._ - * * @deprecated */ export const NavigatorToParentButton = contextConnect( UnconnectedNavigatorToParentButton, - 'NavigatorToParentButton' + 'Navigator.ToParentButton' ); - -export default NavigatorToParentButton; diff --git a/packages/components/src/navigator/navigator-provider/component.tsx b/packages/components/src/navigator/navigator/component.tsx similarity index 82% rename from packages/components/src/navigator/navigator-provider/component.tsx rename to packages/components/src/navigator/navigator/component.tsx index 01254b743f87d..bd49b3682fb14 100644 --- a/packages/components/src/navigator/navigator-provider/component.tsx +++ b/packages/components/src/navigator/navigator/component.tsx @@ -21,7 +21,7 @@ import { View } from '../../view'; import { NavigatorContext } from '../context'; import * as styles from '../styles'; import type { - NavigatorProviderProps, + NavigatorProps, NavigatorLocation, NavigatorContext as NavigatorContextType, NavigateOptions, @@ -213,8 +213,8 @@ function routerReducer( }; } -function UnconnectedNavigatorProvider( - props: WordPressComponentProps< NavigatorProviderProps, 'div' >, +function UnconnectedNavigator( + props: WordPressComponentProps< NavigatorProps, 'div' >, forwardedRef: ForwardedRef< any > ) { const { @@ -222,7 +222,7 @@ function UnconnectedNavigatorProvider( children, className, ...otherProps - } = useContextSystem( props, 'NavigatorProvider' ); + } = useContextSystem( props, 'Navigator' ); const [ routerState, dispatch ] = useReducer( routerReducer, @@ -275,7 +275,7 @@ function UnconnectedNavigatorProvider( const cx = useCx(); const classes = useMemo( - () => cx( styles.navigatorProviderWrapper, className ), + () => cx( styles.navigatorWrapper, className ), [ className, cx ] ); @@ -288,42 +288,4 @@ function UnconnectedNavigatorProvider( ); } -/** - * The `NavigatorProvider` component allows rendering nested views/panels/menus - * (via the `NavigatorScreen` component and navigate between these different - * view (via the `NavigatorButton` and `NavigatorBackButton` components or the - * `useNavigator` hook). - * - * ```jsx - * import { - * __experimentalNavigatorProvider as NavigatorProvider, - * __experimentalNavigatorScreen as NavigatorScreen, - * __experimentalNavigatorButton as NavigatorButton, - * __experimentalNavigatorBackButton as NavigatorBackButton, - * } from '@wordpress/components'; - * - * const MyNavigation = () => ( - * - * - *

This is the home screen.

- * - * Navigate to child screen. - * - *
- * - * - *

This is the child screen.

- * - * Go back - * - *
- *
- * ); - * ``` - */ -export const NavigatorProvider = contextConnect( - UnconnectedNavigatorProvider, - 'NavigatorProvider' -); - -export default NavigatorProvider; +export const Navigator = contextConnect( UnconnectedNavigator, 'Navigator' ); diff --git a/packages/components/src/navigator/stories/index.story.tsx b/packages/components/src/navigator/stories/index.story.tsx index 8edac7e7b8168..e9e342bb0d2ee 100644 --- a/packages/components/src/navigator/stories/index.story.tsx +++ b/packages/components/src/navigator/stories/index.story.tsx @@ -8,20 +8,20 @@ import type { Meta, StoryObj } from '@storybook/react'; */ import Button from '../../button'; import { VStack } from '../../v-stack'; -import { - NavigatorProvider, - NavigatorScreen, - NavigatorButton, - NavigatorBackButton, - useNavigator, -} from '..'; import { HStack } from '../../h-stack'; - -const meta: Meta< typeof NavigatorProvider > = { - component: NavigatorProvider, - // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 - subcomponents: { NavigatorScreen, NavigatorButton, NavigatorBackButton }, - title: 'Components (Experimental)/Navigator', +import { Navigator, useNavigator } from '../'; + +const meta: Meta< typeof Navigator > = { + component: Navigator, + subcomponents: { + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + Screen: Navigator.Screen, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + Button: Navigator.Button, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + BackButton: Navigator.BackButton, + }, + title: 'Components/Navigator', argTypes: { as: { control: { type: null } }, children: { control: { type: null } }, @@ -40,10 +40,10 @@ const meta: Meta< typeof NavigatorProvider > = { * detail of the Navigator component. Do not use outside of * its source code. */ - [data-wp-component="NavigatorProvider"] { + [data-wp-component="Navigator"] { height: 250px; } - [data-wp-component="NavigatorScreen"]:not([data-sticky]) { + [data-wp-component="Navigator.Screen"] { padding: 8px; } ` } @@ -55,55 +55,55 @@ const meta: Meta< typeof NavigatorProvider > = { }; export default meta; -export const Default: StoryObj< typeof NavigatorProvider > = { +export const Default: StoryObj< typeof Navigator > = { args: { initialPath: '/', children: ( <> - +

This is the home screen.

- + Go to child screen. - + - + Go to dynamic path screen with id 1. - + - + Go to dynamic path screen with id 2. - + -
+ - +

This is the child screen.

- + Go back - + - Go to grand child screen. - + -
+ - +

This is the grand child screen.

- + Go back - -
+ + - + - + ), }, @@ -119,14 +119,14 @@ function DynamicScreen() { This screen can parse params dynamically. The current id is:{ ' ' } { params.id }

- + Go back - + ); } -export const WithNestedInitialPath: StoryObj< typeof NavigatorProvider > = { +export const WithNestedInitialPath: StoryObj< typeof Navigator > = { ...Default, args: { ...Default.args, @@ -138,7 +138,7 @@ const NavigatorButtonWithSkipFocus = ( { path, onClick, ...props -}: React.ComponentProps< typeof NavigatorButton > ) => { +}: React.ComponentProps< typeof Navigator.Button > ) => { const { goTo } = useNavigator(); return ( @@ -156,7 +156,7 @@ const NavigatorButtonWithSkipFocus = ( { ); }; -export const SkipFocus: StoryObj< typeof NavigatorProvider > = { +export const SkipFocus: StoryObj< typeof Navigator > = { args: { initialPath: '/', children: ( @@ -170,19 +170,19 @@ export const SkipFocus: StoryObj< typeof NavigatorProvider > = { display: 'contents', } } > - +

Home screen

- + Go to child screen. - -
+ + - +

Child screen

- + Go back to home screen - -
+ + diff --git a/packages/components/src/navigator/styles.ts b/packages/components/src/navigator/styles.ts index fd355bf4dd48f..167d4ac07de3d 100644 --- a/packages/components/src/navigator/styles.ts +++ b/packages/components/src/navigator/styles.ts @@ -3,7 +3,7 @@ */ import { css, keyframes } from '@emotion/react'; -export const navigatorProviderWrapper = css` +export const navigatorWrapper = css` position: relative; /* Prevents horizontal overflow while animating screen transitions */ overflow-x: clip; diff --git a/packages/components/src/navigator/test/index.tsx b/packages/components/src/navigator/test/index.tsx index 820942a22644b..cab6e9a4cdadf 100644 --- a/packages/components/src/navigator/test/index.tsx +++ b/packages/components/src/navigator/test/index.tsx @@ -14,14 +14,8 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import Button from '../../button'; -import { - NavigatorProvider, - NavigatorScreen, - NavigatorButton, - NavigatorBackButton, - NavigatorToParentButton, - useNavigator, -} from '..'; +import { Navigator, useNavigator } from '..'; +import { NavigatorToParentButton } from '../legacy'; import type { NavigateOptions } from '../types'; const INVALID_HTML_ATTRIBUTE = { @@ -76,11 +70,11 @@ function CustomNavigatorButton( { path, onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof NavigatorButton >, 'onClick' > & { +}: Omit< ComponentPropsWithoutRef< typeof Navigator.Button >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { return ( - { // Used to spy on the values passed to `navigator.goTo`. onClick?.( { type: 'goTo', path } ); @@ -95,7 +89,7 @@ function CustomNavigatorGoToBackButton( { path, onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof NavigatorButton >, 'onClick' > & { +}: Omit< ComponentPropsWithoutRef< typeof Navigator.Button >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { const { goTo } = useNavigator(); @@ -115,7 +109,7 @@ function CustomNavigatorGoToSkipFocusButton( { path, onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof NavigatorButton >, 'onClick' > & { +}: Omit< ComponentPropsWithoutRef< typeof Navigator.Button >, 'onClick' > & { onClick?: CustomTestOnClickHandler; } ) { const { goTo } = useNavigator(); @@ -134,11 +128,14 @@ function CustomNavigatorGoToSkipFocusButton( { function CustomNavigatorBackButton( { onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof NavigatorBackButton >, 'onClick' > & { +}: Omit< + ComponentPropsWithoutRef< typeof Navigator.BackButton >, + 'onClick' +> & { onClick?: CustomTestOnClickHandler; } ) { return ( - { // Used to spy on the values passed to `navigator.goBack`. onClick?.( { type: 'goBack' } ); @@ -151,7 +148,10 @@ function CustomNavigatorBackButton( { function CustomNavigatorToParentButton( { onClick, ...props -}: Omit< ComponentPropsWithoutRef< typeof NavigatorBackButton >, 'onClick' > & { +}: Omit< + ComponentPropsWithoutRef< typeof Navigator.BackButton >, + 'onClick' +> & { onClick?: CustomTestOnClickHandler; } ) { return ( @@ -194,13 +194,13 @@ const ProductScreen = ( { const { params } = useNavigator(); return ( - +

{ SCREEN_TEXT.product }

Product ID is { params.productId }

{ BUTTON_TEXT.back } -
+ ); }; @@ -215,8 +215,8 @@ const MyNavigation = ( { const [ outerInputValue, setOuterInputValue ] = useState( '' ); return ( <> - - + +

{ SCREEN_TEXT.home }

{ /* * A button useful to test focus restoration. This button is the first @@ -254,9 +254,9 @@ const MyNavigation = ( { > { BUTTON_TEXT.toInvalidHtmlPathScreen } -
+ - +

{ SCREEN_TEXT.child }

{ /* * A button useful to test focus restoration. This button is the first @@ -286,30 +286,30 @@ const MyNavigation = ( { } } value={ innerInputValue } /> -
+ - +

{ SCREEN_TEXT.nested }

{ BUTTON_TEXT.back } -
+ - +

{ SCREEN_TEXT.invalidHtmlPath }

{ BUTTON_TEXT.back } -
+ - { /* A `NavigatorScreen` with `path={ PATHS.NOT_FOUND }` is purposefully not included. */ } -
+ { /* A `Navigator.Screen` with `path={ PATHS.NOT_FOUND }` is purposefully not included. */ } + { return ( <> - - + +

{ SCREEN_TEXT.home }

{ /* * A button useful to test focus restoration. This button is the first @@ -349,9 +349,9 @@ const MyHierarchicalNavigation = ( { > { BUTTON_TEXT.toChildScreen } -
+ - +

{ SCREEN_TEXT.child }

{ /* * A button useful to test focus restoration. This button is the first @@ -370,9 +370,9 @@ const MyHierarchicalNavigation = ( { > { BUTTON_TEXT.back } -
+ - +

{ SCREEN_TEXT.nested }

{ BUTTON_TEXT.backUsingGoTo } -
+ { BUTTON_TEXT.goToWithSkipFocus } -
+ ); }; @@ -406,8 +406,8 @@ const MyDeprecatedNavigation = ( { } ) => { return ( <> - - + +

{ SCREEN_TEXT.home }

{ /* * A button useful to test focus restoration. This button is the first @@ -421,9 +421,9 @@ const MyDeprecatedNavigation = ( { > { BUTTON_TEXT.toChildScreen } -
+ - +

{ SCREEN_TEXT.child }

{ /* * A button useful to test focus restoration. This button is the first @@ -442,17 +442,17 @@ const MyDeprecatedNavigation = ( { > { BUTTON_TEXT.back } -
+ - +

{ SCREEN_TEXT.nested }

{ BUTTON_TEXT.back } -
-
+ + ); }; @@ -643,10 +643,10 @@ describe( 'Navigator', () => { } ); it( 'should warn if the `path` prop does not follow the required format', () => { - render( Test ); + render( Test ); expect( console ).toHaveWarnedWith( - 'wp.components.NavigatorScreen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' + 'wp.components.Navigator.Screen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.' ); } ); @@ -880,7 +880,7 @@ describe( 'Navigator', () => { // Rendering `NavigatorToParentButton` logs a deprecation notice expect( console ).toHaveWarnedWith( - 'wp.components.NavigatorToParentButton is deprecated since version 6.7. Please use wp.components.NavigatorBackButton instead.' + 'wp.components.NavigatorToParentButton is deprecated since version 6.7. Please use wp.components.Navigator.BackButton instead.' ); } ); diff --git a/packages/components/src/navigator/types.ts b/packages/components/src/navigator/types.ts index 855787b4d0a19..aeb5fd3b12c7f 100644 --- a/packages/components/src/navigator/types.ts +++ b/packages/components/src/navigator/types.ts @@ -86,7 +86,7 @@ export type NavigatorContext = Navigator & { match?: string; }; -export type NavigatorProviderProps = { +export type NavigatorProps = { /** * The initial active path. */ @@ -100,6 +100,24 @@ export type NavigatorProviderProps = { export type NavigatorScreenProps = { /** * The screen's path, matched against the current path stored in the navigator. + * + * `Navigator` assumes that screens are organized hierarchically according + * to their `path`, which should follow a URL-like scheme where each path + * segment starts with and is separated by the `/` character. + * + * `Navigator` will treat "back" navigations as going to the parent screen — + * it is, therefore, the responsibility of the consumer of the component to + * create the correct screen hierarchy. + * + * For example: + * - `/` is the root of all paths. There should always be a screen with + * `path="/"`; + * - `/parent/child` is a child of `/parent`; + * - `/parent/child/grand-child` is a child of `/parent/child`; + * - `/parent/:param` is a child of `/parent` as well; + * - if the current screen has a `path="/parent/child/grand-child"`, when + * going "back" `Navigator` will try to recursively navigate the path + * hierarchy until a matching screen (or the root `/`) is found. */ path: string; /** diff --git a/packages/components/src/navigator/use-navigator.ts b/packages/components/src/navigator/use-navigator.ts index 7ac35d73150d3..1ea99f3f1c857 100644 --- a/packages/components/src/navigator/use-navigator.ts +++ b/packages/components/src/navigator/use-navigator.ts @@ -10,7 +10,10 @@ import { NavigatorContext } from './context'; import type { Navigator } from './types'; /** - * Retrieves a `navigator` instance. + * Retrieves a `navigator` instance. This hook provides advanced functionality, + * such as imperatively navigating to a new location (with options like + * navigating back or skipping focus restoration) and accessing the current + * location and path parameters. */ export function useNavigator(): Navigator { const { location, params, goTo, goBack, goToParent } = diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index 0629a39728602..ffbe50ab3cc90 100644 --- a/packages/e2e-tests/plugins/block-bindings.php +++ b/packages/e2e-tests/plugins/block-bindings.php @@ -8,67 +8,158 @@ */ /** -* Register custom fields and custom block bindings sources. -*/ + * Code necessary for testing block bindings: + * - Enqueues a custom script to register sources in the client. + * - Registers sources in the server. + * - Registers a custom post type and custom fields. + */ function gutenberg_test_block_bindings_registration() { + // Define fields list. + $upload_dir = wp_upload_dir(); + $testing_url = $upload_dir['url'] . '/1024x768_e2e_test_image_size.jpeg'; + $fields_list = array( + 'text_field' => array( + 'label' => 'Text Field Label', + 'value' => 'Text Field Value', + ), + 'url_field' => array( + 'label' => 'URL Field Label', + 'value' => $testing_url, + ), + 'empty_field' => array( + 'label' => 'Empty Field Label', + 'value' => '', + ), + ); + + // Enqueue a custom script for the plugin. + wp_enqueue_script( + 'gutenberg-test-block-bindings', + plugins_url( 'block-bindings/index.js', __FILE__ ), + array( + 'wp-blocks', + 'wp-private-apis', + ), + filemtime( plugin_dir_path( __FILE__ ) . 'block-bindings/index.js' ), + true + ); + + // Pass data to the script. + wp_localize_script( + 'gutenberg-test-block-bindings', + 'testingBindings', + array( + 'fieldsList' => $fields_list, + ) + ); + // Register custom block bindings sources. register_block_bindings_source( - 'core/server-source', + 'testing/complete-source', + array( + 'label' => 'Complete Source', + 'get_value_callback' => function ( $source_args ) use ( $fields_list ) { + if ( ! isset( $source_args['key'] ) || ! isset( $fields_list[ $source_args['key'] ] ) ) { + return null; + } + return $fields_list[ $source_args['key'] ]['value']; }, + ) + ); + register_block_bindings_source( + 'testing/server-only-source', array( 'label' => 'Server Source', 'get_value_callback' => function () {}, ) ); - // Register custom fields. + // Register "movie" custom post type. + register_post_type( + 'movie', + array( + 'label' => 'Movie', + 'public' => true, + 'supports' => array( 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', 'page-attributes', 'thumbnail', 'custom-fields', 'post-formats' ), + 'has_archive' => true, + 'show_in_rest' => true, + ) + ); + + // Register global custom fields. register_meta( 'post', 'text_custom_field', array( + 'default' => 'Value of the text custom field', 'show_in_rest' => true, - 'type' => 'string', 'single' => true, - 'default' => 'Value of the text custom field', + 'type' => 'string', ) ); register_meta( 'post', 'url_custom_field', array( + 'default' => '#url-custom-field', 'show_in_rest' => true, - 'type' => 'string', 'single' => true, - 'default' => '#url-custom-field', + 'type' => 'string', ) ); + // Register CPT custom fields. register_meta( 'post', - 'empty_field', + 'movie_field', array( - 'show_in_rest' => true, - 'type' => 'string', - 'single' => true, - 'default' => '', + 'label' => 'Movie field label', + 'default' => 'Movie field default value', + 'object_subtype' => 'movie', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + register_meta( + 'post', + 'field_with_only_label', + array( + 'label' => 'Field with only label', + 'object_subtype' => 'movie', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + register_meta( + 'post', + 'field_without_label_or_default', + array( + 'object_subtype' => 'movie', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', ) ); register_meta( 'post', '_protected_field', array( - 'type' => 'string', - 'show_in_rest' => true, - 'single' => true, - 'default' => 'protected field value', + 'default' => 'Protected field value', + 'object_subtype' => 'movie', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', ) ); register_meta( 'post', 'show_in_rest_false_field', array( - 'show_in_rest' => false, - 'type' => 'string', - 'single' => true, - 'default' => 'show_in_rest false field value', + 'default' => 'show_in_rest false field value', + 'object_subtype' => 'movie', + 'show_in_rest' => false, + 'single' => true, + 'type' => 'string', ) ); } diff --git a/packages/e2e-tests/plugins/block-bindings/index.js b/packages/e2e-tests/plugins/block-bindings/index.js new file mode 100644 index 0000000000000..dfdc706b69c64 --- /dev/null +++ b/packages/e2e-tests/plugins/block-bindings/index.js @@ -0,0 +1,55 @@ +const { unlock } = + wp.privateApis.__dangerousOptInToUnstableAPIsOnlyForCoreModules( + 'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.', + '@wordpress/blocks' + ); + +const { registerBlockBindingsSource } = unlock( wp.blocks.privateApis ); +const { fieldsList } = window.testingBindings || {}; + +const getValues = ( { bindings } ) => { + const newValues = {}; + for ( const [ attributeName, source ] of Object.entries( bindings ) ) { + newValues[ attributeName ] = fieldsList[ source.args.key ]?.value; + } + return newValues; +}; +const setValues = ( { registry, bindings } ) => { + Object.values( bindings ).forEach( ( { args, newValue } ) => { + // Example of what could be done. + registry.dispatch( 'core' ).editEntityRecord( 'postType', 'post', 1, { + meta: { [ args?.key ]: newValue }, + } ); + } ); +}; + +registerBlockBindingsSource( { + name: 'testing/complete-source', + label: 'Complete Source', + getValues, + setValues, + canUserEditValue: () => true, + getFieldsList: () => fieldsList, +} ); + +registerBlockBindingsSource( { + name: 'testing/can-user-edit-false', + label: 'Can User Edit: False', + getValues, + setValues, + canUserEditValue: () => false, +} ); + +registerBlockBindingsSource( { + name: 'testing/can-user-edit-undefined', + label: 'Can User Edit: Undefined', + getValues, + setValues, +} ); + +registerBlockBindingsSource( { + name: 'testing/set-values-undefined', + label: 'Set Values: Undefined', + getValues, + canUserEditValue: () => true, +} ); diff --git a/packages/edit-post/src/store/actions.js b/packages/edit-post/src/store/actions.js index d00f7472382f8..1dc0401baf21c 100644 --- a/packages/edit-post/src/store/actions.js +++ b/packages/edit-post/src/store/actions.js @@ -8,7 +8,7 @@ import { privateApis as editorPrivateApis, } from '@wordpress/editor'; import deprecated from '@wordpress/deprecated'; -import { addFilter } from '@wordpress/hooks'; +import { addAction } from '@wordpress/hooks'; import { store as coreStore } from '@wordpress/core-data'; /** @@ -478,21 +478,14 @@ export const initializeMetaBoxes = metaBoxesInitialized = true; // Save metaboxes on save completion, except for autosaves. - addFilter( - 'editor.__unstableSavePost', + addAction( + 'editor.savePost', 'core/edit-post/save-metaboxes', - ( previous, options ) => - previous.then( () => { - if ( options.isAutosave ) { - return; - } - - if ( ! select.hasMetaBoxes() ) { - return; - } - - return dispatch.requestMetaBoxUpdates(); - } ) + async ( options ) => { + if ( ! options.isAutosave && select.hasMetaBoxes() ) { + await dispatch.requestMetaBoxUpdates(); + } + } ); dispatch( { diff --git a/packages/edit-post/src/store/selectors.js b/packages/edit-post/src/store/selectors.js index 5bea6e7d35eb6..8d85249e8100b 100644 --- a/packages/edit-post/src/store/selectors.js +++ b/packages/edit-post/src/store/selectors.js @@ -506,7 +506,7 @@ export const __experimentalGetInsertionPoint = createRegistrySelector( version: '6.7', } ); - return unlock( select( editorStore ) ).getInsertionPoint(); + return unlock( select( editorStore ) ).getInserter(); } ); 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 c55d6b188e1a2..ac1083e69abd7 100644 --- a/packages/edit-site/src/components/editor-canvas-container/index.js +++ b/packages/edit-site/src/components/editor-canvas-container/index.js @@ -125,7 +125,7 @@ function EditorCanvasContainer( { > { shouldShowCloseButton && (