diff --git a/.eslintrc.js b/.eslintrc.js index 729ce7d8574d6..eb5b2c6dccf20 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -44,125 +44,7 @@ const restrictedImports = [ }, { name: 'lodash', - importNames: [ - 'camelCase', - 'capitalize', - 'castArray', - 'chunk', - 'clamp', - 'clone', - 'cloneDeep', - 'compact', - 'concat', - 'countBy', - 'debounce', - 'deburr', - 'defaults', - 'defaultTo', - 'delay', - 'difference', - 'differenceWith', - 'dropRight', - 'each', - 'escape', - 'escapeRegExp', - 'every', - 'extend', - 'filter', - 'find', - 'findIndex', - 'findKey', - 'findLast', - 'first', - 'flatMap', - 'flatten', - 'flattenDeep', - 'flow', - 'flowRight', - 'forEach', - 'fromPairs', - 'get', - 'groupBy', - 'has', - 'identity', - 'includes', - 'invoke', - 'isArray', - 'isBoolean', - 'isEmpty', - 'isEqual', - 'isFinite', - 'isFunction', - 'isMatch', - 'isNil', - 'isNumber', - 'isObject', - 'isObjectLike', - 'isPlainObject', - 'isString', - 'isUndefined', - 'kebabCase', - 'keyBy', - 'keys', - 'last', - 'lowerCase', - 'map', - 'mapKeys', - 'mapValues', - 'maxBy', - 'memoize', - 'merge', - 'mergeWith', - 'negate', - 'noop', - 'nth', - 'omit', - 'omitBy', - 'once', - 'orderby', - 'overEvery', - 'partial', - 'partialRight', - 'pick', - 'pickBy', - 'random', - 'reduce', - 'reject', - 'repeat', - 'reverse', - 'set', - 'setWith', - 'size', - 'snakeCase', - 'some', - 'sortBy', - 'startCase', - 'startsWith', - 'stubFalse', - 'stubTrue', - 'sum', - 'sumBy', - 'take', - 'throttle', - 'times', - 'toString', - 'trim', - 'truncate', - 'unescape', - 'unionBy', - 'uniq', - 'uniqBy', - 'uniqueId', - 'uniqWith', - 'upperFirst', - 'values', - 'without', - 'words', - 'xor', - 'zip', - ], - message: - 'This Lodash method is not recommended. Please use native functionality instead. If using `memoize`, please use `memize` instead.', + message: 'Please use native functionality instead.', }, { name: 'reakit', diff --git a/.github/workflows/php-changes-detection.yml b/.github/workflows/php-changes-detection.yml new file mode 100644 index 0000000000000..be1e686483f5b --- /dev/null +++ b/.github/workflows/php-changes-detection.yml @@ -0,0 +1,100 @@ +name: OPTIONAL - Confirm if PHP changes require backporting to WordPress Core + +on: + pull_request: + types: [opened, synchronize] +jobs: + detect_php_changes: + name: Detect PHP changes + runs-on: ubuntu-latest + if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} + steps: + - name: Check out code + uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + with: + fetch-depth: 0 + + - name: Get changed PHP files + id: changed-files-php + uses: tj-actions/changed-files@v37 + with: + files: | + *.{php} + lib/** + phpunit/** + + - name: List all changed files + if: steps.changed-files-php.outputs.any_changed == 'true' + id: list-changed-php-files + run: | + echo "Changed files:" + formatted_change_list="" + for file in ${{ steps.changed-files-php.outputs.all_changed_files }}; do + echo "$file was changed" + formatted_change_list+="
:grey_question: $file" + done + formatted_change_list+="
" + echo "formatted_change_list=$formatted_change_list" >> $GITHUB_OUTPUT + + - name: Find Comment + uses: peter-evans/find-comment@v2 + id: find-comment + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: '' + + - name: Create comment + if: steps.find-comment.outputs.comment-id == '' && steps.changed-files-php.outputs.any_changed == 'true' + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + + This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress. + + If so, it is recommended to create a [new Trac ticket](https://core.trac.wordpress.org/newticket) and submit a pull request to the [WordPress Core Github repository](https://github.com/WordPress/wordpress-develop) soon after this pull request is merged. + + If you're unsure, you can always ask for help in the #core-editor channel in [WordPress Slack](https://make.wordpress.org/chat/). + + Thank you! :heart: + +
+ View changed files + ${{ steps.list-changed-php-files.outputs.formatted_change_list }} +
+ + - name: Update comment + if: steps.find-comment.outputs.comment-id != '' && steps.changed-files-php.outputs.any_changed == 'true' + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + + This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress. + + If so, it is recommended to create a [new Trac ticket](https://core.trac.wordpress.org/newticket) and submit a pull request to the [WordPress Core Github repository](https://github.com/WordPress/wordpress-develop) soon after this pull request is merged. + + If you're unsure, you can always ask for help in the #core-editor channel in [WordPress Slack](https://make.wordpress.org/chat/). + + Thank you! :heart: + +
+ View changed files + ${{ steps.list-changed-php-files.outputs.formatted_change_list }} +
+ + - name: Update comment + if: steps.find-comment.outputs.comment-id != '' && steps.changed-files-php.outputs.any_changed != 'true' + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + + This pull request changed or added PHP files in previous commits, but none have been detected in the latest commit. + + Thank you! :heart: diff --git a/.github/workflows/stale-issue-gardening.yml b/.github/workflows/stale-issue-gardening.yml index 078108a182850..da823198ed806 100644 --- a/.github/workflows/stale-issue-gardening.yml +++ b/.github/workflows/stale-issue-gardening.yml @@ -38,7 +38,7 @@ jobs: days-before-stale: 180 days-before-close: -1 remove-stale-when-updated: false - stale-issue-label: 'Needs Testing' + stale-issue-label: 'Needs check-in' steps: - name: Update issues diff --git a/bin/cherry-pick.mjs b/bin/cherry-pick.mjs index 36829db6ee5cf..baf8d42962f8e 100644 --- a/bin/cherry-pick.mjs +++ b/bin/cherry-pick.mjs @@ -114,12 +114,25 @@ async function fetchPRs() { const { items } = await GitHubFetch( `/search/issues?q=is:pr state:closed sort:updated label:"${ LABEL }" repo:WordPress/gutenberg` ); - const PRs = items.map( ( { id, number, title } ) => ( { + const PRs = items.map( ( { id, number, title, pull_request, closed_at } ) => ( { id, number, title, - } ) ); - console.log( 'Found the following PRs to cherry-pick: ' ); + closed_at, + pull_request, + } ) ).sort( ( a, b ) => { + /* + * `closed_at` and `pull_request.merged_at` are _usually_ the same, + * but let's prefer the latter if it's available. + */ + if ( a?.pull_request?.merged_at && b?.pull_request?.merged_at ) { + return new Date( a?.pull_request?.merged_at ) - new Date( b?.pull_request?.merged_at ); + } + return new Date( a.closed_at ) - new Date( b.closed_at ); + } ); + + + console.log( 'Found the following PRs to cherry-pick (sorted by closed date in ascending order): ' ); PRs.forEach( ( { number, title } ) => console.log( indent( `#${ number } – ${ title }` ) ) ); diff --git a/bin/test-create-block.sh b/bin/test-create-block.sh index e17bdbb2d6694..7959334a8e30e 100755 --- a/bin/test-create-block.sh +++ b/bin/test-create-block.sh @@ -55,7 +55,7 @@ if [ "$expected" -ne "$actual" ]; then error "Expected $expected files in the project root, but found $actual." exit 1 fi -expected=6 +expected=7 actual=$( find src -maxdepth 1 -type f | wc -l ) if [ "$expected" -ne "$actual" ]; then error "Expected $expected files in the \`src\` directory, but found $actual." @@ -69,7 +69,7 @@ status "Building block..." ../node_modules/.bin/wp-scripts build status "Verifying build..." -expected=5 +expected=7 actual=$( find build -maxdepth 1 -type f | wc -l ) if [ "$expected" -ne "$actual" ]; then error "Expected $expected files in the \`build\` directory, but found $actual." diff --git a/docs/manifest.json b/docs/manifest.json index aaadd15a57079..ec55132cd7d4b 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1505,6 +1505,12 @@ "markdown_source": "../packages/core-data/README.md", "parent": "packages" }, + { + "title": "@wordpress/create-block-interactive-template", + "slug": "packages-create-block-interactive-template", + "markdown_source": "../packages/create-block-interactive-template/README.md", + "parent": "packages" + }, { "title": "@wordpress/create-block-tutorial-template", "slug": "packages-create-block-tutorial-template", diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md index 7e68e18eb14bf..0995f4d86cfd7 100644 --- a/docs/reference-guides/block-api/block-supports.md +++ b/docs/reference-guides/block-api/block-supports.md @@ -543,6 +543,79 @@ supports: { } ``` +## layout + +- Type: `boolean` or `Object` +- Default value: null +- Subproperties: + - `default`: type `Object`, default value null + - `allowSwitching`: type `boolean`, default value `false` + - `allowEditing`: type `boolean`, default value `true` + - `allowInheriting`: type `boolean`, default value `true` + - `allowSizingOnChildren`: type `boolean`, default value `false` + - `allowVerticalAlignment`: type `boolean`, default value `true` + - `allowJustification`: type `boolean`, default value `true` + - `allowOrientation`: type `boolean`, default value `true` + +This value only applies to blocks that are containers for inner blocks. If set to `true` the layout type will be `flow`. For other layout types it's necessary to set the `type` explicitly inside the `default` object. + +### layout.default + +- Type: `Object` +- Default value: null + +Allows setting the `type` property to define what layout type is default for the block, and also default values for any properties inherent to that layout type, e.g., for a `flex` layout, a default value can be set for `flexWrap`. + +### layout.allowSwitching + +- Type: `boolean` +- Default value: `false` + +Exposes a switcher control that allows toggling between all existing layout types. + +### layout.allowEditing + +- Type: `boolean` +- Default value: `true` + +Determines display of layout controls in the block sidebar. If set to false, layout controls will be hidden. + +### layout.allowInheriting + +- Type: `boolean` +- Default value: `true` + +For the `flow` layout type only, determines display of the "Inner blocks use content width" toggle. + +### layout.allowSizingOnChildren + +- Type: `boolean` +- Default value: `false` + +For the `flex` layout type only, determines display of sizing controls (Fit/Fill/Fixed) on all child blocks of the flex block. + +### layout.allowVerticalAlignment + +- Type: `boolean` +- Default value: `true` + +For the `flex` layout type only, determines display of the vertical alignment control in the block toolbar. + +### layout.allowJustification + +- Type: `boolean` +- Default value: `true` + +For the `flex` layout type, determines display of the justification control in the block toolbar and block sidebar. For the `constrained` layout type, determines display of justification control in the block sidebar. + +### layout.allowOrientation + +- Type: `boolean` +- Default value: `true` + +For the `flex` layout type only, determines display of the orientation control in the block toolbar. + + ## multiple - Type: `boolean` diff --git a/docs/reference-guides/filters/block-filters.md b/docs/reference-guides/filters/block-filters.md index e41215d67065a..66e89b0a1d614 100644 --- a/docs/reference-guides/filters/block-filters.md +++ b/docs/reference-guides/filters/block-filters.md @@ -63,11 +63,13 @@ function addListBlockClassName( settings, name ) { return settings; } - return lodash.assign( {}, settings, { - supports: lodash.assign( {}, settings.supports, { + return { + ...settings, + supports: { + ...settings.supports, className: true, - } ), - } ); + }, + }; } wp.hooks.addFilter( @@ -126,7 +128,10 @@ Adding a background by default to all blocks. ```js function addBackgroundColorStyle( props ) { - return lodash.assign( props, { style: { backgroundColor: 'red' } } ); + return { + ...props, + style: { backgroundColor: 'red' }, + }; } wp.hooks.addFilter( @@ -276,9 +281,10 @@ var withClientIdClassName = wp.compose.createHigherOrderComponent( function ( BlockListBlock ) { return function ( props ) { - var newProps = lodash.assign( {}, props, { + var newProps = { + ...props, className: 'block-' + props.clientId, - } ); + }; return el( BlockListBlock, newProps ); }; diff --git a/package-lock.json b/package-lock.json index 6db4ee9ee666a..42ace4e6e0d7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,24 +101,24 @@ "dev": true }, "@ariakit/core": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.2.6.tgz", - "integrity": "sha512-83r2YmLvHLsV2NoclM5sfpLXfJ9S3R4lQIZK5Iad/KdfuFolvtVKPVrLW9OGoD1D4OuLxO1PgYKZEDPH0a1TjQ==" + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.2.7.tgz", + "integrity": "sha512-Hs0N1EMYq88WW4v9xnSIHNR38TvbQuoUX6FYFmeLCZSTIXQBiET7lr1DQXwOOmdEtRtlxQ5HsxbTkxeOkPv+eg==" }, "@ariakit/react": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.2.10.tgz", - "integrity": "sha512-T0ftSgAuEXzA5MvurSWALfJBhTHzEgkXTDWEBTOkSzR5nxilPU/80UgA7dKHi4SGA3wUXIIMjRb42Djk3Qi9pQ==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.2.12.tgz", + "integrity": "sha512-4rAgMyUURHW78EKgRCanhyRUtsiYCOxO65BBHF4mg3tZsDeOvu9kBG5IAXX8mUgakTcyr0EKXuOtGThaj7gobA==", "requires": { - "@ariakit/react-core": "0.2.10" + "@ariakit/react-core": "0.2.12" } }, "@ariakit/react-core": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.2.10.tgz", - "integrity": "sha512-/MBX9ToIBQUR//uaOs1XzLz+Zq7ECMQmr670mXiDg3L9bu0siQKP3vD2Fl8RDRWMEMOk6+0Utr3Fm49hYlg24g==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.2.12.tgz", + "integrity": "sha512-3KSKlX10nnhCvjsbPW0CAnqG+6grryfwnMkeJJ/h34FSV7hEfUMexmIjKBVZyfBG08Xj8NjSK8kkx9c3ChkXeA==", "requires": { - "@ariakit/core": "0.2.6", + "@ariakit/core": "0.2.7", "@floating-ui/dom": "^1.0.0", "use-sync-external-store": "^1.2.0" } @@ -17419,7 +17419,7 @@ "@wordpress/components": { "version": "file:packages/components", "requires": { - "@ariakit/react": "^0.2.10", + "@ariakit/react": "^0.2.12", "@babel/runtime": "^7.16.0", "@emotion/cache": "^11.7.1", "@emotion/css": "^11.7.1", @@ -18572,6 +18572,7 @@ "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", "@wordpress/notices": "file:packages/notices", + "@wordpress/private-apis": "^0.19.0", "@wordpress/url": "file:packages/url" } }, @@ -25919,7 +25920,7 @@ "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", "dev": true }, "array-includes": { @@ -31074,7 +31075,7 @@ "debuglog": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", "dev": true }, "decache": { @@ -35920,7 +35921,7 @@ "git-remote-origin-url": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", "dev": true, "requires": { "gitconfiglocal": "^1.0.0", @@ -35967,7 +35968,7 @@ "gitconfiglocal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", "dev": true, "requires": { "ini": "^1.3.2" @@ -37215,7 +37216,7 @@ "humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "dev": true, "requires": { "ms": "^2.0.0" @@ -38231,7 +38232,7 @@ "is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", "dev": true, "requires": { "text-extensions": "^1.0.0" @@ -40004,7 +40005,7 @@ "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, "jsprim": { @@ -41110,7 +41111,7 @@ "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "dev": true }, "lodash.isplainobject": { @@ -48716,7 +48717,7 @@ "promzard": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz", - "integrity": "sha512-JZeYqd7UAcHCwI+sTOeUDYkvEU+1bQ7iE0UT1MgB/tERkAPkesW46MrpIySzODi+owTjZtiF8Ay5j9m60KmMBw==", + "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", "dev": true, "requires": { "read": "1" @@ -48750,7 +48751,7 @@ "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, "protocols": { @@ -50367,7 +50368,7 @@ "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", "dev": true, "requires": { "mute-stream": "~0.0.4" @@ -55757,7 +55758,7 @@ "temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", - "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", "dev": true }, "terminal-link": { diff --git a/package.json b/package.json index 0ab7e1932eba7..5032f589b592f 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,6 @@ "@types/estree": "0.0.50", "@types/highlight-words-core": "1.2.1", "@types/istanbul-lib-report": "3.0.0", - "@types/lodash": "4.14.172", "@types/mime": "2.0.3", "@types/npm-package-arg": "6.1.1", "@types/prettier": "2.4.4", @@ -207,7 +206,6 @@ "jest-watch-typeahead": "2.2.2", "lerna": "5.5.2", "lint-staged": "10.0.1", - "lodash": "4.17.21", "make-dir": "3.0.0", "metro-react-native-babel-preset": "0.70.3", "metro-react-native-babel-transformer": "0.70.3", diff --git a/packages/README.md b/packages/README.md index ac34216efcd13..c2a32cf068ac3 100644 --- a/packages/README.md +++ b/packages/README.md @@ -75,10 +75,10 @@ The simplest way to add a production dependency to one of the packages is to run _Example:_ ```bash -lerna add lodash packages/a11y +lerna add change-case packages/a11y ``` -This command adds the latest version of `lodash` as a dependency to the `@wordpress/a11y` package, which is located in `packages/a11y` folder. +This command adds the latest version of `change-case` as a dependency to the `@wordpress/a11y` package, which is located in `packages/a11y` folder. #### Removing Existing Dependencies diff --git a/packages/base-styles/_colors.native.scss b/packages/base-styles/_colors.native.scss index c21de46e6a459..ef4443c38c0e2 100644 --- a/packages/base-styles/_colors.native.scss +++ b/packages/base-styles/_colors.native.scss @@ -102,6 +102,7 @@ $dark-ultra-dim: #ffffff14; //rgba(255, 255, 255, 0.08); $app-background: $white; $app-background-dark: $black; $app-background-dark-alt: $background-dark-elevated; +$app-safe-area-background-dark: #141414; $modal-background: $white; $modal-background-dark: $background-dark-elevated; diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 937bfea2f4965..2c42b42afc442 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -680,10 +680,6 @@ _Related_ Private @wordpress/block-editor APIs. -### ReusableBlocksRenameHint - -Undocumented declaration. - ### RichText _Related_ diff --git a/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap b/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap index db322ec2af840..21445e691f428 100644 --- a/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap +++ b/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap @@ -49,6 +49,9 @@ exports[`Block Mover Picker should render without crashing and match snapshot 1` { "height": 44, }, + { + "borderLeftWidth": 0.5, + }, undefined, ] } @@ -108,7 +111,6 @@ exports[`Block Mover Picker should render without crashing and match snapshot 1` } > { - return ( - - - - ); -}; +const { Fill, Slot } = createSlotFill( 'SettingsToolbarButton' ); -export default SettingsButton; +const SettingsButton = ( { openGeneralSidebar } ) => ( + +); + +const SettingsButtonFill = ( props ) => ( + + + +); + +const SettingsToolbarButton = withDispatch( ( dispatch ) => { + const { openGeneralSidebar } = dispatch( 'core/edit-post' ); + + return { + openGeneralSidebar: () => openGeneralSidebar( 'edit-post/block' ), + }; +} )( SettingsButtonFill ); + +SettingsToolbarButton.Slot = Slot; + +export default SettingsToolbarButton; diff --git a/packages/block-editor/src/components/block-toolbar/index.native.js b/packages/block-editor/src/components/block-toolbar/index.native.js index 32233fa54a1c1..bdf4c778ebc15 100644 --- a/packages/block-editor/src/components/block-toolbar/index.native.js +++ b/packages/block-editor/src/components/block-toolbar/index.native.js @@ -20,7 +20,7 @@ const REMOVE_EMPY_PARENT_BLOCKS = [ 'core/social-links', ]; -export default function BlockToolbar( { anchorNodeRef, onOpenBlockSettings } ) { +export default function BlockToolbar( { anchorNodeRef } ) { const { rootClientId, blockClientId, @@ -93,9 +93,15 @@ export default function BlockToolbar( { anchorNodeRef, onOpenBlockSettings } ) { <> { isValidAndVisual && ( <> - + + { /* Render only one settings icon even if we have more than one fill - need for hooks with controls. */ } + { ( fills = [ null ] ) => { + if ( ! fills?.length > 0 ) { + return null; + } + return fills[ 0 ]; + } } + diff --git a/packages/block-editor/src/components/block-toolbar/test/index.native.js b/packages/block-editor/src/components/block-toolbar/test/index.native.js new file mode 100644 index 0000000000000..473e1a40bb966 --- /dev/null +++ b/packages/block-editor/src/components/block-toolbar/test/index.native.js @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import { + addBlock, + fireEvent, + getBlock, + initializeEditor, + setupCoreBlocks, +} from 'test/helpers'; + +setupCoreBlocks(); + +describe( 'Block Toolbar', () => { + it( "doesn't render the block settings button if there aren't any settings for the current selected block", async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Image' ); + + // Act + fireEvent( + screen.getByTestId( 'media-options-picker' ), + 'backdropPress' + ); + + // Assert + expect( screen.queryByLabelText( 'Open Settings' ) ).toBeNull(); + } ); + + it( 'renders the block settings button for the current selected block', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = await getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + + // Assert + expect( screen.queryByLabelText( 'Open Settings' ) ).toBeVisible(); + } ); +} ); diff --git a/packages/block-editor/src/components/colors-gradients/control.js b/packages/block-editor/src/components/colors-gradients/control.js index 4277e7f68b2b6..14fb9841e5f92 100644 --- a/packages/block-editor/src/components/colors-gradients/control.js +++ b/packages/block-editor/src/components/colors-gradients/control.js @@ -6,6 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +import { __ } from '@wordpress/i18n'; import { BaseControl, __experimentalVStack as VStack, @@ -28,12 +29,12 @@ const colorsAndGradientKeys = [ const TAB_COLOR = { name: 'color', - title: 'Solid', + title: __( 'Solid' ), value: 'color', }; const TAB_GRADIENT = { name: 'gradient', - title: 'Gradient', + title: __( 'Gradient' ), value: 'gradient', }; diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index afe403af0aa39..382d71c29071e 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -88,11 +88,14 @@ function Iframe( { forwardedRef: ref, ...props } ) { - const { styles = '', scripts = '' } = useSelect( - ( select ) => - select( blockEditorStore ).getSettings().__unstableResolvedAssets, - [] - ); + const { resolvedAssets, isPreviewMode } = useSelect( ( select ) => { + const settings = select( blockEditorStore ).getSettings(); + return { + resolvedAssets: settings.__unstableResolvedAssets, + isPreviewMode: settings.__unstableIsPreviewMode, + }; + }, [] ); + const { styles = '', scripts = '' } = resolvedAssets; const [ iframeDocument, setIframeDocument ] = useState(); const [ bodyClasses, setBodyClasses ] = useState( [] ); const compatStyles = useCompatibilityStyles(); @@ -140,11 +143,13 @@ function Iframe( { compatStyle.cloneNode( true ) ); - // eslint-disable-next-line no-console - console.warn( - `${ compatStyle.id } was added to the iframe incorrectly. Please use block.json or enqueue_block_assets to add styles to the iframe.`, - compatStyle - ); + if ( ! isPreviewMode ) { + // eslint-disable-next-line no-console + console.warn( + `${ compatStyle.id } was added to the iframe incorrectly. Please use block.json or enqueue_block_assets to add styles to the iframe.`, + compatStyle + ); + } } iFrameDocument.addEventListener( diff --git a/packages/block-editor/src/components/iframe/use-compatibility-styles.js b/packages/block-editor/src/components/iframe/use-compatibility-styles.js index 4b250175d73d3..eb738c7ebefdf 100644 --- a/packages/block-editor/src/components/iframe/use-compatibility-styles.js +++ b/packages/block-editor/src/components/iframe/use-compatibility-styles.js @@ -45,6 +45,11 @@ export function useCompatibilityStyles() { return accumulator; } + // Don't try to add styles without ID. Styles enqueued via the WP dependency system will always have IDs. + if ( ! ownerNode.id ) { + return accumulator; + } + function matchFromRules( _cssRules ) { return Array.from( _cssRules ).find( ( { diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index db93f112a366d..5876eb4ec01e9 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -165,8 +165,3 @@ export { default as __experimentalInspectorPopoverHeader } from './inspector-pop export { default as BlockEditorProvider } from './provider'; export { default as useSetting } from './use-setting'; - -/* - * The following rename hint component can be removed in 6.4. - */ -export { default as ReusableBlocksRenameHint } from './inserter/reusable-block-rename-hint'; diff --git a/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js index d4702eb137283..6a3a3d1eec260 100644 --- a/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js +++ b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js @@ -10,7 +10,24 @@ import { close } from '@wordpress/icons'; import { store as preferencesStore } from '@wordpress/preferences'; const PREFERENCE_NAME = 'isResuableBlocksrRenameHintVisible'; +/* + * This hook was added in 6.3 to help users with the transition from Reusable blocks to Patterns. + * It is only exported for use in the reusable-blocks package as well as block-editor. + * It will be removed in 6.4. and should not be used in any new code. + */ +export function useReusableBlocksRenameHint() { + return useSelect( + ( select ) => + select( preferencesStore ).get( 'core', PREFERENCE_NAME ) ?? true, + [] + ); +} +/* + * This component was added in 6.3 to help users with the transition from Reusable blocks to Patterns. + * It is only exported for use in the reusable-blocks package as well as block-editor. + * It will be removed in 6.4. and should not be used in any new code. + */ export default function ReusableBlocksRenameHint() { const isReusableBlocksRenameHint = useSelect( ( select ) => diff --git a/packages/block-editor/src/components/inspector-controls/fill.native.js b/packages/block-editor/src/components/inspector-controls/fill.native.js index f4cd288c6913c..d38d865cd15cc 100644 --- a/packages/block-editor/src/components/inspector-controls/fill.native.js +++ b/packages/block-editor/src/components/inspector-controls/fill.native.js @@ -6,6 +6,7 @@ import { View } from 'react-native'; /** * WordPress dependencies */ +import { Children } from '@wordpress/element'; import { BottomSheetConsumer } from '@wordpress/components'; import warning from '@wordpress/warning'; import deprecated from '@wordpress/deprecated'; @@ -15,6 +16,7 @@ import deprecated from '@wordpress/deprecated'; */ import groups from './groups'; import useDisplayBlockControls from '../use-display-block-controls'; +import { BlockSettingsButton } from '../block-settings'; export default function InspectorControlsFill( { children, @@ -53,6 +55,7 @@ export default function InspectorControlsFill( { } + { Children.count( children ) > 0 && } ); } diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss index 565b6b05957f5..552b8e4e2f54e 100644 --- a/packages/block-editor/src/components/link-control/style.scss +++ b/packages/block-editor/src/components/link-control/style.scss @@ -139,6 +139,7 @@ $preview-image-height: 140px; // Inline block required to preserve white space // between `` elements and text nodes. display: inline-block; + width: 100%; mark { font-weight: 600; diff --git a/packages/block-editor/src/components/rich-text/get-rich-text-values.js b/packages/block-editor/src/components/rich-text/get-rich-text-values.js index 4ecee9b76530e..9af2eb054cf6c 100644 --- a/packages/block-editor/src/components/rich-text/get-rich-text-values.js +++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js @@ -78,10 +78,20 @@ function addValuesForElements( children, ...args ) { } } +function _getSaveElement( name, attributes, innerBlocks ) { + return getSaveElement( + name, + attributes, + innerBlocks.map( ( block ) => + _getSaveElement( block.name, block.attributes, block.innerBlocks ) + ) + ); +} + function addValuesForBlocks( values, blocks ) { for ( let i = 0; i < blocks.length; i++ ) { const { name, attributes, innerBlocks } = blocks[ i ]; - const saveElement = getSaveElement( name, attributes ); + const saveElement = _getSaveElement( name, attributes, innerBlocks ); addValuesForElement( saveElement, values, innerBlocks ); } } diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 453fbd7ce63eb..20aeaa2a79040 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -19,6 +19,10 @@ import { BlockRemovalWarningModal } from './components/block-removal-warning-mod import { useLayoutClasses, useLayoutStyles } from './hooks'; import DimensionsTool from './components/dimensions-tool'; import ResolutionTool from './components/resolution-tool'; +import { + default as ReusableBlocksRenameHint, + useReusableBlocksRenameHint, +} from './components/inserter/reusable-block-rename-hint'; /** * Private @wordpress/block-editor APIs. @@ -43,4 +47,6 @@ lock( privateApis, { useLayoutStyles, DimensionsTool, ResolutionTool, + ReusableBlocksRenameHint, + useReusableBlocksRenameHint, } ); diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index b4750e9e261be..245aaf7adb0fd 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1855,7 +1855,6 @@ export function lastBlockInserted( state = {}, action ) { switch ( action.type ) { case 'INSERT_BLOCKS': case 'REPLACE_BLOCKS': - case 'REPLACE_INNER_BLOCKS': if ( ! action.blocks.length ) { return state; } diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index c06936047f832..9d886f9aa37bb 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -3328,25 +3328,32 @@ describe( 'state', () => { expect( state.clientIds ).toEqual( [ clientIdOne, clientIdTwo ] ); } ); - it( 'should return client ids of all blocks when inner blocks are replaced with REPLACE_INNER_BLOCKS', () => { - const clientIdOne = '62bfef6e-d5e9-43ba-b7f9-c77cf354141f'; - const clientIdTwo = '9db792c6-a25a-495d-adbd-97d56a4c4189'; + it( 'should return client ids of the original blocks when inner blocks are replaced with REPLACE_INNER_BLOCKS', () => { + const initialBlocks = deepFreeze( [ + '62bfef6e-d5e9-43ba-b7f9-c77cf354141f', + '9db792c6-a25a-495d-adbd-97d56a4c4189', + ] ); const action = { blocks: [ { - clientId: clientIdOne, + clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1', }, { - clientId: clientIdTwo, + clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1', }, ], type: 'REPLACE_INNER_BLOCKS', }; - const state = lastBlockInserted( {}, action ); + const state = lastBlockInserted( + { + clientIds: initialBlocks, + }, + action + ); - expect( state.clientIds ).toEqual( [ clientIdOne, clientIdTwo ] ); + expect( state.clientIds ).toEqual( initialBlocks ); } ); it( 'should return empty state if last block inserted is called with action RESET_BLOCKS', () => { diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php index 8ce35f3b766ea..5da11ffc883d2 100644 --- a/packages/block-library/src/search/index.php +++ b/packages/block-library/src/search/index.php @@ -9,10 +9,10 @@ * Dynamically renders the `core/search` block. * * @param array $attributes The block attributes. - * @param string $content The block content. + * @param string $content The saved content. * @param WP_Block $block The parsed block. * - * @return string Returns the block content. + * @return string The search block markup. */ function render_block_core_search( $attributes, $content, $block ) { // Older versions of the Search block defaulted the label and buttonText @@ -67,34 +67,32 @@ function render_block_core_search( $attributes, $content, $block ) { if ( ! empty( $typography_classes ) ) { $input_classes[] = $typography_classes; } - - $view_js_file = 'wp-block-search-view'; - wp_script_add_data( $view_js_file, 'strategy', 'async' ); // TODO: This should be able to be specified in block.json. See Core-54018. - $should_load_view_script = false; - if ( $input->next_tag() ) { $input->add_class( implode( ' ', $input_classes ) ); $input->set_attribute( 'id', $input_id ); $input->set_attribute( 'value', get_search_query() ); $input->set_attribute( 'placeholder', $attributes['placeholder'] ); - if ( 'button-only' === $button_position && 'expand-searchfield' === $button_behavior ) { + + $is_expandable_searchfield = 'button-only' === $button_position && 'expand-searchfield' === $button_behavior; + if ( $is_expandable_searchfield ) { $input->set_attribute( 'aria-hidden', 'true' ); $input->set_attribute( 'tabindex', '-1' ); - $should_load_view_script = true; } - } - // If the script already exists, there is no point in removing it from viewScript. - if ( ! wp_script_is( $view_js_file ) ) { - $script_handles = $block->block_type->view_script_handles; + // If the script already exists, there is no point in removing it from viewScript. + $view_js_file = 'wp-block-search-view'; + wp_script_add_data( $view_js_file, 'strategy', 'async' ); // TODO: This should be able to be specified in block.json. See Core-54018. + if ( ! wp_script_is( $view_js_file ) ) { + $script_handles = $block->block_type->view_script_handles; - // If the script is not needed, and it is still in the `view_script_handles`, remove it. - if ( ! $should_load_view_script && in_array( $view_js_file, $script_handles, true ) ) { - $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) ); - } - // If the script is needed, but it was previously removed, add it again. - if ( $should_load_view_script && ! in_array( $view_js_file, $script_handles, true ) ) { - $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) ); + // If the script is not needed, and it is still in the `view_script_handles`, remove it. + if ( ! $is_expandable_searchfield && in_array( $view_js_file, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) ); + } + // If the script is needed, but it was previously removed, add it again. + if ( $is_expandable_searchfield && ! in_array( $view_js_file, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) ); + } } } diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index 84e3a985ec441..1066aa0141915 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -18,12 +18,11 @@ function render_block_core_template_part( $attributes ) { $template_part_id = null; $content = null; $area = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; - $stylesheet = get_stylesheet(); if ( isset( $attributes['slug'] ) && isset( $attributes['theme'] ) && - $stylesheet === $attributes['theme'] + get_stylesheet() === $attributes['theme'] ) { $template_part_id = $attributes['theme'] . '//' . $attributes['slug']; $template_part_query = new WP_Query( @@ -64,22 +63,17 @@ function render_block_core_template_part( $attributes ) { */ do_action( 'render_block_core_template_part_post', $template_part_id, $attributes, $template_part_post, $content ); } else { + $template_part_file_path = ''; // Else, if the template part was provided by the active theme, // render the corresponding file content. if ( 0 === validate_file( $attributes['slug'] ) ) { - $themes = array( $stylesheet ); - $template = get_template(); - if ( $stylesheet !== $template ) { - $themes[] = $template; - } - - foreach ( $themes as $theme ) { - $theme_folders = get_block_theme_folders( $theme ); - $template_part_file_path = get_theme_file_path( '/' . $theme_folders['wp_template_part'] . '/' . $attributes['slug'] . '.html' ); - if ( file_exists( $template_part_file_path ) ) { - $content = (string) file_get_contents( $template_part_file_path ); - $content = '' !== $content ? _inject_theme_attribute_in_block_template_content( $content ) : ''; - break; + $block_template_file = _get_block_template_file( 'wp_template_part', $attributes['slug'] ); + if ( $block_template_file ) { + $template_part_file_path = $block_template_file['path']; + $content = (string) file_get_contents( $template_part_file_path ); + $content = '' !== $content ? _inject_theme_attribute_in_block_template_content( $content ) : ''; + if ( isset( $block_template_file['area'] ) ) { + $area = $block_template_file['area']; } } } diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 1e7e654ac1c81..8f5d2d946fa38 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -11,6 +11,7 @@ - `Popover`: Pin `react-dropdown-menu` version to avoid breaking changes in dependency updates. ([#52356](https://github.com/WordPress/gutenberg/pull/52356)). - `Item`: Unify focus style and add default font styles. ([#52495](https://github.com/WordPress/gutenberg/pull/52495)). +- `Toolbar`: Fix toolbar items not being tabbable on the first render. ([#52613](https://github.com/WordPress/gutenberg/pull/52613)) ## 25.3.0 (2023-07-05) diff --git a/packages/components/package.json b/packages/components/package.json index 74220c11013e2..455d3daa3c089 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -30,7 +30,7 @@ ], "types": "build-types", "dependencies": { - "@ariakit/react": "^0.2.10", + "@ariakit/react": "^0.2.12", "@babel/runtime": "^7.16.0", "@emotion/cache": "^11.7.1", "@emotion/css": "^11.7.1", diff --git a/packages/components/src/button/index.native.js b/packages/components/src/button/index.native.js index 9643f442d28c6..95ca9d36b3791 100644 --- a/packages/components/src/button/index.native.js +++ b/packages/components/src/button/index.native.js @@ -24,6 +24,7 @@ import { */ import Tooltip from '../tooltip'; import Icon from '../icon'; +import style from './style.scss'; const isAndroid = Platform.OS === 'android'; const marginBottom = isAndroid ? -0.5 : 0; @@ -51,8 +52,6 @@ const styles = StyleSheet.create( { justifyContent: 'center', alignItems: 'center', borderRadius: 6, - borderColor: '#2e4453', - backgroundColor: '#2e4453', }, subscriptInactive: { color: '#7b9ab1', // $toolbar-button. @@ -106,10 +105,16 @@ export function Button( props ) { customContainerStyles && { ...customContainerStyles }, ]; + const buttonActiveColorStyles = usePreferredColorSchemeStyle( + style[ 'components-button-light--active' ], + style[ 'components-button-dark--active' ] + ); + const buttonViewStyle = { opacity: isDisabled ? 0.3 : 1, ...( fixedRatio && styles.fixedRatio ), ...( isPressed ? styles.buttonActive : styles.buttonInactive ), + ...( isPressed ? buttonActiveColorStyles : {} ), ...( isPressed && isActiveStyle?.borderRadius && { borderRadius: isActiveStyle.borderRadius, @@ -159,7 +164,6 @@ export function Button( props ) { const newIcon = icon ? cloneElement( , { - colorScheme: preferredColorScheme, isPressed, } ) : null; diff --git a/packages/components/src/button/style.native.scss b/packages/components/src/button/style.native.scss new file mode 100644 index 0000000000000..ab90f8ec64e82 --- /dev/null +++ b/packages/components/src/button/style.native.scss @@ -0,0 +1,9 @@ +.components-button-light--active { + border-color: $light-dim; + background-color: $light-dim; +} + +.components-button-dark--active { + border-color: $dark-quaternary; + background-color: $dark-quaternary; +} diff --git a/packages/components/src/placeholder/style.scss b/packages/components/src/placeholder/style.scss index 7fd9977e4645d..df06969852fda 100644 --- a/packages/components/src/placeholder/style.scss +++ b/packages/components/src/placeholder/style.scss @@ -161,6 +161,11 @@ padding: 0 $grid-unit-10 2px; } } + .components-placeholder__learn-more { + .components-external-link { + color: var(--wp-admin-theme-color); + } + } } diff --git a/packages/components/src/toolbar/toolbar-group/style.native.scss b/packages/components/src/toolbar/toolbar-group/style.native.scss index e218aa37363e3..f45797de3a625 100644 --- a/packages/components/src/toolbar/toolbar-group/style.native.scss +++ b/packages/components/src/toolbar/toolbar-group/style.native.scss @@ -1,11 +1,10 @@ .container { flex-direction: row; - border-left-width: 1px; - border-color: #e9eff3; + border-color: $light-quaternary; padding-left: 5px; padding-right: 5px; } .containerDark { - border-color: #525354; + border-color: $dark-quaternary; } diff --git a/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js b/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js index 6eaf0dc0c3c74..53c2c59bda954 100644 --- a/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js +++ b/packages/components/src/toolbar/toolbar-group/toolbar-group-container.native.js @@ -1,31 +1,26 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; /** * WordPress dependencies */ -import { withPreferredColorScheme } from '@wordpress/compose'; +import { usePreferredColorSchemeStyle } from '@wordpress/compose'; /** * Internal dependencies */ import styles from './style.scss'; -const ToolbarGroupContainer = ( { - getStylesFromColorScheme, - passedStyle, - children, -} ) => ( - - { children } - -); +const ToolbarGroupContainer = ( { passedStyle, children } ) => { + const groupStyles = [ + usePreferredColorSchemeStyle( styles.container, styles.containerDark ), + { borderLeftWidth: StyleSheet.hairlineWidth }, + passedStyle, + ]; -export default withPreferredColorScheme( ToolbarGroupContainer ); + return { children }; +}; + +export default ToolbarGroupContainer; diff --git a/packages/create-block-interactive-template/.npmrc b/packages/create-block-interactive-template/.npmrc new file mode 100644 index 0000000000000..43c97e719a5a8 --- /dev/null +++ b/packages/create-block-interactive-template/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md new file mode 100644 index 0000000000000..a79948001de14 --- /dev/null +++ b/packages/create-block-interactive-template/CHANGELOG.md @@ -0,0 +1 @@ + diff --git a/packages/create-block-interactive-template/README.md b/packages/create-block-interactive-template/README.md new file mode 100644 index 0000000000000..cc0530c063054 --- /dev/null +++ b/packages/create-block-interactive-template/README.md @@ -0,0 +1,19 @@ +# Create Block Interactive Template + +This is a template for [`@wordpress/create-block`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/create-block/README.md) to create interactive blocks + +## Usage + +This block template can be used by running the following command: + +```bash +npx @wordpress/create-block --template @wordpress/create-block-interactive-template +``` + +## Contributing to this package + +This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. + +To find out more about contributing to this package or Gutenberg as a whole, please read the project's main [contributor guide](https://github.com/WordPress/gutenberg/tree/HEAD/CONTRIBUTING.md). + +

Code is Poetry.

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

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

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

+ +

+
+{{/isBasicVariant}} \ No newline at end of file diff --git a/packages/create-block-interactive-template/block-templates/style.scss.mustache b/packages/create-block-interactive-template/block-templates/style.scss.mustache new file mode 100644 index 0000000000000..1c73fa1c38ff9 --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/style.scss.mustache @@ -0,0 +1,12 @@ +/** + * The following styles get applied both on the front of your site + * and in the editor. + * + * Replace them with your own styles or remove the file completely. + */ + +.wp-block-{{namespace}}-{{slug}} { + font-size: 1em; + background: #ffff001a; + padding: 1em; +} diff --git a/packages/create-block-interactive-template/block-templates/view.js.mustache b/packages/create-block-interactive-template/block-templates/view.js.mustache new file mode 100644 index 0000000000000..85d74fec190ba --- /dev/null +++ b/packages/create-block-interactive-template/block-templates/view.js.mustache @@ -0,0 +1,26 @@ +{{#isBasicVariant}} + +/** + * WordPress dependencies + */ +import { store } from "@wordpress/interactivity"; + +store( { + actions: { + '{{namespace}}': { + toggle: ( { context } ) => { + context[ '{{namespace}}' ].isOpen = !context[ '{{namespace}}' ].isOpen; + }, + }, + }, + effects: { + '{{namespace}}': { + logIsOpen: ( { context } ) => { + // Log the value of `isOpen` each time it changes. + console.log( `Is open: ${ context[ '{{namespace}}' ].isOpen }` ); + }, + }, + }, +} ); + +{{/isBasicVariant}} \ No newline at end of file diff --git a/packages/create-block-interactive-template/index.js b/packages/create-block-interactive-template/index.js new file mode 100644 index 0000000000000..a52475ed7a79e --- /dev/null +++ b/packages/create-block-interactive-template/index.js @@ -0,0 +1,24 @@ +/** + * External dependencies + */ +const { join } = require( 'path' ); + +module.exports = { + defaultValues: { + slug: 'example-interactive', + title: 'Example Interactive', + description: 'An interactive block with the Interactivity API', + dashicon: 'media-interactive', + npmDependencies: [ '@wordpress/interactivity' ], + supports: { + interactivity: true, + }, + render: 'file:./render.php', + viewScript: 'file:./view.js', + }, + variants: { + basic: {}, + }, + pluginTemplatesPath: join( __dirname, 'plugin-templates' ), + blockTemplatesPath: join( __dirname, 'block-templates' ), +}; diff --git a/packages/create-block-interactive-template/package.json b/packages/create-block-interactive-template/package.json new file mode 100644 index 0000000000000..1638b33e64847 --- /dev/null +++ b/packages/create-block-interactive-template/package.json @@ -0,0 +1,25 @@ +{ + "name": "@wordpress/create-block-interactive-template", + "version": "1.0.0", + "description": "Template for @wordpress/create-block to create interactive blocks with the Interactivity API.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "create block", + "block template", + "Interactivity API" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/docs/getting-started/create-block", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/create-block-interactive-template" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache b/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache new file mode 100644 index 0000000000000..2322881ab0d71 --- /dev/null +++ b/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache @@ -0,0 +1,43 @@ + !! value ) ), diff --git a/packages/create-block/lib/scaffold.js b/packages/create-block/lib/scaffold.js index cdeaf85a97bb4..a8d9c3859e20d 100644 --- a/packages/create-block/lib/scaffold.js +++ b/packages/create-block/lib/scaffold.js @@ -44,6 +44,7 @@ module.exports = async ( editorStyle, style, render, + viewScript, variantVars, customPackageJSON, customBlockJSON, @@ -103,6 +104,7 @@ module.exports = async ( editorStyle, style, render, + viewScript, customPackageJSON, customBlockJSON, ...variantVars, diff --git a/packages/create-block/lib/templates.js b/packages/create-block/lib/templates.js index 018385c9fde82..4ce898617adde 100644 --- a/packages/create-block/lib/templates.js +++ b/packages/create-block/lib/templates.js @@ -33,6 +33,7 @@ const predefinedPluginTemplates = { editorScript: null, editorStyle: null, style: null, + viewScript: 'file:./view.js', }, templatesPath: join( __dirname, 'templates', 'es5' ), variants: { @@ -53,6 +54,7 @@ const predefinedPluginTemplates = { supports: { html: false, }, + viewScript: 'file:./view.js', }, variants: { static: {}, diff --git a/packages/create-block/lib/templates/block/view.js.mustache b/packages/create-block/lib/templates/block/view.js.mustache new file mode 100644 index 0000000000000..baff12a165cd9 --- /dev/null +++ b/packages/create-block/lib/templates/block/view.js.mustache @@ -0,0 +1,3 @@ +/* eslint-disable no-console */ +console.log("Hello World! (from {{namespace}}-{{slug}} block)"); +/* eslint-enable no-console */ diff --git a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap index faeb6f9e434cb..0e4ff9e63a10a 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -50,7 +50,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`dynamic-import\` should pro `; exports[`DependencyExtractionWebpackPlugin Webpack \`function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; @@ -96,7 +96,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`has-extension-suffix\` shou `; exports[`DependencyExtractionWebpackPlugin Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(), 'version' => 'f7e2cb527e601f74f8bd'); +" array(), 'version' => '43880e6c42e7c39fcdf1'); " `; @@ -110,7 +110,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`no-deps\` should produce ex exports[`DependencyExtractionWebpackPlugin Webpack \`no-deps\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin Webpack \`option-function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; @@ -133,7 +133,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`option-function-output-file `; exports[`DependencyExtractionWebpackPlugin Webpack \`option-output-filename\` should produce expected output: Asset file 'main-foo.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; @@ -155,7 +155,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`option-output-filename\` sh ] `; -exports[`DependencyExtractionWebpackPlugin Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"4c42b9646049ad2e9438"}"`; +exports[`DependencyExtractionWebpackPlugin Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"7bd48470d799a795bf9a"}"`; exports[`DependencyExtractionWebpackPlugin Webpack \`output-format-json\` should produce expected output: External modules should match snapshot 1`] = ` [ @@ -240,7 +240,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`runtime-chunk-single\` shou `; exports[`DependencyExtractionWebpackPlugin Webpack \`style-imports\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => 'd8c0ee89d933a3809c0e'); +" array('lodash', 'wp-blob'), 'version' => '4c661914a4e4d80b8a0b'); " `; @@ -263,7 +263,7 @@ exports[`DependencyExtractionWebpackPlugin Webpack \`style-imports\` should prod `; exports[`DependencyExtractionWebpackPlugin Webpack \`wordpress\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '9b7ebe61044661fdabda'); +" array('lodash', 'wp-blob'), 'version' => 'fc2d750fc9e08c5749db'); " `; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js index 917b4cd7e204b..4545bcd0c19bc 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js index 612e420c2a6c4..f9eb6f42f0a2d 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/no-default/index.js @@ -1,6 +1,7 @@ /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.map( [], _.identity ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js index 917b4cd7e204b..4545bcd0c19bc 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js index 917b4cd7e204b..4545bcd0c19bc 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js index 612e420c2a6c4..f9eb6f42f0a2d 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/output-format-json/index.js @@ -1,6 +1,7 @@ /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.map( [], _.identity ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js index 917b4cd7e204b..4545bcd0c19bc 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/b.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js index df02e0b35e6f8..b4e8cb76d1fa5 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; /** diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js index 917b4cd7e204b..4545bcd0c19bc 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/index.js @@ -6,6 +6,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * External dependencies */ +// eslint-disable-next-line no-restricted-imports import _ from 'lodash'; _.isEmpty( isBlobURL( '' ) ); diff --git a/packages/e2e-test-utils-playwright/src/test.ts b/packages/e2e-test-utils-playwright/src/test.ts index 894abf93dcd02..eec8e4e279c0f 100644 --- a/packages/e2e-test-utils-playwright/src/test.ts +++ b/packages/e2e-test-utils-playwright/src/test.ts @@ -136,18 +136,6 @@ const test = base.extend< storageStatePath: STORAGE_STATE_PATH, } ); - await Promise.all( [ - requestUtils.activateTheme( 'twentytwentyone' ), - // Disable this test plugin as it's conflicting with some of the tests. - // We already have reduced motion enabled and Playwright will wait for most of the animations anyway. - requestUtils.deactivatePlugin( - 'gutenberg-test-plugin-disables-the-css-animations' - ), - requestUtils.deleteAllPosts(), - requestUtils.deleteAllBlocks(), - requestUtils.resetPreferences(), - ] ); - await use( requestUtils ); }, { scope: 'worker', auto: true }, diff --git a/packages/e2e-test-utils/src/create-reusable-block.js b/packages/e2e-test-utils/src/create-reusable-block.js index 9714638154403..860b5ac5660eb 100644 --- a/packages/e2e-test-utils/src/create-reusable-block.js +++ b/packages/e2e-test-utils/src/create-reusable-block.js @@ -15,8 +15,6 @@ import { canvas } from './canvas'; export const createReusableBlock = async ( content, title ) => { const reusableBlockNameInputSelector = '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; - const syncToggleSelector = - '.reusable-blocks-menu-items__convert-modal .components-form-toggle__input'; const syncToggleSelectorChecked = '.reusable-blocks-menu-items__convert-modal .components-form-toggle.is-checked'; // Insert a paragraph block @@ -31,8 +29,6 @@ export const createReusableBlock = async ( content, title ) => { await nameInput.click(); await page.keyboard.type( title ); - const syncToggle = await page.waitForSelector( syncToggleSelector ); - syncToggle.click(); await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js index 2f237822b1ccc..1fc9217b4a77c 100644 --- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js @@ -23,8 +23,6 @@ const reusableBlockNameInputSelector = '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; const reusableBlockInspectorNameInputSelector = '.block-editor-block-inspector .components-text-control__input'; -const syncToggleSelector = - '.reusable-blocks-menu-items__convert-modal .components-form-toggle__input'; const syncToggleSelectorChecked = '.reusable-blocks-menu-items__convert-modal .components-form-toggle.is-checked'; @@ -205,8 +203,6 @@ describe( 'Reusable blocks', () => { ); await nameInput.click(); await page.keyboard.type( 'Multi-selection reusable block' ); - const syncToggle = await page.waitForSelector( syncToggleSelector ); - syncToggle.click(); await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); @@ -389,8 +385,6 @@ describe( 'Reusable blocks', () => { ); await nameInput.click(); await page.keyboard.type( 'Block with styles' ); - const syncToggle = await page.waitForSelector( syncToggleSelector ); - syncToggle.click(); await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); const reusableBlock = await canvas().waitForSelector( diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index 55854ffc995f6..692136b2c3c20 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -1,13 +1,13 @@ /** * External dependencies */ -import { Platform, ScrollView, View } from 'react-native'; +import { ScrollView, StyleSheet, View } from 'react-native'; /** * WordPress dependencies */ -import { useCallback, useRef, useEffect } from '@wordpress/element'; -import { compose, withPreferredColorScheme } from '@wordpress/compose'; +import { useCallback, useRef, useEffect, Platform } from '@wordpress/element'; +import { compose, usePreferredColorSchemeStyle } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; import { withViewportMatch } from '@wordpress/viewport'; import { __ } from '@wordpress/i18n'; @@ -39,6 +39,13 @@ import { import styles from './style.scss'; import { store as editPostStore } from '../../../store'; +const shadowStyle = { + shadowOffset: { width: 2, height: 2 }, + shadowOpacity: 1, + shadowRadius: 6, + elevation: 18, +}; + function HeaderToolbar( { hasRedo, hasUndo, @@ -46,18 +53,20 @@ function HeaderToolbar( { undo, showInserter, showKeyboardHideButton, - getStylesFromColorScheme, insertBlock, onHideKeyboard, - onOpenBlockSettings, isRTL, noContentSelected, } ) { const anchorNodeRef = useRef(); - const containerStyle = getStylesFromColorScheme( - styles[ 'header-toolbar__container' ], - styles[ 'header-toolbar__container--dark' ] - ); + + const containerStyle = [ + usePreferredColorSchemeStyle( + styles[ 'header-toolbar__container' ], + styles[ 'header-toolbar__container--dark' ] + ), + { borderTopWidth: StyleSheet.hairlineWidth }, + ]; useEffect( () => { const onUndoSubscription = subscribeOnUndoPressed( undo ); @@ -80,8 +89,7 @@ function HeaderToolbar( { const scrollViewRef = useRef( null ); const scrollToStart = () => { // scrollview doesn't seem to automatically adjust to RTL on Android so, scroll to end when Android - const isAndroid = Platform.OS === 'android'; - if ( isAndroid && isRTL ) { + if ( Platform.isAndroid && isRTL ) { scrollViewRef.current.scrollToEnd(); } else { scrollViewRef.current.scrollTo( { x: 0 } ); @@ -145,6 +153,23 @@ function HeaderToolbar( { /* translators: accessibility text for the editor toolbar */ const toolbarAriaLabel = __( 'Document tools' ); + const shadowColor = usePreferredColorSchemeStyle( + styles[ 'header-toolbar__keyboard-hide-shadow--light' ], + styles[ 'header-toolbar__keyboard-hide-shadow--dark' ] + ); + const showKeyboardButtonStyles = [ + usePreferredColorSchemeStyle( + styles[ 'header-toolbar__keyboard-hide-container' ], + styles[ 'header-toolbar__keyboard-hide-container--dark' ] + ), + shadowStyle, + { + shadowColor: Platform.isAndroid + ? styles[ 'header-toolbar__keyboard-hide-shadow--solid' ].color + : shadowColor.color, + }, + ]; + return ( { noContentSelected && renderMediaButtons } - + { showKeyboardHideButton && ( - + { const { clearSelectedBlock, insertBlock } = dispatch( blockEditorStore ); - const { openGeneralSidebar } = dispatch( editPostStore ); const { togglePostTitleSelection } = dispatch( editorStore ); return { @@ -231,11 +248,7 @@ export default compose( [ togglePostTitleSelection( false ); }, insertBlock, - onOpenBlockSettings() { - openGeneralSidebar( 'edit-post/block' ); - }, }; } ), withViewportMatch( { isLargeViewport: 'medium' } ), - withPreferredColorScheme, ] )( HeaderToolbar ); diff --git a/packages/edit-post/src/components/header/header-toolbar/style.native.scss b/packages/edit-post/src/components/header/header-toolbar/style.native.scss index 031d082706656..751f83d3dc63e 100644 --- a/packages/edit-post/src/components/header/header-toolbar/style.native.scss +++ b/packages/edit-post/src/components/header/header-toolbar/style.native.scss @@ -3,13 +3,13 @@ height: $mobile-header-toolbar-height; flex-direction: row; background-color: $app-background; - border-top-color: #e9eff3; - border-top-width: 1px; + border-top-color: $light-quaternary; + overflow: hidden; } .header-toolbar__container--dark { - background-color: $app-background-dark-alt; - border-top-color: $background-dark-elevated; + background-color: $app-safe-area-background-dark; + border-top-color: $dark-quaternary; } .header-toolbar__container--expanded { @@ -18,6 +18,7 @@ .header-toolbar__scrollable-content { flex-grow: 1; // Fixes RTL issue on Android. + padding-right: 8px; } .header-toolbar__keyboard-hide-container { @@ -27,4 +28,22 @@ width: 44px; justify-content: center; align-items: center; + border-color: transparent; + background-color: $app-background; +} + +.header-toolbar__keyboard-hide-container--dark { + background-color: $app-background-dark-alt; +} + +.header-toolbar__keyboard-hide-shadow--solid { + color: $black; +} + +.header-toolbar__keyboard-hide-shadow--light { + color: $light-quaternary; +} + +.header-toolbar__keyboard-hide-shadow--dark { + color: $light-primary; } diff --git a/packages/edit-post/src/components/layout/style.native.scss b/packages/edit-post/src/components/layout/style.native.scss index faee2799e54eb..765314bf15955 100644 --- a/packages/edit-post/src/components/layout/style.native.scss +++ b/packages/edit-post/src/components/layout/style.native.scss @@ -6,7 +6,7 @@ } .containerDark { - background-color: $app-background-dark-alt; + background-color: $app-safe-area-background-dark; } .background { diff --git a/packages/edit-post/src/plugins/index.js b/packages/edit-post/src/plugins/index.js index 1cd03debbf7a7..aa663659acbdc 100644 --- a/packages/edit-post/src/plugins/index.js +++ b/packages/edit-post/src/plugins/index.js @@ -41,7 +41,7 @@ function ManagePatternsMenuItem() { return ( - { __( 'Manage Patterns' ) } + { __( 'Manage patterns' ) } ); } diff --git a/packages/edit-site/src/components/add-new-page/index.js b/packages/edit-site/src/components/add-new-page/index.js index 0c8efa3dfaa0b..09d2e17ff94d1 100644 --- a/packages/edit-site/src/components/add-new-page/index.js +++ b/packages/edit-site/src/components/add-new-page/index.js @@ -72,9 +72,6 @@ export default function AddNewPageModal( { onSave, onClose } ) {
{ + const settings = select( editSiteStore ).getSettings(); + return !! settings.supportsTemplatePartsMode; + }, [] ); function handleCreatePattern( { pattern, categoryId } ) { setShowPatternModal( false ); @@ -55,7 +61,7 @@ export default function AddNewPattern() { <> setShowTemplatePartModal( true ), title: __( 'Create template part' ), @@ -65,7 +71,7 @@ export default function AddNewPattern() { onClick: () => setShowPatternModal( true ), title: __( 'Create pattern' ), }, - ] } + ].filter( Boolean ) } toggleProps={ { as: SidebarButton, } } diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index 0eb114164a0f2..3d5d05c35fa74 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -11,16 +11,17 @@ import { store as editSiteStore } from '../../store'; import { unlock } from '../../lock-unlock'; import inserterMediaCategories from './inserter-media-categories'; -export default function useSiteEditorSettings( templateType ) { +export default function useSiteEditorSettings() { const { setIsInserterOpened } = useDispatch( editSiteStore ); - const { storedSettings, canvasMode } = useSelect( + const { storedSettings, canvasMode, templateType } = useSelect( ( select ) => { - const { getSettings, getCanvasMode } = unlock( + const { getSettings, getCanvasMode, getEditedPostType } = unlock( select( editSiteStore ) ); return { storedSettings: getSettings( setIsInserterOpened ), canvasMode: getCanvasMode(), + templateType: getEditedPostType(), }; }, [ setIsInserterOpened ] diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 9ff576e5842da..2c4693ba7ded6 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -70,6 +70,9 @@ const blockRemovalRules = { 'core/post-content': __( 'Post Content displays the content of a post or page.' ), + 'core/post-template': __( + 'Post Template displays each post or page in a Query Loop.' + ), }; export default function Editor( { isLoading } ) { diff --git a/packages/edit-site/src/components/page-actions/index.js b/packages/edit-site/src/components/page-actions/index.js index 6aaf9cadc211d..f6f0119a16454 100644 --- a/packages/edit-site/src/components/page-actions/index.js +++ b/packages/edit-site/src/components/page-actions/index.js @@ -8,7 +8,7 @@ import { moreVertical } from '@wordpress/icons'; /** * Internal dependencies */ -import DeletePageMenuItem from './delete-page-menu-item'; +import TrashPageMenuItem from './trash-page-menu-item'; export default function PageActions( { postId, @@ -25,7 +25,7 @@ export default function PageActions( { > { () => ( - diff --git a/packages/edit-site/src/components/page-actions/delete-page-menu-item.js b/packages/edit-site/src/components/page-actions/trash-page-menu-item.js similarity index 59% rename from packages/edit-site/src/components/page-actions/delete-page-menu-item.js rename to packages/edit-site/src/components/page-actions/trash-page-menu-item.js index 4bbebbe818252..cc36af311d58a 100644 --- a/packages/edit-site/src/components/page-actions/delete-page-menu-item.js +++ b/packages/edit-site/src/components/page-actions/trash-page-menu-item.js @@ -3,17 +3,12 @@ */ import { useDispatch, useSelect } from '@wordpress/data'; import { decodeEntities } from '@wordpress/html-entities'; -import { useState } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; import { __, sprintf } from '@wordpress/i18n'; -import { - MenuItem, - __experimentalConfirmDialog as ConfirmDialog, -} from '@wordpress/components'; +import { MenuItem } from '@wordpress/components'; import { store as noticesStore } from '@wordpress/notices'; -export default function DeletePageMenuItem( { postId, onRemove } ) { - const [ isModalOpen, setIsModalOpen ] = useState( false ); +export default function TrashPageMenuItem( { postId, onRemove } ) { const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); const { deleteEntityRecord } = useDispatch( coreStore ); @@ -34,12 +29,12 @@ export default function DeletePageMenuItem( { postId, onRemove } ) { createSuccessNotice( sprintf( /* translators: The page's title. */ - __( '"%s" deleted.' ), + __( '"%s" moved to the Trash.' ), decodeEntities( page.title.rendered ) ), { type: 'snackbar', - id: 'edit-site-page-removed', + id: 'edit-site-page-trashed', } ); onRemove?.(); @@ -47,26 +42,18 @@ export default function DeletePageMenuItem( { postId, onRemove } ) { const errorMessage = error.message && error.code !== 'unknown_error' ? error.message - : __( 'An error occurred while deleting the page.' ); + : __( + 'An error occurred while moving the page to the trash.' + ); createErrorNotice( errorMessage, { type: 'snackbar' } ); - } finally { - setIsModalOpen( false ); } } return ( <> - setIsModalOpen( true ) } isDestructive> - { __( 'Delete' ) } + removePage() } isDestructive> + { __( 'Move to Trash' ) } - setIsModalOpen( false ) } - confirmButtonText={ __( 'Delete' ) } - > - { __( 'Are you sure you want to delete this page?' ) } - ); } diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js index 441529e1c0583..df5c5b80677b9 100644 --- a/packages/edit-site/src/components/page-patterns/grid-item.js +++ b/packages/edit-site/src/components/page-patterns/grid-item.js @@ -114,7 +114,9 @@ function GridItem( { categoryId, item, ...props } ) { } if ( isNonUserPattern ) { - ariaDescriptions.push( __( 'Theme patterns cannot be edited.' ) ); + ariaDescriptions.push( + __( 'Theme & plugin patterns cannot be edited.' ) + ); } const itemIcon = @@ -174,7 +176,7 @@ function GridItem( { categoryId, item, ...props } ) { spacing={ 3 } className="edit-site-patterns__pattern-title" > - { itemIcon && ( + { itemIcon && ! isNonUserPattern && ( diff --git a/packages/edit-site/src/components/page-patterns/patterns-list.js b/packages/edit-site/src/components/page-patterns/patterns-list.js index f4fefed4a2d3e..69bbcff772300 100644 --- a/packages/edit-site/src/components/page-patterns/patterns-list.js +++ b/packages/edit-site/src/components/page-patterns/patterns-list.js @@ -57,11 +57,18 @@ export default function PatternsList( { categoryId, type } ) { const [ syncFilter, setSyncFilter ] = useState( 'all' ); const deferredSyncedFilter = useDeferredValue( syncFilter ); - const { patterns, isResolving } = usePatterns( type, categoryId, { - search: deferredFilterValue, - syncStatus: - deferredSyncedFilter === 'all' ? undefined : deferredSyncedFilter, - } ); + + const { patterns, isResolving } = usePatterns( + type, + categoryId !== 'uncategorized' ? categoryId : '', + { + search: deferredFilterValue, + syncStatus: + deferredSyncedFilter === 'all' + ? undefined + : deferredSyncedFilter, + } + ); const id = useId(); const titleId = `${ id }-title`; diff --git a/packages/edit-site/src/components/page-patterns/use-patterns.js b/packages/edit-site/src/components/page-patterns/use-patterns.js index ea2b8ac976fea..66aba19e9b5b4 100644 --- a/packages/edit-site/src/components/page-patterns/use-patterns.js +++ b/packages/edit-site/src/components/page-patterns/use-patterns.js @@ -93,11 +93,17 @@ const selectThemePatterns = ( select, { categoryId, search = '' } = {} ) => { blocks: parse( pattern.content ), } ) ); - patterns = searchItems( patterns, search, { - categoryId, - hasCategory: ( item, currentCategory ) => - item.categories?.includes( currentCategory ), - } ); + if ( categoryId ) { + patterns = searchItems( patterns, search, { + categoryId, + hasCategory: ( item, currentCategory ) => + item.categories?.includes( currentCategory ), + } ); + } else { + patterns = searchItems( patterns, search, { + hasCategory: ( item ) => ! item.hasOwnProperty( 'categories' ), + } ); + } return { patterns, isResolving: false }; }; diff --git a/packages/edit-site/src/components/save-panel/index.js b/packages/edit-site/src/components/save-panel/index.js index 418de53390cac..5c1bcc1df281e 100644 --- a/packages/edit-site/src/components/save-panel/index.js +++ b/packages/edit-site/src/components/save-panel/index.js @@ -39,13 +39,19 @@ const EntitiesSavedStatesForPreview = ( { onClose } ) => { activateSaveLabel = __( 'Activate' ); } - const { getTheme } = useSelect( coreStore ); - const theme = getTheme( currentlyPreviewingTheme() ); + const themeName = useSelect( ( select ) => { + const theme = select( coreStore ).getTheme( + currentlyPreviewingTheme() + ); + + return theme?.name?.rendered; + }, [] ); + const additionalPrompt = (

{ sprintf( - 'Saving your changes will change your active theme to %1$s.', - theme?.name?.rendered + 'Saving your changes will change your active theme to %s.', + themeName ) }

); diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-status.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-status.js index 8910cb1968c3e..b872d4e12c25e 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-status.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-status.js @@ -218,7 +218,7 @@ export default function PageStatus( { placeholder={ __( 'Enter a secure password' ) } - type="password" + type="text" /> ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js b/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js index 8fbe74f81bb4d..c6f270465b86f 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js @@ -28,9 +28,7 @@ export default function TemplatePartHint() { setPreference( 'core', PREFERENCE_NAME, false ); } } > - { __( - 'Looking for template parts? You can now find them in the new "Patterns" page.' - ) } + { __( 'Looking for template parts? Find them in "Patterns".' ) } ); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js index c7d3a3b77d151..86155c166d583 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js @@ -12,7 +12,7 @@ import { import { useViewportMatch } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; import { getTemplatePartIcon } from '@wordpress/editor'; -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { getQueryArgs } from '@wordpress/url'; import { file, starFilled, lockSmall } from '@wordpress/icons'; @@ -61,9 +61,6 @@ function TemplatePartGroup( { areas, currentArea, currentType } ) { function ThemePatternsGroup( { categories, currentCategory, currentType } ) { return ( <> -
- { __( 'Theme patterns' ) } -
{ categories.map( ( category ) => ( @@ -117,9 +116,6 @@ export default function SidebarNavigationScreenPatterns() { const templatePartsLink = useLink( { path: '/wp_template_part/all' } ); const footer = ! isMobileViewport ? ( - - { __( 'Manage all template parts' ) } - { __( 'Manage all of my patterns' ) } + + { __( 'Manage all template parts' ) } + ) : undefined; @@ -172,13 +171,6 @@ export default function SidebarNavigationScreenPatterns() { } /> - { hasTemplateParts && ( - - ) } { hasPatterns && ( ) } + { hasTemplateParts && ( + + ) } ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss index 6a6fbc009e0aa..7b448c375fb40 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss @@ -1,18 +1,16 @@ .edit-site-sidebar-navigation-screen-patterns__group { - margin-bottom: $grid-unit-40; - &:last-of-type, - &:first-of-type { + margin-bottom: $grid-unit-30; + + &:last-of-type { border-bottom: 0; padding-bottom: 0; margin-bottom: 0; } - - &:first-of-type { - margin-bottom: $grid-unit-40; - } } .edit-site-sidebar-navigation-screen-patterns__group-header { + margin-top: $grid-unit-20; + p { color: $gray-600; } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-pattern-categories.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-pattern-categories.js index 96491018d0772..da4732f5be448 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-pattern-categories.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-pattern-categories.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -11,6 +12,10 @@ import useThemePatterns from './use-theme-patterns'; export default function usePatternCategories() { const defaultCategories = useDefaultPatternCategories(); + defaultCategories.push( { + name: 'uncategorized', + label: __( 'Uncategorized' ), + } ); const themePatterns = useThemePatterns(); const patternCategories = useMemo( () => { @@ -31,6 +36,10 @@ export default function usePatternCategories() { categoryMap[ category ].count += 1; } } ); + // If the pattern has no categories, add it to uncategorized. + if ( ! pattern.categories?.length ) { + categoryMap.uncategorized.count += 1; + } } ); // Filter categories so we only have those containing patterns. diff --git a/packages/editor/src/components/entities-saved-states/entity-record-item.js b/packages/editor/src/components/entities-saved-states/entity-record-item.js index 1e3ddcbc69668..1778b9c18b8cb 100644 --- a/packages/editor/src/components/entities-saved-states/entity-record-item.js +++ b/packages/editor/src/components/entities-saved-states/entity-record-item.js @@ -4,7 +4,6 @@ import { CheckboxControl, Button, PanelRow } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useCallback } from '@wordpress/element'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; @@ -21,20 +20,23 @@ export default function EntityRecordItem( { closePanel, } ) { const { name, kind, title, key } = record; - const parentBlockId = useSelect( ( select ) => { - // Get entity's blocks. - const { blocks = [] } = select( coreStore ).getEditedEntityRecord( - kind, - name, - key - ); - // Get parents of the entity's first block. - const parents = select( blockEditorStore ).getBlockParents( - blocks[ 0 ]?.clientId - ); - // Return closest parent block's clientId. - return parents[ parents.length - 1 ]; - }, [] ); + const parentBlockId = useSelect( + ( select ) => { + // Get entity's blocks. + const { blocks = [] } = select( coreStore ).getEditedEntityRecord( + kind, + name, + key + ); + // Get parents of the entity's first block. + const parents = select( blockEditorStore ).getBlockParents( + blocks[ 0 ]?.clientId + ); + // Return closest parent block's clientId. + return parents[ parents.length - 1 ]; + }, + [ key, kind, name ] + ); // Handle templates that might use default descriptive titles. const entityRecordTitle = useSelect( @@ -65,14 +67,6 @@ export default function EntityRecordItem( { ); const isSelectedText = isSelected ? __( 'Selected' ) : __( 'Select' ); const { selectBlock } = useDispatch( blockEditorStore ); - const selectParentBlock = useCallback( - () => selectBlock( parentBlockId ), - [ parentBlockId ] - ); - const selectAndDismiss = useCallback( () => { - selectBlock( parentBlockId ); - closePanel(); - }, [ parentBlockId ] ); return ( @@ -90,16 +84,19 @@ export default function EntityRecordItem( { { parentBlockId ? ( <> diff --git a/packages/editor/src/components/post-sync-status/index.js b/packages/editor/src/components/post-sync-status/index.js index 8219ef7c0e4f5..bbc8cd0cfe974 100644 --- a/packages/editor/src/components/post-sync-status/index.js +++ b/packages/editor/src/components/post-sync-status/index.js @@ -12,12 +12,13 @@ import { ToggleControl, } from '@wordpress/components'; import { useEffect, useState } from '@wordpress/element'; -import { ReusableBlocksRenameHint } from '@wordpress/block-editor'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; /** * Internal dependencies */ import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; export default function PostSyncStatus() { const { syncStatus, postType, meta } = useSelect( ( select ) => { @@ -81,7 +82,7 @@ export function PostSyncStatusModal() { if ( postType !== 'wp_block' || ! isNewPost ) { return null; } - + const { ReusableBlocksRenameHint } = unlock( blockEditorPrivateApis ); return ( <> { isModalOpen && ( diff --git a/packages/format-library/src/text-color/index.native.js b/packages/format-library/src/text-color/index.native.js index 5003dee86d3a5..5f133383ae1d1 100644 --- a/packages/format-library/src/text-color/index.native.js +++ b/packages/format-library/src/text-color/index.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; /** * WordPress dependencies @@ -14,7 +14,11 @@ import { ToolbarButton, useMobileGlobalStylesColors, } from '@wordpress/components'; -import { Icon, textColor as textColorIcon } from '@wordpress/icons'; +import { + Icon, + color as colorIcon, + textColor as textColorIcon, +} from '@wordpress/icons'; import { removeFormat } from '@wordpress/rich-text'; import { usePreferredColorSchemeStyle } from '@wordpress/compose'; @@ -98,10 +102,13 @@ function TextColorEdit( { } }, [ hasColorsToChoose, value ] ); - const outlineStyle = usePreferredColorSchemeStyle( - styles[ 'components-inline-color__outline' ], - styles[ 'components-inline-color__outline--dark' ] - ); + const outlineStyle = [ + usePreferredColorSchemeStyle( + styles[ 'components-inline-color__outline' ], + styles[ 'components-inline-color__outline--dark' ] + ), + { borderWidth: StyleSheet.hairlineWidth }, + ]; if ( ! hasColorsToChoose && ! isActive ) { return null; @@ -131,7 +138,11 @@ function TextColorEdit( { isActive={ isActive } icon={ { - const colorScheme = props.colorScheme || 'light'; + const colorScheme = usePreferredColorScheme(); const stylesFromClasses = className .split( ' ' ) .map( ( element ) => styles[ element ] ) .filter( Boolean ); const defaultStyle = isPressed - ? styles[ 'is-pressed' ] + ? styles[ `is-pressed--${ colorScheme }` ] : styles[ 'components-toolbar__control-' + colorScheme ]; const propStyle = Array.isArray( props.style ) ? props.style.reduce( ( acc, el ) => { diff --git a/packages/primitives/src/svg/style.native.scss b/packages/primitives/src/svg/style.native.scss index ea8e7845174aa..2e7dfdbfaff7a 100644 --- a/packages/primitives/src/svg/style.native.scss +++ b/packages/primitives/src/svg/style.native.scss @@ -1,21 +1,30 @@ .dashicon-light, .components-toolbar__control-light { - color: $toolbar-button; + color: $light-primary; fill: currentColor; } .dashicon-dark, .components-toolbar__control-dark { - color: $gray_20; + color: $dark-primary; fill: currentColor; } -.dashicon-active, -.is-pressed { +.dashicon-active { color: #fff; fill: currentColor; } +.is-pressed--light { + color: $light-primary; + fill: currentColor; +} + +.is-pressed--dark { + color: $dark-primary; + fill: currentColor; +} + .dashicons-insert { color: #87a6bc; fill: currentColor; diff --git a/packages/private-apis/src/implementation.js b/packages/private-apis/src/implementation.js index 31936fba5ad51..2c9fdadb27c57 100644 --- a/packages/private-apis/src/implementation.js +++ b/packages/private-apis/src/implementation.js @@ -23,6 +23,7 @@ const CORE_MODULES_USING_PRIVATE_APIS = [ '@wordpress/edit-site', '@wordpress/edit-widgets', '@wordpress/editor', + '@wordpress/reusable-blocks', '@wordpress/router', ]; diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index c110932d6c0d8..a3abe54a2e65b 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.99.0", + "version": "1.99.1", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index 8c07de0e29871..e27c6fd80edad 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.99.0", + "version": "1.99.1", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index da4ddfad0ae52..b7bd28a9926c9 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -14,6 +14,10 @@ For each user feature we should also add a importance categorization label to i - [**] Update native BlockOutline component styles to remove blue border from blocks [#51222] - [**] Move the undo/redo buttons to the navigation bar [#51766] - [**] Update Editor block inserter button styles and default text input placeholder/selection styles [#52269] +- [**] Update Editor toolbar icons and colors [#52336] + +## 1.99.1 +- [**] Fix crash related to removing a block under certain conditions [#52595] ## 1.99.0 - [*] Rename "Reusable blocks" to "Synced patterns", aligning with the web editor. [#51704] diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 27d2e6ca929b5..285d7dcd1b88d 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.69.4) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.99.0): + - Gutenberg (1.99.1): - React-Core (= 0.69.4) - React-CoreModules (= 0.69.4) - React-RCTImage (= 0.69.4) @@ -360,7 +360,7 @@ PODS: - React-Core - RNSVG (9.13.6): - React-Core - - RNTAztecView (1.99.0): + - RNTAztecView (1.99.1): - React-Core - WordPress-Aztec-iOS (~> 1.19.8) - SDWebImage (5.11.1): @@ -540,7 +540,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 2ff441cbe6e58c1778d8a5cf3311831a6a8c0809 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a - Gutenberg: 06d0e1bc1dbd7ad23b8f9b587cceba18aa8518da + Gutenberg: 8520acd4989acf941c57a1da5c83bec15423a056 libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a RCTRequired: bd9d2ab0fda10171fcbcf9ba61a7df4dc15a28f4 @@ -582,7 +582,7 @@ SPEC CHECKSUMS: RNReanimated: 5740ec9926f80bccd404bacd3e71108e87c94afa RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 - RNTAztecView: 4ccd0ce94481a4026420a7b0725ce86e762f1c16 + RNTAztecView: 47f185f1e8b276ed63ed571330930428a28353d3 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index 91f261457d4e0..2f1868e693d2c 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.99.0", + "version": "1.99.1", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index a15e98ac1a906..81bd9f5424572 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -37,6 +37,7 @@ "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", "@wordpress/notices": "file:../notices", + "@wordpress/private-apis": "^0.19.0", "@wordpress/url": "file:../url" }, "peerDependencies": { diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js index 3635c0d1c3e81..d7823483f5e09 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js @@ -25,8 +25,14 @@ function ReusableBlocksMenuItems( { clientIds, rootClientId } ) { } export default withSelect( ( select ) => { - const { getSelectedBlockClientIds } = select( blockEditorStore ); + const { getSelectedBlockClientIds, getBlockRootClientId } = + select( blockEditorStore ); + const clientIds = getSelectedBlockClientIds(); return { - clientIds: getSelectedBlockClientIds(), + clientIds, + rootClientId: + clientIds?.length > 0 + ? getBlockRootClientId( clientIds[ 0 ] ) + : undefined, }; } )( ReusableBlocksMenuItems ); diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js index 981776880a137..2dfd635e2963a 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js @@ -5,7 +5,7 @@ import { hasBlockSupport, isReusableBlock } from '@wordpress/blocks'; import { BlockSettingsMenuControls, store as blockEditorStore, - ReusableBlocksRenameHint, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { useCallback, useState } from '@wordpress/element'; import { @@ -27,6 +27,7 @@ import { store as coreStore } from '@wordpress/core-data'; * Internal dependencies */ import { store } from '../../store'; +import { unlock } from '../../lock-unlock'; /** * Menu control to convert block(s) to reusable block. @@ -40,7 +41,11 @@ export default function ReusableBlockConvertButton( { clientIds, rootClientId, } ) { - const [ syncType, setSyncType ] = useState( 'unsynced' ); + const { useReusableBlocksRenameHint, ReusableBlocksRenameHint } = unlock( + blockEditorPrivateApis + ); + const showRenameHint = useReusableBlocksRenameHint(); + const [ syncType, setSyncType ] = useState( undefined ); const [ isModalOpen, setIsModalOpen ] = useState( false ); const [ title, setTitle ] = useState( '' ); const canConvert = useSelect( @@ -97,7 +102,7 @@ export default function ReusableBlockConvertButton( { syncType ); createSuccessNotice( - syncType === 'fully' + ! syncType ? sprintf( // translators: %s: the name the user has given to the pattern. __( 'Synced Pattern created: %s' ), @@ -141,7 +146,9 @@ export default function ReusableBlockConvertButton( { icon={ symbol } onClick={ () => setIsModalOpen( true ) } > - { __( 'Create pattern/reusable block' ) } + { showRenameHint + ? __( 'Create pattern/reusable block' ) + : __( 'Create pattern' ) }
{ isModalOpen && ( { setSyncType( - syncType === 'fully' + ! syncType ? 'unsynced' - : 'fully' + : undefined ); } } /> diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js index e3bbef8bf7738..b69dbc5e97719 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js @@ -64,7 +64,7 @@ function ReusableBlocksManageButton( { clientId } ) { return ( - { __( 'Manage Patterns' ) } + { __( 'Manage patterns' ) } { canRemove && ( convertBlockToStatic( clientId ) }> diff --git a/packages/reusable-blocks/src/lock-unlock.js b/packages/reusable-blocks/src/lock-unlock.js new file mode 100644 index 0000000000000..c33f209c9d76a --- /dev/null +++ b/packages/reusable-blocks/src/lock-unlock.js @@ -0,0 +1,9 @@ +/** + * WordPress dependencies + */ +import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis'; + +export const { unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules( + 'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.', + '@wordpress/reusable-blocks' +); diff --git a/packages/reusable-blocks/src/store/actions.js b/packages/reusable-blocks/src/store/actions.js index 17a2e83d5e776..292a3082146aa 100644 --- a/packages/reusable-blocks/src/store/actions.js +++ b/packages/reusable-blocks/src/store/actions.js @@ -42,9 +42,9 @@ export const __experimentalConvertBlockToStatic = /** * Returns a generator converting one or more static blocks into a pattern. * - * @param {string[]} clientIds The client IDs of the block to detach. - * @param {string} title Pattern title. - * @param {'fully'|'unsynced'} syncType They way block is synced, current 'fully' and 'unsynced'. + * @param {string[]} clientIds The client IDs of the block to detach. + * @param {string} title Pattern title. + * @param {undefined|'unsynced'} syncType They way block is synced, current undefined (synced) and 'unsynced'. */ export const __experimentalConvertBlocksToReusable = ( clientIds, title, syncType ) => diff --git a/schemas/json/block.json b/schemas/json/block.json index 6ca40ba90f961..147378da0293a 100644 --- a/schemas/json/block.json +++ b/schemas/json/block.json @@ -336,6 +336,116 @@ "description": "By default, all blocks will appear in the inserter, block transforms menu, Style Book, etc. To hide a block from all parts of the user interface so that it can only be inserted programmatically, set inserter to false.", "default": true }, + "layout": { + "default": false, + "description": "This value only applies to blocks that are containers for inner blocks. If set to `true` the layout type will be `flow`. For other layout types it's necessary to set the `type` explicitly inside the `default` object.", + "oneOf": [ + { "type": "boolean" }, + { + "type": "object", + "properties": { + "default": { + "type": "object", + "description": "Allows setting the `type` property to define what layout type is default for the block, and also default values for any properties inherent to that layout type, e.g., for a `flex` layout, a default value can be set for `flexWrap`.", + "properties": { + "type": { + "type": "string", + "description": "The layout type.", + "enum": [ + "constrained", + "grid", + "flex" + ] + }, + "contentSize": { + "type": "string", + "description": "The content size used on all children." + }, + "wideSize": { + "type": "string", + "description": "The wide size used on alignwide children." + }, + "justifyContent": { + "type": "string", + "description": "Content justification value.", + "enum": [ + "right", + "center", + "space-between", + "left", + "stretch" + ] + }, + "orientation": { + "type": "string", + "description": "The orientation of the layout.", + "enum": [ "horizontal", "vertical" ] + }, + "flexWrap": { + "type": "string", + "description": "The flex wrap value.", + "enum": [ "wrap", "nowrap" ] + }, + "verticalAlignment": { + "type": "string", + "description": "The vertical alignment value.", + "enum": [ + "top", + "center", + "bottom", + "space-between", + "stretch" + ] + }, + "minimumColumnWidth": { + "type": "string", + "description": "The minimum column width value." + }, + "columnCount": { + "type": "number", + "description": "The column count value." + } + } + }, + "allowSwitching": { + "type": "boolean", + "description": "Exposes a switcher control that allows toggling between all existing layout types.", + "default": false + }, + "allowEditing": { + "type": "boolean", + "description": "Determines display of layout controls in the block sidebar. If set to false, layout controls will be hidden.", + "default": true + }, + "allowInheriting": { + "type": "boolean", + "description": "For the `flow` layout type only, determines display of the `Inner blocks use content width` toggle.", + "default": true + }, + "allowSizingOnChildren": { + "type": "boolean", + "description": "For the `flex` layout type only, determines display of sizing controls (Fit/Fill/Fixed) on all child blocks of the flex block.", + "default": false + }, + "allowVerticalAlignment": { + "type": "boolean", + "description": "For the `flex` layout type only, determines display of vertical alignment controls in the block toolbar.", + "default": true + }, + "allowJustification": { + "type": "boolean", + "description": "For the `flex` layout type, determines display of justification controls in the block toolbar and block sidebar. For the `constrained` layout type, determines display of justification control in the block sidebar.", + "default": true + }, + "allowOrientation": { + "type": "boolean", + "description": "For the `flex` layout type only, determines display of the orientation control in the block toolbar.", + "default": true + } + } + } + ] + }, "multiple": { "type": "boolean", "description": "A non-multiple block can be inserted into each post, one time only. For example, the built-in ‘More’ block cannot be inserted again if it already exists in the post being edited. A non-multiple block’s icon is automatically dimmed (unclickable) to prevent multiple instances.", diff --git a/test/e2e/config/global-setup.ts b/test/e2e/config/global-setup.ts index 10f2822fdfe1a..787488ac72fca 100644 --- a/test/e2e/config/global-setup.ts +++ b/test/e2e/config/global-setup.ts @@ -25,6 +25,19 @@ async function globalSetup( config: FullConfig ) { // Authenticate and save the storageState to disk. await requestUtils.setupRest(); + // Reset the test environment before running the tests. + await Promise.all( [ + requestUtils.activateTheme( 'twentytwentyone' ), + // Disable this test plugin as it's conflicting with some of the tests. + // We already have reduced motion enabled and Playwright will wait for most of the animations anyway. + requestUtils.deactivatePlugin( + 'gutenberg-test-plugin-disables-the-css-animations' + ), + requestUtils.deleteAllPosts(), + requestUtils.deleteAllBlocks(), + requestUtils.resetPreferences(), + ] ); + await requestContext.dispose(); } diff --git a/test/e2e/specs/editor/blocks/navigation-colors.spec.js b/test/e2e/specs/editor/blocks/navigation-colors.spec.js index dd74b3194a16a..2763b1c208cd0 100644 --- a/test/e2e/specs/editor/blocks/navigation-colors.spec.js +++ b/test/e2e/specs/editor/blocks/navigation-colors.spec.js @@ -6,57 +6,44 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); test.describe( 'Navigation colors', () => { test.beforeAll( async ( { requestUtils } ) => { // We want emptytheme because it doesn't have any styles. - await requestUtils.activateTheme( 'emptytheme' ); - await requestUtils.deleteAllTemplates( 'wp_template_part' ); - await requestUtils.deleteAllMenus(); - await requestUtils.deleteAllPages(); + await Promise.all( [ + requestUtils.activateTheme( 'emptytheme' ), + requestUtils.deleteAllMenus(), + requestUtils.deleteAllPages(), + ] ); } ); test.beforeEach( async ( { admin, editor, requestUtils } ) => { - await admin.visitSiteEditor( { - postId: 'emptytheme//header', - postType: 'wp_template_part', - } ); - await editor.canvas.click( 'body' ); const { id: pageId } = await requestUtils.createPage( { title: 'Test Page', status: 'publish', } ); + const { id: menuId } = await requestUtils.createNavigationMenu( { title: 'Colored menu', content: ``, attributes: { openSubmenusOnClick: true }, } ); + + await admin.createNewPost(); + await editor.insertBlock( { name: 'core/navigation', attributes: { ref: menuId, }, } ); - - await editor.saveSiteEditorEntities(); - await admin.visitSiteEditor(); - await editor.canvas.click( 'body' ); - // Focus the navigation block inside the header template part. - await editor.canvas - .getByRole( 'document', { name: 'Block: header' } ) - .focus(); - await editor.canvas - .getByRole( 'document', { name: 'Block: Navigation' } ) - .click(); } ); test.afterEach( async ( { requestUtils } ) => { - await requestUtils.deleteAllTemplates( 'wp_template_part' ); - await requestUtils.deleteAllMenus(); - await requestUtils.deleteAllPages(); + await Promise.all( [ + requestUtils.deleteAllMenus(), + requestUtils.deleteAllPages(), + ] ); } ); test.afterAll( async ( { requestUtils } ) => { await Promise.all( [ - requestUtils.deleteAllTemplates( 'wp_template_part' ), - requestUtils.deleteAllMenus(), - requestUtils.deleteAllPages(), requestUtils.activateTheme( 'twentytwentyone' ), ] ); } ); @@ -68,6 +55,7 @@ test.describe( 'Navigation colors', () => { } ); test( 'All navigation links should default to the body color and submenus and mobile overlay should default to a white background with black text', async ( { + editor, page, colorControl, } ) => { @@ -80,8 +68,9 @@ test.describe( 'Navigation colors', () => { await colorControl.testEditorColors( expectedNavigationColors ); - // And finally we check the colors of the links on the frontend. - await page.goto( '/' ); + // Check the colors of the links on the frontend. + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); await colorControl.testFrontendColors( expectedNavigationColors ); } ); @@ -114,19 +103,11 @@ test.describe( 'Navigation colors', () => { .getByRole( 'button', { name: 'Color: Black' } ) .click( { force: true } ); - // Save them so we can check on the frontend later too. - await editor.saveSiteEditorEntities(); // Close the sidebar so our selectors don't accidentally select the sidebar links instead of the editor canvas. await page .getByRole( 'button', { name: 'Close Settings' } ) .click( { force: true } ); - await editor.canvas.click( 'body' ); - - // Focus the navigation block inside the header template part. - await editor.canvas - .getByRole( 'document', { name: 'Block: header' } ) - .focus(); await editor.canvas .getByRole( 'document', { name: 'Block: Navigation' } ) .click(); @@ -140,8 +121,9 @@ test.describe( 'Navigation colors', () => { await colorControl.testEditorColors( expectedNavigationColors ); - // And finally we check the colors of the links on the frontend. - await page.goto( '/' ); + // Check the colors of the links on the frontend. + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); await colorControl.testFrontendColors( expectedNavigationColors ); } ); @@ -177,19 +159,11 @@ test.describe( 'Navigation colors', () => { .getByRole( 'button', { name: 'Color: Vivid purple' } ) .click( { force: true } ); - // Save them so we can check on the frontend later too. - await editor.saveSiteEditorEntities(); // Close the sidebar so our selectors don't accidentally select the sidebar links instead of the editor canvas. await page .getByRole( 'button', { name: 'Close Settings' } ) .click( { force: true } ); - await editor.canvas.click( 'body' ); - - // Focus the navigation block inside the header template part. - await editor.canvas - .getByRole( 'document', { name: 'Block: header' } ) - .focus(); await editor.canvas .getByRole( 'document', { name: 'Block: Navigation' } ) .click(); @@ -203,8 +177,9 @@ test.describe( 'Navigation colors', () => { await colorControl.testEditorColors( expectedNavigationColors ); - // And finally we check the colors of the links on the frontend. - await page.goto( '/' ); + // Check the colors of the links on the frontend. + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); await colorControl.testFrontendColors( expectedNavigationColors ); } ); @@ -226,18 +201,11 @@ test.describe( 'Navigation colors', () => { .getByRole( 'button', { name: 'Color: Pale pink' } ) .click( { force: true } ); - await editor.saveSiteEditorEntities(); // Close the sidebar so our selectors don't accidentally select the sidebar links instead of the editor canvas. await page .getByRole( 'button', { name: 'Close Settings' } ) .click( { force: true } ); - await editor.canvas.click( 'body' ); - - // Focus the navigation block inside the header template part. - await editor.canvas - .getByRole( 'document', { name: 'Block: header' } ) - .focus(); await editor.canvas .getByRole( 'document', { name: 'Block: Navigation' } ) .click(); @@ -251,8 +219,9 @@ test.describe( 'Navigation colors', () => { await colorControl.testEditorColors( expectedNavigationColors ); - // And finally we check the colors of the links on the frontend. - await page.goto( '/' ); + // Check the colors of the links on the frontend. + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); await colorControl.testFrontendColors( expectedNavigationColors ); } ); @@ -276,17 +245,11 @@ test.describe( 'Navigation colors', () => { .getByRole( 'button', { name: 'Color: Pale cyan blue' } ) .click( { force: true } ); - await editor.saveSiteEditorEntities(); // Close the sidebar so our selectors don't accidentally select the sidebar links instead of the editor canvas. await page .getByRole( 'button', { name: 'Close Settings' } ) .click( { force: true } ); - await editor.canvas.click( 'body' ); - // Focus the navigation block inside the header template part. - await editor.canvas - .getByRole( 'document', { name: 'Block: header' } ) - .focus(); await editor.canvas .getByRole( 'document', { name: 'Block: Navigation' } ) .click(); @@ -301,8 +264,9 @@ test.describe( 'Navigation colors', () => { await colorControl.testEditorColors( expectedNavigationColors ); - // And finally we check the colors of the links on the frontend. - await page.goto( '/' ); + // Check the colors of the links on the frontend. + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); await colorControl.testFrontendColors( expectedNavigationColors ); } ); @@ -351,18 +315,11 @@ test.describe( 'Navigation colors', () => { .getByRole( 'button', { name: 'Color: Luminous vivid amber' } ) .click( { force: true } ); - await editor.saveSiteEditorEntities(); // Close the sidebar so our selectors don't accidentally select the sidebar links instead of the editor canvas. await page .getByRole( 'button', { name: 'Close Settings' } ) .click( { force: true } ); - await editor.canvas.click( 'body' ); - - // Focus the navigation block inside the header template part. - await editor.canvas - .getByRole( 'document', { name: 'Block: header' } ) - .focus(); await editor.canvas .getByRole( 'document', { name: 'Block: Navigation' } ) .click(); @@ -376,8 +333,9 @@ test.describe( 'Navigation colors', () => { await colorControl.testEditorColors( expectedNavigationColors ); - // And finally we check the colors of the links on the frontend. - await page.goto( '/' ); + // Check the colors of the links on the frontend. + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); await colorControl.testFrontendColors( expectedNavigationColors ); } ); @@ -469,6 +427,9 @@ class ColorControl { 'background-color', submenuBackgroundColor ); + + // Set the mobile view option back to mobile + await this.page.getByRole( 'radio', { name: 'Mobile' } ).click(); } async testFrontendColors( { diff --git a/test/e2e/specs/editor/various/navigable-toolbar.spec.js b/test/e2e/specs/editor/various/navigable-toolbar.spec.js index d6524eeabb64b..abdb1800d150a 100644 --- a/test/e2e/specs/editor/various/navigable-toolbar.spec.js +++ b/test/e2e/specs/editor/various/navigable-toolbar.spec.js @@ -45,4 +45,19 @@ test.describe( 'Block Toolbar', () => { expect( scrollTopBefore ).toBe( scrollTopAfter ); } ); } ); + + test( 'should focus with Shift+Tab', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( 'a' ); + await pageUtils.pressKeys( 'shift+Tab' ); + await expect( + page + .getByRole( 'toolbar', { name: 'Block Tools' } ) + .getByRole( 'button', { name: 'Paragraph' } ) + ).toBeFocused(); + } ); } ); diff --git a/test/e2e/specs/site-editor/block-removal.spec.js b/test/e2e/specs/site-editor/block-removal.spec.js index 66c75d722e425..c1d6cdfea0022 100644 --- a/test/e2e/specs/site-editor/block-removal.spec.js +++ b/test/e2e/specs/site-editor/block-removal.spec.js @@ -38,6 +38,30 @@ test.describe( 'Site editor block removal prompt', () => { ).toBeVisible(); } ); + test( 'should appear when attempting to remove Post Template Block', async ( { + page, + } ) => { + // Open and focus List View + const topBar = page.getByRole( 'region', { name: 'Editor top bar' } ); + await topBar.getByRole( 'button', { name: 'List View' } ).click(); + + // Select and open child blocks of Query Loop block + const listView = page.getByRole( 'region', { name: 'List View' } ); + await listView.getByRole( 'link', { name: 'Query Loop' } ).click(); + await page.keyboard.press( 'ArrowRight' ); + + // Select and try to remove Post Template block + await listView.getByRole( 'link', { name: 'Post Template' } ).click(); + await page.keyboard.press( 'Backspace' ); + + // Expect the block removal prompt to have appeared + await expect( + page.getByText( + 'Post Template displays each post or page in a Query Loop.' + ) + ).toBeVisible(); + } ); + test( 'should not appear when attempting to remove something else', async ( { editor, page, @@ -53,14 +77,20 @@ test.describe( 'Site editor block removal prompt', () => { // Reveal its inner blocks in the list view await page.keyboard.press( 'ArrowRight' ); - // Select and remove its Post Template inner block + // Select its Post Template inner block await listView.getByRole( 'link', { name: 'Post Template' } ).click(); + + // Reveal its inner blocks in the list view + await page.keyboard.press( 'ArrowRight' ); + + // Select and remove its Title inner block + await listView.getByRole( 'link', { name: 'Title' } ).click(); await page.keyboard.press( 'Backspace' ); // Expect the block to have been removed with no prompt await expect( editor.canvas.getByRole( 'document', { - name: 'Block: Post Template', + name: 'Block: Title', } ) ).toBeHidden(); } ); diff --git a/test/e2e/specs/site-editor/hybrid-theme.spec.js b/test/e2e/specs/site-editor/hybrid-theme.spec.js new file mode 100644 index 0000000000000..9d5def2caf71f --- /dev/null +++ b/test/e2e/specs/site-editor/hybrid-theme.spec.js @@ -0,0 +1,41 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Hybrid theme', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'emptyhybrid' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test( 'should not show the Add Template Part button', async ( { + admin, + page, + } ) => { + await admin.visitAdminPage( + '/site-editor.php', + 'postType=wp_template_part&path=/wp_template_part/all' + ); + + await expect( + page.locator( 'role=button[name="Add New Template Part"i]' ) + ).toBeHidden(); + + // Back to Patterns page. + await page.click( + 'role=region[name="Navigation"i] >> role=button[name="Back"i]' + ); + + await page.click( + 'role=region[name="Navigation"i] >> role=button[name="Create pattern"i]' + ); + + await expect( + page.locator( 'role=menuitem[name="Create template part"i]' ) + ).toBeHidden(); + } ); +} ); diff --git a/test/native/__mocks__/styleMock.js b/test/native/__mocks__/styleMock.js index a3707d71e3d3b..7aae4fd6f8941 100644 --- a/test/native/__mocks__/styleMock.js +++ b/test/native/__mocks__/styleMock.js @@ -190,4 +190,10 @@ module.exports = { 'rich-text-selection': { color: 'black', }, + 'header-toolbar__keyboard-hide-shadow--light': { + color: 'black', + }, + 'header-toolbar__keyboard-hide-shadow--solid': { + color: 'black', + }, }; diff --git a/test/performance/config/global-setup.ts b/test/performance/config/global-setup.ts index 10f2822fdfe1a..787488ac72fca 100644 --- a/test/performance/config/global-setup.ts +++ b/test/performance/config/global-setup.ts @@ -25,6 +25,19 @@ async function globalSetup( config: FullConfig ) { // Authenticate and save the storageState to disk. await requestUtils.setupRest(); + // Reset the test environment before running the tests. + await Promise.all( [ + requestUtils.activateTheme( 'twentytwentyone' ), + // Disable this test plugin as it's conflicting with some of the tests. + // We already have reduced motion enabled and Playwright will wait for most of the animations anyway. + requestUtils.deactivatePlugin( + 'gutenberg-test-plugin-disables-the-css-animations' + ), + requestUtils.deleteAllPosts(), + requestUtils.deleteAllBlocks(), + requestUtils.resetPreferences(), + ] ); + await requestContext.dispose(); } diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js index 715d824979e5e..26e49966ad40c 100644 --- a/tools/webpack/interactivity.js +++ b/tools/webpack/interactivity.js @@ -53,4 +53,8 @@ module.exports = { }, ], }, + watchOptions: { + ignored: [ '**/node_modules' ], + aggregateTimeout: 500, + }, };