diff --git a/.eslintrc.js b/.eslintrc.js
index 725c6e3023708..529f4d3a2bcfa 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,7 +1,6 @@
/**
* External dependencies
*/
-const { escapeRegExp } = require( 'lodash' );
const glob = require( 'glob' ).sync;
const { join } = require( 'path' );
@@ -17,7 +16,8 @@ const { version } = require( './package' );
* @type {string}
*/
const majorMinorRegExp =
- escapeRegExp( version.replace( /\.\d+$/, '' ) ) + '(\\.\\d+)?';
+ version.replace( /\.\d+$/, '' ).replace( /[\\^$.*+?()[\]{}|]/g, '\\$&' ) +
+ '(\\.\\d+)?';
/**
* The list of patterns matching files used only for development purposes.
@@ -83,6 +83,7 @@ module.exports = {
'capitalize',
'chunk',
'clamp',
+ 'cloneDeep',
'compact',
'concat',
'countBy',
@@ -94,14 +95,18 @@ module.exports = {
'differenceWith',
'dropRight',
'each',
+ 'escapeRegExp',
'extend',
'findIndex',
'findKey',
'findLast',
+ 'first',
'flatMap',
'flatten',
'flattenDeep',
+ 'forEach',
'fromPairs',
+ 'has',
'identity',
'invoke',
'isArray',
@@ -118,6 +123,7 @@ module.exports = {
'isUndefined',
'keyBy',
'keys',
+ 'last',
'lowerCase',
'mapKeys',
'maxBy',
@@ -127,6 +133,7 @@ module.exports = {
'nth',
'once',
'overEvery',
+ 'partial',
'partialRight',
'random',
'reject',
@@ -146,6 +153,7 @@ module.exports = {
'toString',
'trim',
'truncate',
+ 'unionBy',
'uniq',
'uniqBy',
'uniqueId',
diff --git a/.github/workflows/stale-issue-flaky-test.yml b/.github/workflows/stale-issue-flaky-test.yml
new file mode 100644
index 0000000000000..c17935e2eb144
--- /dev/null
+++ b/.github/workflows/stale-issue-flaky-test.yml
@@ -0,0 +1,19 @@
+name: 'Mark old flaky tests issues as stale'
+on:
+ schedule:
+ - cron: '20 1 * * *'
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ if: ${{ github.repository == 'WordPress/gutenberg' }}
+
+ steps:
+ - uses: actions/stale@996798eb71ef485dc4c7b4d3285842d714040c4a # v3.0.17
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ stale-issue-message: 'This issue has gone 30 days without any activity.'
+ days-before-stale: 30
+ days-before-close: 1
+ only-labels: '[Type] Flaky Test'
+ stale-issue-label: '[Status] Stale'
diff --git a/bin/api-docs/are-api-docs-unstaged.js b/bin/api-docs/are-api-docs-unstaged.js
index ed2b50fb6fa12..06d1b4b58685c 100644
--- a/bin/api-docs/are-api-docs-unstaged.js
+++ b/bin/api-docs/are-api-docs-unstaged.js
@@ -35,7 +35,7 @@ const getUnstagedReadmes = () =>
'\n',
'Some API docs may be out of date:',
unstagedReadmes.toString(),
- 'Either build and stage them with npm run docs:blocks or continue with --no-verify.',
+ 'Either build and stage them with npm run docs:build or continue with --no-verify.',
'\n'
)
);
diff --git a/bin/plugin/commands/changelog.js b/bin/plugin/commands/changelog.js
index 520e9c60a29e1..7c8ad778d3cbf 100644
--- a/bin/plugin/commands/changelog.js
+++ b/bin/plugin/commands/changelog.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-const { groupBy, escapeRegExp, flow } = require( 'lodash' );
+const { groupBy, flow } = require( 'lodash' );
const Octokit = require( '@octokit/rest' );
const { sprintf } = require( 'sprintf-js' );
const semver = require( 'semver' );
@@ -185,6 +185,17 @@ const REWORD_TERMS = {
docs: 'documentation',
};
+/**
+ * Escapes the RegExp special characters.
+ *
+ * @param {string} string Input string.
+ *
+ * @return {string} Regex-escaped string.
+ */
+function escapeRegExp( string ) {
+ return string.replace( /[\\^$.*+?()[\]{}|]/g, '\\$&' );
+}
+
/**
* Returns candidates based on whether the given labels
* are part of the allowed list.
diff --git a/changelog.txt b/changelog.txt
index e10d9c84a402d..0c5250af02d24 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,441 @@
== Changelog ==
+= 14.0.3 =
+
+## Changelog
+
+### Bug Fixes
+
+- Fix GitHub Actions failure due to duplicate functions. ([44052](https://github.com/WordPress/gutenberg/pull/44052))
+
+## Contributors
+
+The following contributors merged PRs in this release:
+
+@t-hamano
+
+
+= 14.0.2 =
+
+
+
+## Changelog
+
+### Bug Fixes
+
+- Layout: Fix undefined index notice when attempting to check layout type. ([43756](https://github.com/WordPress/gutenberg/pull/43756))
+- List block: Prevent error upon pasting when list is empty. ([43761](https://github.com/WordPress/gutenberg/pull/43761))
+
+
+## First time contributors
+
+The following PRs were merged by first time contributors:
+
+
+
+## Contributors
+
+The following contributors merged PRs in this release:
+
+@andrewserong @oandregal
+
+
+= 14.0.1 =
+
+
+
+## Changelog
+
+### Bug Fixes
+
+#### Post Editor
+- Fix layout applied to post editor on classic themes. ([43755](https://github.com/WordPress/gutenberg/pull/43755))
+
+
+## First time contributors
+
+The following PRs were merged by first time contributors:
+
+
+
+## Contributors
+
+The following contributors merged PRs in this release:
+
+@tellthemachines
+
+
+= 14.0.0 =
+
+## Changelog
+
+### Enhancements
+
+- Add optional capture group to URL regex in wp-env. ([43200](https://github.com/WordPress/gutenberg/pull/43200))
+- Core Data: Add canRead to useResourcePermissions. ([43484](https://github.com/WordPress/gutenberg/pull/43484))
+- Element: Remove `enzyme` from `platform` test. ([43531](https://github.com/WordPress/gutenberg/pull/43531))
+- [Create-block] Add `--variant` flag to allow creation of different block type variants. ([41289](https://github.com/WordPress/gutenberg/pull/41289))
+- Use str_starts_with. ([43371](https://github.com/WordPress/gutenberg/pull/43371))
+- [Create Block] Adding a `--no-plugin` flag to scaffold only block files. ([41642](https://github.com/WordPress/gutenberg/pull/41642))
+- Customize widgets: Fix top contents cutoff in keyboard shortcuts. ([43391](https://github.com/WordPress/gutenberg/pull/43391))
+
+#### Design Tools
+
+As part of the effort to improve design tool consistency for typography and spacing on blocks ([43242](https://github.com/WordPress/gutenberg/issues/43242)), there's been a lot of work done to blocks during this release to add missing supports to them.
+
+Added typography support:
+
+- Paragraph: ([39642](https://github.com/WordPress/gutenberg/pull/39642))
+- Categories List: ([43254](https://github.com/WordPress/gutenberg/pull/43254))
+- Code Block: ([43255](https://github.com/WordPress/gutenberg/pull/43255))
+- Column Block: ([43252](https://github.com/WordPress/gutenberg/pull/43252))
+- Columns Block: ([43253](https://github.com/WordPress/gutenberg/pull/43253))
+- Comment Author Name: ([43256](https://github.com/WordPress/gutenberg/pull/43256))
+- Comment Date: ([43262](https://github.com/WordPress/gutenberg/pull/43262))
+- Comment Edit Link: ([43263](https://github.com/WordPress/gutenberg/pull/43263))
+- Comment Template: ([43266](https://github.com/WordPress/gutenberg/pull/43266))
+- Comments Content: ([43257](https://github.com/WordPress/gutenberg/pull/43257))
+- Comments Pagination: ([43288](https://github.com/WordPress/gutenberg/pull/43288), [43289](https://github.com/WordPress/gutenberg/pull/43289), [43290](https://github.com/WordPress/gutenberg/pull/43290), [43287](https://github.com/WordPress/gutenberg/pull/43287))
+- Comments Reply Link: ([43264](https://github.com/WordPress/gutenberg/pull/43264))
+- Comments Title: ([43291](https://github.com/WordPress/gutenberg/pull/43291))
+- Comments: ([43286](https://github.com/WordPress/gutenberg/pull/43286))
+- Cover: ([43298](https://github.com/WordPress/gutenberg/pull/43298))
+- Group: ([43308](https://github.com/WordPress/gutenberg/pull/43308))
+- Media & Text: ([43314](https://github.com/WordPress/gutenberg/pull/43314))
+- Navigation: ([43542](https://github.com/WordPress/gutenberg/pull/43542))
+- Post Author Biography: ([43318](https://github.com/WordPress/gutenberg/pull/43318))
+- Post Author Name: ([43319](https://github.com/WordPress/gutenberg/pull/43319))
+- Post Author: ([43317](https://github.com/WordPress/gutenberg/pull/43317))
+- Post Comments Count: ([43321](https://github.com/WordPress/gutenberg/pull/43321))
+- Post Comments Link: ([43338](https://github.com/WordPress/gutenberg/pull/43338))
+- Post Date: ([43340](https://github.com/WordPress/gutenberg/pull/43340))
+- Post Navigation Link: ([43344](https://github.com/WordPress/gutenberg/pull/43344))
+- Post Template: ([43342](https://github.com/WordPress/gutenberg/pull/43342))
+- Post Terms: ([43343](https://github.com/WordPress/gutenberg/pull/43343))
+- Preformatted: ([43345](https://github.com/WordPress/gutenberg/pull/43345))
+- Pullquote: ([43346](https://github.com/WordPress/gutenberg/pull/43346))
+- Table of Contents: ([43509](https://github.com/WordPress/gutenberg/pull/43509))
+
+Added spacing supports:
+
+- Paragraph: Add spacing block supports. ([43455](https://github.com/WordPress/gutenberg/pull/43455))
+- Heading: Add padding support. ([43454](https://github.com/WordPress/gutenberg/pull/43454))
+- List: Add spacing block supports. ([43402](https://github.com/WordPress/gutenberg/pull/43402))
+- Media & Text: Add spacing block supports. ([43456](https://github.com/WordPress/gutenberg/pull/43456))
+- Post Date: Add spacing support. ([43406](https://github.com/WordPress/gutenberg/pull/43406))
+- Post Title: Add padding support. ([43457](https://github.com/WordPress/gutenberg/pull/43457))
+- Query Title: Add padding support. ([43458](https://github.com/WordPress/gutenberg/pull/43458))
+- Spacer: Add spacing block supports. ([43366](https://github.com/WordPress/gutenberg/pull/43366))
+- Table of contents: Add spacing supports. ([43368](https://github.com/WordPress/gutenberg/pull/43368))
+- Table: Add spacing block supports. ([43370](https://github.com/WordPress/gutenberg/pull/43370))
+- Tag cloud: Add spacing block supports. ([43367](https://github.com/WordPress/gutenberg/pull/43367))
+- Term description: Add spacing block supports. ([43372](https://github.com/WordPress/gutenberg/pull/43372))
+- Verse: Add margin support. ([43461](https://github.com/WordPress/gutenberg/pull/43461))
+- Video: Add spacing block supports. ([43365](https://github.com/WordPress/gutenberg/pull/43365))
+
+Added other supports:
+
+- Gallery: Add background color block supports. ([43294](https://github.com/WordPress/gutenberg/pull/43294))
+- Post Featured Image: Add border support applied to inner img. ([42847](https://github.com/WordPress/gutenberg/pull/42847))
+- Social Links: Enable alpha on color pickers. ([43453](https://github.com/WordPress/gutenberg/pull/43453))
+- Social links: Add background color block supports. ([43293](https://github.com/WordPress/gutenberg/pull/43293))
+- Table of contents block: Add color block supports. ([43363](https://github.com/WordPress/gutenberg/pull/43363))
+
+
+#### Components
+- (Custom)SelectControl: Refresh and refactor chevron. ([42962](https://github.com/WordPress/gutenberg/pull/42962))
+- Always use `screen` for `testing-library` queries. ([43152](https://github.com/WordPress/gutenberg/pull/43152))
+- Autocomplete: Use KeyboardEvent.code instead of KeyboardEvent.keyCode. ([43432](https://github.com/WordPress/gutenberg/pull/43432))
+- Card: Migrate to TypeScript. ([42941](https://github.com/WordPress/gutenberg/pull/42941))
+- ComboboxControl: Normalize hyphen-like Unicode characters to ASCII hyphens when matching search queries. ([42942](https://github.com/WordPress/gutenberg/pull/42942))
+- CustomGradientPicker: Use KeyboardEvent.code instead of KeyboardEvent.keyCode. ([43437](https://github.com/WordPress/gutenberg/pull/43437))
+- DateTimePicker: Replace react-dates and moment with useLilius and date-fns. ([43005](https://github.com/WordPress/gutenberg/pull/43005))
+- FocalPointPicker: Use KeyboardEvent.code, partially refactor tests to modern RTL and user-event. ([43441](https://github.com/WordPress/gutenberg/pull/43441))
+- FontSizePicker: Add a flag to remove bottom margin. ([43062](https://github.com/WordPress/gutenberg/pull/43062))
+- FormTokenField: Add the ability to auto-select first matching suggestion for incomplete token. ([42527](https://github.com/WordPress/gutenberg/pull/42527))
+- FormTokenField: Use KeyboardEvent.code, refactor tests to model RTL and user-event. ([43442](https://github.com/WordPress/gutenberg/pull/43442))
+- Modal: Use code instead of keyCode for keyboard events. ([43429](https://github.com/WordPress/gutenberg/pull/43429))
+- Popover: Move eslint-disable comment to the correct deps array. ([43320](https://github.com/WordPress/gutenberg/pull/43320))
+- ToggleGroupControl: Improve styling for icon options. ([43060](https://github.com/WordPress/gutenberg/pull/43060))
+
+#### Block Library
+- Add a setting to hide the prefix in the archive title. ([42594](https://github.com/WordPress/gutenberg/pull/42594))
+- Embed: Update Reddit icon. ([43326](https://github.com/WordPress/gutenberg/pull/43326))
+- Post date: Add option to display as the last modified date. ([42312](https://github.com/WordPress/gutenberg/pull/42312))
+- Reset focalPoint after replacing the cover image. ([42859](https://github.com/WordPress/gutenberg/pull/42859))
+- Social Link: Update Reddit icon and color to match brand guidelines. ([43325](https://github.com/WordPress/gutenberg/pull/43325))
+- Try "constrained" content width as new layout type. ([42763](https://github.com/WordPress/gutenberg/pull/42763))
+- Try: Add a clickable Group setup state. ([40664](https://github.com/WordPress/gutenberg/pull/40664))
+- Use page list instead of placeholder as fallback. ([42735](https://github.com/WordPress/gutenberg/pull/42735))
+- [Query Loop]: Honour intended post type when previewing patterns and when replacing them with patterns. ([43285](https://github.com/WordPress/gutenberg/pull/43285))
+- List: Use nested blocks. ([42711](https://github.com/WordPress/gutenberg/pull/42711))
+
+#### Global Styles
+- BlockGap: Add axial gap option to global styles where available. ([42490](https://github.com/WordPress/gutenberg/pull/42490))
+- BlockGap: Add support for spacing presets. ([43296](https://github.com/WordPress/gutenberg/pull/43296))
+- Spacing presets: Add check for 0 spacing steps. ([43105](https://github.com/WordPress/gutenberg/pull/43105))
+- Spacing presets: Add support for margins. ([43246](https://github.com/WordPress/gutenberg/pull/43246))
+- Spacing presets: Implement disabling of custom space sizes. ([43216](https://github.com/WordPress/gutenberg/pull/43216))
+- Pseudo-elements supports on button elements. ([43088](https://github.com/WordPress/gutenberg/pull/43088))
+
+#### CSS & Styling
+- Placeholder: Add blurred background to work in nested cases. ([43379](https://github.com/WordPress/gutenberg/pull/43379))
+- Placeholder: Refactor and simplify dashed placeholders used for Featured Image & Site Logo. ([43228](https://github.com/WordPress/gutenberg/pull/43228))
+
+#### Data Layer
+- [data] Export the type for the combineReducers export. ([43516](https://github.com/WordPress/gutenberg/pull/43516))
+
+#### Site Editor
+- Template Part: Allow changing properties in focus mode. ([43151](https://github.com/WordPress/gutenberg/pull/43151))
+
+#### Block Editor
+- Refactor `LinkControl` tests to `@testing-library`. ([43147](https://github.com/WordPress/gutenberg/pull/43147))
+
+
+### Bug Fixes
+
+- Create Block: Refactor handling for template variants. ([43481](https://github.com/WordPress/gutenberg/pull/43481))
+- Fix no-results grammar. ([43168](https://github.com/WordPress/gutenberg/pull/43168))
+- Fix spinner causing a flash when loading site editor. ([43226](https://github.com/WordPress/gutenberg/pull/43226))
+- Image: Fix unclickable buttons. ([43361](https://github.com/WordPress/gutenberg/pull/43361))
+- Keycodes: Fix display of symbols in keyboard shortcut modal. ([43137](https://github.com/WordPress/gutenberg/pull/43137))
+- MediaReplaceFlow: Reset default LinkControl margins. ([43156](https://github.com/WordPress/gutenberg/pull/43156))
+- Post title: Fix pasting into existing content. ([43123](https://github.com/WordPress/gutenberg/pull/43123))
+- [Block Editor]: Fix block switcher label to take into account block variations. ([43309](https://github.com/WordPress/gutenberg/pull/43309))
+- [useEntityRecord] Pass the correct kind, name, and recordId to getEditedEntityRecord. ([43517](https://github.com/WordPress/gutenberg/pull/43517))
+- wp-env: Set core source to latest when null. ([43133](https://github.com/WordPress/gutenberg/pull/43133))
+
+#### Block Library
+- Ensure the block toolbar doesn't overlap block by modifying forcePosition and shift popover props. ([42887](https://github.com/WordPress/gutenberg/pull/42887))
+- Ensure pagination numbers have an href in block edit function. ([43354](https://github.com/WordPress/gutenberg/pull/43354))
+- Fix Post Featured Image border attributes. ([43426](https://github.com/WordPress/gutenberg/pull/43426))
+- Fix classic block converted to regular blocks when clicking new 'Edit visually' button. ([43219](https://github.com/WordPress/gutenberg/pull/43219))
+- Fix featured image being unselectable using arrow keys. ([43323](https://github.com/WordPress/gutenberg/pull/43323))
+- Fix navigation block undefined index error on frontend. ([43302](https://github.com/WordPress/gutenberg/pull/43302))
+- Gallery block: Ensure image attributes copy correctly between transforms. ([42796](https://github.com/WordPress/gutenberg/pull/42796))
+- Home Link: Fix undo trap. ([43112](https://github.com/WordPress/gutenberg/pull/43112))
+- List v2: Copy list wrapper when copying list items. ([42860](https://github.com/WordPress/gutenberg/pull/42860))
+- Navigation: Page List fix missing padding. ([43358](https://github.com/WordPress/gutenberg/pull/43358))
+- Prevent query block from looping in classic themes. ([43221](https://github.com/WordPress/gutenberg/pull/43221))
+- Pullquote block: Avoid text-align settings affecting block width and font size. ([43188](https://github.com/WordPress/gutenberg/pull/43188))
+- Pullquote block: Remove font definition from the default block styles. ([43195](https://github.com/WordPress/gutenberg/pull/43195))
+- taxonomy-controls.js: Change REST context to "view" when fetching taxonomy terms. ([43274](https://github.com/WordPress/gutenberg/pull/43274))
+- Home Link: Properly close tags. ([43706](https://github.com/WordPress/gutenberg/pull/43706))
+- Social Link: Fix background color on WhatsApp icon. ([43683](https://github.com/WordPress/gutenberg/pull/43683))
+
+#### Components
+- (Custom)SelectControl: Truncate long options. ([43301](https://github.com/WordPress/gutenberg/pull/43301))
+- AlignmentMatrixControl: Fix `width` bug. ([43482](https://github.com/WordPress/gutenberg/pull/43482))
+- ColorPalette, GradientPicker: Fix color picker popover positioning. ([42989](https://github.com/WordPress/gutenberg/pull/42989))
+- ColorPalette: Make sure "key" is unique when iterating over color entries with the same value. ([43096](https://github.com/WordPress/gutenberg/pull/43096))
+- Dropdown: Anchor popover to the dropdown wrapper (instead of the toggle). ([43377](https://github.com/WordPress/gutenberg/pull/43377))
+- Fix block toolbar offset in site editor when toggling sidebars. ([43172](https://github.com/WordPress/gutenberg/pull/43172))
+- Fix popover glitch that results in incorrect toolbar positioning in site editor. ([43267](https://github.com/WordPress/gutenberg/pull/43267))
+- Improve appearance of controls in the Global Styles Typography panel. ([43304](https://github.com/WordPress/gutenberg/pull/43304))
+- Popover: Fix and improve opening animation, use framer motion. ([43186](https://github.com/WordPress/gutenberg/pull/43186))
+- Popover: Make sure offset middleware always applies the latest frame offset values. ([43329](https://github.com/WordPress/gutenberg/pull/43329))
+- Refactor `Guide` tests to `@testing-library/react`. ([43380](https://github.com/WordPress/gutenberg/pull/43380))
+
+#### Global Styles
+- Check for recursive dynamic reference in the site editor. ([43166](https://github.com/WordPress/gutenberg/pull/43166))
+- Duotone: Prevent early return blocking other style generation. ([43300](https://github.com/WordPress/gutenberg/pull/43300))
+- Fix dynamic references on the site editor. ([42976](https://github.com/WordPress/gutenberg/pull/42976))
+- Fix error in handling spacing preset slugs. ([43237](https://github.com/WordPress/gutenberg/pull/43237))
+- Layout: Re-instate alignwide and alignfull in flow layout get alignments. ([43502](https://github.com/WordPress/gutenberg/pull/43502))
+- Spacing presets: Fix/minor issues noted in initial UI PR. ([43214](https://github.com/WordPress/gutenberg/pull/43214))
+- Layout: Fix has-global-padding classname for constrained layouts without contentSize. ([43689](https://github.com/WordPress/gutenberg/pull/43689))
+
+#### Block Editor
+- Fix Cmd+A issue in Storybook. ([43145](https://github.com/WordPress/gutenberg/pull/43145))
+- Fix drag and drop indicator before first block and after last block. ([43135](https://github.com/WordPress/gutenberg/pull/43135))
+- Fix spinner position in URLInput component. ([43472](https://github.com/WordPress/gutenberg/pull/43472))
+- Partial select: Fix selecting into image. ([42983](https://github.com/WordPress/gutenberg/pull/42983))
+
+#### Design Tools
+- Border Radius: Prevent invalid css unit only styles or empty radii style attribute. ([42409](https://github.com/WordPress/gutenberg/pull/42409))
+- Border Support: Fix disabling of border style control. ([43109](https://github.com/WordPress/gutenberg/pull/43109))
+- Post Comments Count: Prevent text-decoration from affecting warning. ([43497](https://github.com/WordPress/gutenberg/pull/43497))
+
+#### Site Editor
+- Do not show scrollbar when toolbar overflows the editor wrapper. ([43332](https://github.com/WordPress/gutenberg/pull/43332))
+- Fix template part focus mode resizable editor height. ([43408](https://github.com/WordPress/gutenberg/pull/43408))
+
+#### npm Packages
+- Jest Preset: Ignore `is-plain-obj` transformation. ([43179](https://github.com/WordPress/gutenberg/pull/43179))
+- Jest Preset: Improve `is-plain-obj` transformation ignore. ([43271](https://github.com/WordPress/gutenberg/pull/43271))
+
+#### Widgets Editor
+- Fix legacy widget form positioning in customizer. ([43297](https://github.com/WordPress/gutenberg/pull/43297))
+
+#### CSS & Styling
+- Group/Stack/Row: Scope the dashed placeholder rules. ([43169](https://github.com/WordPress/gutenberg/pull/43169))
+
+#### List View
+- Ensure long anchors don't cause the List View to extend. ([43134](https://github.com/WordPress/gutenberg/pull/43134))
+
+#### Post Editor
+- Post Template: Don't fetch settings and templates for non-admin users. ([42845](https://github.com/WordPress/gutenberg/pull/42845))
+
+#### Patterns
+- Fix custom placeholder not displaying on subsequent Paragraph blocks. ([42519](https://github.com/WordPress/gutenberg/pull/42519))
+
+### Accessibility
+- Block Editor: Remove `aria-selected` from `LinkPreview`. ([43279](https://github.com/WordPress/gutenberg/pull/43279))
+- Block Editor: Replace `aria-owns` with `aria-controls` in `URLInput`. ([43278](https://github.com/WordPress/gutenberg/pull/43278))
+- Separator: Disable the contrastChecker via block.json. ([43357](https://github.com/WordPress/gutenberg/pull/43357))
+- Fix Top toolbar buttons tooltips and style when 'Show button text labels' is enabled. ([42815](https://github.com/WordPress/gutenberg/pull/42815))
+
+### Performance
+
+Lodash is known to unnecessarily inflate the bundle size of packages, and in most cases, it can be replaced with native language functionality. See these for more information and rationale: ([16938](https://github.com/WordPress/gutenberg/issues/16938#issuecomment-602837246), [17025](https://github.com/WordPress/gutenberg/issues/17025), [39495](https://github.com/WordPress/gutenberg/issues/39495))
+
+The related PRs are: [43118](https://github.com/WordPress/gutenberg/pull/43118), [43306](https://github.com/WordPress/gutenberg/pull/43306), [43389](https://github.com/WordPress/gutenberg/pull/43389), [42466](https://github.com/WordPress/gutenberg/pull/42466), [43362](https://github.com/WordPress/gutenberg/pull/43362), [43100](https://github.com/WordPress/gutenberg/pull/43100), [43420](https://github.com/WordPress/gutenberg/pull/43420), [43117](https://github.com/WordPress/gutenberg/pull/43117), [42467](https://github.com/WordPress/gutenberg/pull/42467), [42502](https://github.com/WordPress/gutenberg/pull/42502), [43220](https://github.com/WordPress/gutenberg/pull/43220), [43224](https://github.com/WordPress/gutenberg/pull/43224), [43258](https://github.com/WordPress/gutenberg/pull/43258), [43374](https://github.com/WordPress/gutenberg/pull/43374), [43518](https://github.com/WordPress/gutenberg/pull/43518), [43474](https://github.com/WordPress/gutenberg/pull/43474), [43229](https://github.com/WordPress/gutenberg/pull/43229), [43411](https://github.com/WordPress/gutenberg/pull/43411), [42465](https://github.com/WordPress/gutenberg/pull/42465), [43231](https://github.com/WordPress/gutenberg/pull/43231), [43479](https://github.com/WordPress/gutenberg/pull/43479), [43330](https://github.com/WordPress/gutenberg/pull/43330), [43182](https://github.com/WordPress/gutenberg/pull/43182), [43419](https://github.com/WordPress/gutenberg/pull/43419)
+
+### Experiments
+
+#### Components
+- Font size picker: Use t-shirt sizes for the ToggleGroupControl component. ([43074](https://github.com/WordPress/gutenberg/pull/43074))
+
+
+### Documentation
+
+- Add documentation for useRootPaddingAwareAlignments in theme.json. ([43463](https://github.com/WordPress/gutenberg/pull/43463))
+- Comma is missing. ([43446](https://github.com/WordPress/gutenberg/pull/43446))
+- Convert HTML to Markdown in changelog for 13.9. ([43324](https://github.com/WordPress/gutenberg/pull/43324))
+- Handbook: Fix format API example link. ([43477](https://github.com/WordPress/gutenberg/pull/43477))
+- Stabilize the useResourcePermissions hook. ([43268](https://github.com/WordPress/gutenberg/pull/43268))
+- [Docs] Replace useState with edit in useEntityRecord usage examples. ([43270](https://github.com/WordPress/gutenberg/pull/43270))
+- Block Editor Handbook: Added missing codetabs end marker. ([43185](https://github.com/WordPress/gutenberg/pull/43185))
+- Docs: Fix some typos. ([43175](https://github.com/WordPress/gutenberg/pull/43175))
+
+
+### Code Quality
+
+- PHP: Use str_contains(). ([43382](https://github.com/WordPress/gutenberg/pull/43382))
+- PHP: Use str_starts_with. ([43410](https://github.com/WordPress/gutenberg/pull/43410))
+- Style engine: Pass options to CSS static methods. ([43399](https://github.com/WordPress/gutenberg/pull/43399))
+- Style engine tweaks. ([43303](https://github.com/WordPress/gutenberg/pull/43303))
+- Navigation block - minor refactor to classic menu conversion code. ([43081](https://github.com/WordPress/gutenberg/pull/43081))
+- Data: Bundle TypeScript types with the data package. ([43315](https://github.com/WordPress/gutenberg/pull/43315))
+- getTemplateInfo: Return stable reference to an empty object. ([43155](https://github.com/WordPress/gutenberg/pull/43155))
+- Remove duplicated 'import' comments. ([43478](https://github.com/WordPress/gutenberg/pull/43478))
+- Disabled: Migrate to TypeScript. ([42708](https://github.com/WordPress/gutenberg/pull/42708))
+
+#### Components
+- Clean up unused and duplicate `COLORS` values. ([43445](https://github.com/WordPress/gutenberg/pull/43445))
+- Packages: Ensure dependencies use version ranges. ([43355](https://github.com/WordPress/gutenberg/pull/43355))
+- Swatch: Remove component in favor of ColorIndicator. ([43068](https://github.com/WordPress/gutenberg/pull/43068))
+- Update/floating UI version. ([43206](https://github.com/WordPress/gutenberg/pull/43206))
+
+#### List View
+- Block list: Update block list view preferences name for consistency. ([43494](https://github.com/WordPress/gutenberg/pull/43494))
+
+#### Widgets Editor
+- Use useResourcePermissions in block-library and the widgets editor. ([43305](https://github.com/WordPress/gutenberg/pull/43305))
+
+#### Block Editor
+- Rich Text: Eliminate second scan when getting text content. ([43207](https://github.com/WordPress/gutenberg/pull/43207))
+
+#### Global Styles
+- Theme_JSON: Use existing append_to_selector for pseudo-elements. ([43167](https://github.com/WordPress/gutenberg/pull/43167))
+- Enable appearance tools via theme_support. ([43337](https://github.com/WordPress/gutenberg/pull/43337))
+
+
+#### Testing
+- Migrate wp editor meta box test to Playwright. ([41519](https://github.com/WordPress/gutenberg/pull/41519))
+- PHPCS: Exclude PHPUnit tests from file and class name sniffs (for Core parity). ([43131](https://github.com/WordPress/gutenberg/pull/43131))
+- PHPUnit: Let PHPUnit Polyfills match PHPUnit version. ([43334](https://github.com/WordPress/gutenberg/pull/43334))
+- PHPUnit: Turns on PHP notices and deprecations. ([43102](https://github.com/WordPress/gutenberg/pull/43102))
+- Update incorrect quote end-to-end test snapshot. ([43407](https://github.com/WordPress/gutenberg/pull/43407))
+- Update test fixture for performance test. ([43359](https://github.com/WordPress/gutenberg/pull/43359))
+- Quote: Stabilise flaky end-to-end test. ([43460](https://github.com/WordPress/gutenberg/pull/43460))
+
+#### Build Tooling
+- Build Tools: Fix typo in performance tests workflow. ([43153](https://github.com/WordPress/gutenberg/pull/43153))
+- Packages: Update the minimum required Node.js version to 14 for tools. ([43141](https://github.com/WordPress/gutenberg/pull/43141))
+- ESLint Plugin: Remove all rules targeting test files from recommended presets. ([43272](https://github.com/WordPress/gutenberg/pull/43272))
+- Ignore library CSS and built CSS in stylelint. ([42027](https://github.com/WordPress/gutenberg/pull/42027))
+- Fix 'Mark issues stale after needs testing for 30 days' workflow. ([43545](https://github.com/WordPress/gutenberg/pull/43545))
+
+#### npm Packages
+- Packages: Replace `is-plain-obj` with `is-plain-object`. ([43511](https://github.com/WordPress/gutenberg/pull/43511))
+
+
+#### Components
+- (Custom)GradientPicker: Add flag to remove margins. ([43387](https://github.com/WordPress/gutenberg/pull/43387))
+- AlignmentMatrixControl: Improve stories. ([43438](https://github.com/WordPress/gutenberg/pull/43438))
+- AnglePickerControl: Add flag to remove bottom margin. ([43160](https://github.com/WordPress/gutenberg/pull/43160))
+- ComboboxControl: Add flag to remove bottom margin. ([43165](https://github.com/WordPress/gutenberg/pull/43165))
+- CustomSelectControl: Deprecate constrained width style. ([43230](https://github.com/WordPress/gutenberg/pull/43230))
+- DuotonePicker/DuotoneSwatch: Add stories. ([43225](https://github.com/WordPress/gutenberg/pull/43225))
+- Storybook: Add margin checker tool. ([43223](https://github.com/WordPress/gutenberg/pull/43223))
+- ToggleGroupControl: Improve stories for documentation view. ([43265](https://github.com/WordPress/gutenberg/pull/43265))
+- ToolsPanel: Tighten grid gaps. ([43424](https://github.com/WordPress/gutenberg/pull/43424))
+
+#### Block Library
+- Buttons: Update selectors to work better with button elements. ([43022](https://github.com/WordPress/gutenberg/pull/43022))
+- Comments block: Remove empty block wrapper. ([43383](https://github.com/WordPress/gutenberg/pull/43383))
+- Group block: Update description to remove "layout." ([43498](https://github.com/WordPress/gutenberg/pull/43498))
+- Image: Try different resting state for placeholder, alternate version. ([43180](https://github.com/WordPress/gutenberg/pull/43180))
+- Navigation: Try to improve the appender in an empty block. ([43115](https://github.com/WordPress/gutenberg/pull/43115))
+- Polish placeholder radius and enable duotone on image setup state. ([43425](https://github.com/WordPress/gutenberg/pull/43425))
+- Pullquote: Use inline rich text instead of multiline. ([43210](https://github.com/WordPress/gutenberg/pull/43210))
+- [Blocks] Paragraph and Heading: Add gradient support. ([43119](https://github.com/WordPress/gutenberg/pull/43119))
+
+#### Patterns
+- Bundle new collection of Header and Footer block patterns. ([43157](https://github.com/WordPress/gutenberg/pull/43157))
+- Mark which attributes of the image should be considered content. ([43280](https://github.com/WordPress/gutenberg/pull/43280))
+- Prefer _x() for i18n context in core patterns. ([43409](https://github.com/WordPress/gutenberg/pull/43409))
+
+#### Design Tools
+- Add margin and padding supports to Audio block. ([43351](https://github.com/WordPress/gutenberg/pull/43351))
+- Add margin/padding support to Archives block. ([43350](https://github.com/WordPress/gutenberg/pull/43350))
+
+#### Global Styles
+- Add documentation about spacing presets. ([43349](https://github.com/WordPress/gutenberg/pull/43349))
+- Spacing presets: Add editor UI support. ([42173](https://github.com/WordPress/gutenberg/pull/42173))
+
+#### Site Editor
+- [Site Editor]: Add success notice upon template creation. ([43430](https://github.com/WordPress/gutenberg/pull/43430))
+
+#### CSS & Styling
+- Style engine: Use style engine for block supports CSS in editor. ([43055](https://github.com/WordPress/gutenberg/pull/43055))
+- Style engine: Remove `enqueue` flag. ([43103](https://github.com/WordPress/gutenberg/pull/43103))
+
+#### Block Editor
+- Merging blocks: Allow x to be merged into wrapper blocks (quote, list, group...). ([42780](https://github.com/WordPress/gutenberg/pull/42780))
+
+### Tools
+
+#### Testing
+- Refactor `Button` tests to `@testing-library/react`. ([42981](https://github.com/WordPress/gutenberg/pull/42981))
+- Refactor `Guide` `PageControl` tests to @testing-library/react. ([43148](https://github.com/WordPress/gutenberg/pull/43148))
+- Refactor `MenuGroup` tests to `@testing-library/react`. ([43275](https://github.com/WordPress/gutenberg/pull/43275))
+- Refactor `withSpokenMessages` tests to `@testing-library`. ([43273](https://github.com/WordPress/gutenberg/pull/43273))
+- Editor: Refactor `PostAuthorCheck` tests to `@testing-library`. ([43176](https://github.com/WordPress/gutenberg/pull/43176))
+- Editor: Refactor `ThemeSupportCheck` tests to `@testing-library/react`. ([43532](https://github.com/WordPress/gutenberg/pull/43532))
+- Editor: Refactor a few component tests to `@testing-library/react`. ([43376](https://github.com/WordPress/gutenberg/pull/43376))
+- Components: Refactor Placeholder tests to @testing-library/react. ([43069](https://github.com/WordPress/gutenberg/pull/43069))
+- Components: Refactor `Tooltip` tests to `@testing-library/react`. ([43061](https://github.com/WordPress/gutenberg/pull/43061))
+
+## First time contributors
+
+The following PRs were merged by first time contributors:
+
+- @drzraf: taxonomy-controls.js: Change REST context to "view" when fetching taxonomy terms. ([43274](https://github.com/WordPress/gutenberg/pull/43274))
+- @markbiek: ComboboxControl: Normalize hyphen-like Unicode characters to ASCII hyphens when matching search queries. ([42942](https://github.com/WordPress/gutenberg/pull/42942))
+- @randhirexpresstech: Add font family and text-decoration typography supports to paragraph blocks. ([39642](https://github.com/WordPress/gutenberg/pull/39642))
+- @Rink9: Migrate wp editor meta box test to Playwright. ([41519](https://github.com/WordPress/gutenberg/pull/41519))
+- @titusdmoore: Add optional capture group to URL regex in wp-env. ([43200](https://github.com/WordPress/gutenberg/pull/43200))
+
+
+## Contributors
+
+The following contributors merged PRs in this release:
+
+@aaronrobertshaw @adamziel @afercia @andrewserong @aristath @awps @carolinan @ciampo @derekblank @dinhtungdu @dmsnell @draganescu @drzraf @ellatrix @geriux @glendaviesnz @gziolo @hellofromtonya @hz-tyfoon @jasmussen @jostnes @kdevnel @MaggieCabrera @Mamaduka @markbiek @matiasbenedetto @mcsf @mirka @ndiego @noahtallen @noisysocks @ntsekouras @oandregal @ockham @paulopmt1 @pbking @ramonjd @randhirexpresstech @Rink9 @ryanwelcher @scruffian @SiobhyB @Soean @t-hamano @talldan @tellthemachines @titusdmoore @torounit @tyxla @walbo
+
+
= 13.9.0 =
## Changelog
diff --git a/docs/contributors/documentation/README.md b/docs/contributors/documentation/README.md
index 8bebe02c119db..f06abf9d0de7c 100644
--- a/docs/contributors/documentation/README.md
+++ b/docs/contributors/documentation/README.md
@@ -64,6 +64,25 @@ To add a new document requires a working JavaScript development environment to b
If you forget to run, `npm run docs:build` your PR will fail the static analysis check, since the `manifest.json` file is an uncommitted local change that must be committed.
+### Documenting Packages
+
+Package documentation is generated automatically by the documentation tool by pulling the contents of the README.md file located in the root of the package. Sometimes however, it is preferable to split the contents of the README out into smaller, easier to read portions.
+
+This can be accomplished by creating a `docs` directory in the package and adding `toc.json` file that contains references other markdown files also contained in the `docs` directory. The `toc.json` file should contain an array of pages to be added as sub-pages of the main README file. The formatting follows the [`manifest.json`](https://github.com/WordPress/gutenberg/blob/HEAD/docs/manifest.json) file that is generated automatically.
+
+In order for these pages to be nested under the main package name, be sure to set the `parent` property correctly. See the example below that adds child pages to the [`@wordpress/create-block` section](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/).
+
+```json
+[
+ {
+ "title": "@wordpress/create-block External Template",
+ "slug": "packages-create-block-external-template",
+ "markdown_source": "../packages/create-block/docs/external-template.md",
+ "parent": "packages-create-block"
+ }
+]
+```
+
### Using links
It's likely at some point you'll want to link to other internal documentation pages. It's worth emphasizing all documents can be browsed in different contexts:
diff --git a/docs/how-to-guides/README.md b/docs/how-to-guides/README.md
index 1aeee6c5c3b87..0f50bedf05579 100644
--- a/docs/how-to-guides/README.md
+++ b/docs/how-to-guides/README.md
@@ -30,7 +30,7 @@ Porting PHP meta boxes to blocks or sidebar plugins is highly encouraged, learn
By default, blocks provide their styles to enable basic support for blocks in themes without any change. Themes can add/override these styles, or rely on defaults.
-There are some advanced block features which require opt-in support in the theme. See [theme support](/docs/how-to-guides/themes/theme-support.md).
+There are some advanced block features which require opt-in support in the theme. See [theme support](/docs/how-to-guides/themes/theme-support.md) and [how to filter global styles](/docs/reference-guides/filters/global-styles-filters.md).
## Autocomplete
diff --git a/docs/how-to-guides/data-basics/1-data-basics-setup.md b/docs/how-to-guides/data-basics/1-data-basics-setup.md
index 5e171577b4090..d9abd07967c8c 100644
--- a/docs/how-to-guides/data-basics/1-data-basics-setup.md
+++ b/docs/how-to-guides/data-basics/1-data-basics-setup.md
@@ -163,29 +163,32 @@ add_action( 'admin_enqueue_scripts', 'load_custom_wp_admin_scripts' );
```json
{
- "name": "05-recipe-card-esnext",
- "version": "1.1.0",
- "private": true,
- "description": "Example: Recipe Card (ESNext).",
- "author": "The WordPress Contributors",
- "license": "GPL-2.0-or-later",
- "keywords": [ "WordPress", "block" ],
- "homepage": "https://github.com/WordPress/gutenberg-examples/",
- "repository": "git+https://github.com/WordPress/gutenberg-examples.git",
- "bugs": {
- "url": "https://github.com/WordPress/gutenberg-examples/issues"
- },
- "main": "build/index.js",
- "devDependencies": {
- "@wordpress/scripts": "^18.0.1"
- },
- "scripts": {
- "build": "wp-scripts build",
- "format": "wp-scripts format",
- "lint:js": "wp-scripts lint-js",
- "packages-update": "wp-scripts packages-update",
- "start": "wp-scripts start"
- }
+ "name": "09-code-data-basics-esnext",
+ "version": "1.1.0",
+ "private": true,
+ "description": "My first Gutenberg App",
+ "author": "The WordPress Contributors",
+ "license": "GPL-2.0-or-later",
+ "keywords": [
+ "WordPress",
+ "block"
+ ],
+ "homepage": "https://github.com/WordPress/gutenberg-examples/",
+ "repository": "git+https://github.com/WordPress/gutenberg-examples.git",
+ "bugs": {
+ "url": "https://github.com/WordPress/gutenberg-examples/issues"
+ },
+ "main": "build/index.js",
+ "devDependencies": {
+ "@wordpress/scripts": "^24.0.0"
+ },
+ "scripts": {
+ "build": "wp-scripts build",
+ "format": "wp-scripts format",
+ "lint:js": "wp-scripts lint-js",
+ "packages-update": "wp-scripts packages-update",
+ "start": "wp-scripts start"
+ }
}
```
@@ -209,4 +212,4 @@ Congratulations! You are now ready to start building the app!
- Previous part: [Introduction](/docs/how-to-guides/data-basics/README.md)
- Next part: [Building a basic list of pages](/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md)
-- (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/09-code-data-basics-esnext) in the gutenberg-examples repository
+- (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/non-block-examples/09-code-data-basics-esnext) in the gutenberg-examples repository
diff --git a/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md b/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md
index c830e9d3b21bd..e9d0c50d1d211 100644
--- a/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md
+++ b/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md
@@ -43,7 +43,7 @@ Before we start, let’s confirm we actually have some pages to fetch. Within WP
If it doesn’t, go ahead and create a few pages – you can use the same titles as on the screenshot above. Be sure to _publish_ and not just _save_ them.
-Now that we have the data to work with, let’s dive into the code. We will take advantage of the [`@wordpress/core-data` package](https://github.com/WordPress/gutenberg/tree/trunk/packages/core-data) package which provides resolvers, selectors, and actions to work with the WordPress core API. `@wordpress/core-data` builds on top of the [`@wordpress/data` package](https://github.com/WordPress/gutenberg/tree/trunk/packages/data).
+Now that we have the data to work with, let’s dive into the code. We will take advantage of the [`@wordpress/core-data`](https://github.com/WordPress/gutenberg/tree/trunk/packages/core-data) package which provides resolvers, selectors, and actions to work with the WordPress core API. `@wordpress/core-data` builds on top of the [`@wordpress/data`](https://github.com/WordPress/gutenberg/tree/trunk/packages/data) package.
To fetch the list of pages, we will use the [`getEntityRecords`](/docs/reference-guides/data/data-core/#getentityrecords) selector. In broad strokes, it will issue the correct API request, cache the results, and return the list of the records we need. Here’s how to use it:
@@ -53,6 +53,8 @@ wp.data.select( 'core' ).getEntityRecords( 'postType', 'page' )
If you run that following snippet in your browser’s dev tools, you will see it returns `null`. Why? The pages are only requested by the `getEntityRecords` resolver after first running the _selector_. If you wait a moment and re-run it, it will return the list of all pages.
+*Note: To run this type of command directly make sure your browser is displaying an instance of the block editor (any page will do). Otherwise the `select( 'core' )` function won't be available, and you'll get an error.*
+
Similarly, the `MyFirstApp` component needs to re-run the selector once the data is available. That’s exactly what the `useSelect` hook does:
```js
@@ -67,6 +69,14 @@ function MyFirstApp() {
);
// ...
}
+
+function PagesList({ pages }) {
+ // ...
+
+ {page.title.rendered}
+
+ // ...
+}
```
Note that we use an `import` statement inside index.js. This enables the plugin to automatically load the dependencies using `wp_enqueue_script`. Any references to `coreDataStore` are compiled to the same `wp.data` reference we use in browser's devtools.
@@ -353,6 +363,7 @@ import { SearchControl, Spinner } from '@wordpress/components';
import { useState, render } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { store as coreDataStore } from '@wordpress/core-data';
+import { decodeEntities } from '@wordpress/html-entities';
function MyFirstApp() {
const [ searchTerm, setSearchTerm ] = useState( '' );
@@ -431,4 +442,4 @@ All that’s left is to refresh the page and enjoy the brand new status indicato
* **Previous part:** [Setup](/docs/how-to-guides/data-basics/1-data-basics-setup.md)
* **Next part:** [Building an edit form](/docs/how-to-guides/data-basics/3-building-an-edit-form.md)
-* (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/09-code-data-basics-esnext) in the gutenberg-examples repository
+* (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/non-block-examples/09-code-data-basics-esnext) in the gutenberg-examples repository
diff --git a/docs/how-to-guides/data-basics/3-building-an-edit-form.md b/docs/how-to-guides/data-basics/3-building-an-edit-form.md
index 6fd8be0f75d4c..754a31f1bc492 100644
--- a/docs/how-to-guides/data-basics/3-building-an-edit-form.md
+++ b/docs/how-to-guides/data-basics/3-building-an-edit-form.md
@@ -59,7 +59,7 @@ Our button looks nice but doesn't do anything yet. To display an edit form, we n
```js
import { Button, TextControl } from '@wordpress/components';
-export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
+function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
return (
@@ -148,7 +148,7 @@ export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
}
```
-Now it should look like that:
+Now it should look like this:
![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/edit-form/form-populated.png)
@@ -160,7 +160,7 @@ There's one problem with our _Page title_ field: you can't edit it. It receives
You may have seen a pattern similar to this one in other React apps. It's known as a ["controlled component"](https://reactjs.org/docs/forms.html#controlled-components):
```js
-export function VanillaReactForm({ initialTitle }) {
+function VanillaReactForm({ initialTitle }) {
const [title, setTitle] = useState( initialTitle );
return (
select( coreDataStore ).getEditedEntityRecord( 'postType', 'page', pageId ),
[ pageId ]
@@ -281,7 +281,7 @@ Entity records are updated to reflect any saved changes right after the REST API
This is how the `EditPageForm` looks like with a working *Save* button:
```js
-export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
+function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
// ...
const { saveEditedEntityRecord } = useDispatch( coreDataStore );
const handleSave = () => saveEditedEntityRecord( 'postType', 'page', pageId );
@@ -303,7 +303,7 @@ export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
It works, but there's still one thing to fix: the form modal doesn't automatically close because we never call `onSaveFinished`. Lucky for us, `saveEditedEntityRecord` returns a promise that resolves once the save operation is finished. Let's take advantage of it in `EditPageForm`:
```js
-export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
+function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
// ...
const handleSave = async () => {
await saveEditedEntityRecord( 'postType', 'page', pageId );
@@ -324,7 +324,7 @@ We optimistically assumed that a *save* operation would always succeed. Unfortun
To tell the user when any of these happens, we have to make two adjustments. We don't want to close the form modal when the update fails. The promise returned by `saveEditedEntityRecord` is resolved with an updated record only if the update actually worked. When something goes wrong, it resolves with an empty value. Let's use it to keep the modal open:
```js
-export function EditPageForm( { pageId, onSaveFinished } ) {
+function EditPageForm( { pageId, onSaveFinished } ) {
// ...
const handleSave = async () => {
const updatedRecord = await saveEditedEntityRecord( 'postType', 'page', pageId );
@@ -346,7 +346,7 @@ wp.data.select( 'core' ).getLastEntitySaveError( 'postType', 'page', 9 )
Here's how we can use it in `EditPageForm`:
```js
-export function EditPageForm( { pageId, onSaveFinished } ) {
+function EditPageForm( { pageId, onSaveFinished } ) {
// ...
const { lastError, page } = useSelect(
select => ({
@@ -357,7 +357,7 @@ export function EditPageForm( { pageId, onSaveFinished } ) {
)
// ...
return (
- <>
+
);
}
```
@@ -375,7 +375,7 @@ Great! `EditPageForm` is now fully aware of errors.
Let's see that error message in action. We'll trigger an invalid update and let it fail. The post title is hard to break, so let's set a `date` property to `-1` instead – that's a guaranteed validation error:
```js
-export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
+function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
// ...
const handleChange = ( title ) => editEntityRecord( 'postType', 'page', pageId, { title, date: -1 } );
// ...
@@ -397,7 +397,7 @@ We're going to clear it up and communicate two states to the user: _Saving_ and
Let's use them in `EditPageForm`:
```js
-export function EditPageForm( { pageId, onSaveFinished } ) {
+function EditPageForm( { pageId, onSaveFinished } ) {
// ...
const { isSaving, hasEdits, /* ... */ } = useSelect(
select => ({
@@ -413,8 +413,7 @@ export function EditPageForm( { pageId, onSaveFinished } ) {
We can now use `isSaving` and `hasEdits` to display a spinner when saving is in progress and grey out the save button when there are no edits:
```js
-
-export function EditPageForm( { pageId, onSaveFinished } ) {
+function EditPageForm( { pageId, onSaveFinished } ) {
// ...
return (
// ...
@@ -479,7 +478,7 @@ function PageEditButton( { pageId } ) {
);
}
-export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
+function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
const { page, lastError, isSaving, hasEdits } = useSelect(
( select ) => ( {
page: select( coreDataStore ).getEditedEntityRecord( 'postType', 'page', pageId ),
@@ -541,4 +540,4 @@ export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
* **Previous part:** [Building a list of pages](/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md)
* **Next part:** Building a *New Page* form (coming soon)
-* (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/09-code-data-basics-esnext) in the gutenberg-examples repository
+* (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/non-block-examples/09-code-data-basics-esnext) in the gutenberg-examples repository
diff --git a/docs/how-to-guides/data-basics/4-building-a-create-page-form.md b/docs/how-to-guides/data-basics/4-building-a-create-page-form.md
index 36937974d118d..19aada07c2fc7 100644
--- a/docs/how-to-guides/data-basics/4-building-a-create-page-form.md
+++ b/docs/how-to-guides/data-basics/4-building-a-create-page-form.md
@@ -68,7 +68,7 @@ Now that the button is in place, we can focus entirely on building the form. Thi
Luckily, the `EditPageForm` we built in [part three](/docs/how-to-guides/data-basics/3-building-an-edit-form.md) already takes us 80% of the way there. The bulk of the user interface is already available, and we will reuse it in the `CreatePageForm`. Let’s start by extracting the form UI into a separate component:
```js
-export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
+function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
// ...
return (
setTitle( title );
return (
@@ -180,7 +180,7 @@ Triggers a POST request to the [`/wp/v2/pages` WordPress REST API](https://devel
Now that we know more about `saveEntityRecord`, let's use it in `CreatePageForm`.
```js
-export function CreatePageForm( { onSaveFinished, onCancel } ) {
+function CreatePageForm( { onSaveFinished, onCancel } ) {
// ...
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
@@ -206,7 +206,7 @@ export function CreatePageForm( { onSaveFinished, onCancel } ) {
There is one more detail to address: our newly created pages are not yet picked up by the `PagesList`. Accordingly to the REST API documentation, the `/wp/v2/pages` endpoint creates (`POST` requests) pages with `status=draft` by default, but _returns_ (`GET` requests) pages with `status=publish`. The solution is to pass the `status` parameter explicitly:
```js
-export function CreatePageForm( { onSaveFinished, onCancel } ) {
+function CreatePageForm( { onSaveFinished, onCancel } ) {
// ...
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
@@ -238,7 +238,7 @@ The `EditPageForm` retrieved the error and progress information via the `getLas
In `CreatePageForm` however, we do not have a `pageId`. What now? We can skip the `pageId` argument to retrieve the information about the entity record without any id – this will be the newly created one. The `useSelect` call is thus very similar to the one from `EditPageForm`:
```js
-export function CreatePageForm( { onCancel, onSaveFinished } ) {
+function CreatePageForm( { onCancel, onSaveFinished } ) {
// ...
const { lastError, isSaving } = useSelect(
( select ) => ( {
@@ -272,7 +272,7 @@ And that’s it! Here's what our new form looks like in action:
Here’s everything we built in this chapter in one place:
```js
-export function CreatePageForm( { onCancel, onSaveFinished } ) {
+function CreatePageForm( { onCancel, onSaveFinished } ) {
const [title, setTitle] = useState();
const { lastError, isSaving } = useSelect(
( select ) => ( {
@@ -309,7 +309,7 @@ export function CreatePageForm( { onCancel, onSaveFinished } ) {
);
}
-export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
+function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
const { page, lastError, isSaving, hasEdits } = useSelect(
( select ) => ( {
page: select( coreDataStore ).getEditedEntityRecord( 'postType', 'page', pageId ),
@@ -342,7 +342,7 @@ export function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
);
}
-export function PageForm( { title, onChangeTitle, hasEdits, lastError, isSaving, onCancel, onSave } ) {
+function PageForm( { title, onChangeTitle, hasEdits, lastError, isSaving, onCancel, onSave } ) {
return (
{ decodeEntities( page.title.rendered ) }
-
+
{/* ↓ This is the only change in the PagesList component */}
- {/* ↑ This is the only change in the PagesList component */}
@@ -285,7 +284,7 @@ function DeletePageButton( { pageId } ) {
Great! `DeletePageButton` is now fully aware of errors. Let's see that error message in action. We'll trigger an invalid delete and let it fail. One way to do this is to multiply the `pageId` by a large number:
```js
-export function DeletePageButton( { pageId, onCancel, onSaveFinished } ) {
+function DeletePageButton( { pageId, onCancel, onSaveFinished } ) {
pageId = pageId * 1000;
// ...
}
@@ -447,4 +446,4 @@ function DeletePageButton( { pageId } ) {
## What's next?
* **Previous part:** [Building a *Create page form*](/docs/how-to-guides/data-basics/4-building-a-create-page-form.md)
-* (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/09-code-data-basics-esnext) in the gutenberg-examples repository
+* (optional) Review the [finished app](https://github.com/WordPress/gutenberg-examples/tree/trunk/non-block-examples/09-code-data-basics-esnext) in the gutenberg-examples repository
diff --git a/docs/how-to-guides/format-api.md b/docs/how-to-guides/format-api.md
index 96793b843a13d..a23293bbb27e3 100644
--- a/docs/how-to-guides/format-api.md
+++ b/docs/how-to-guides/format-api.md
@@ -78,7 +78,7 @@ registerFormatType( 'my-custom-format/sample-output', {
} );
```
-Let's check that everything is working as expected. Build and reload and then select a text block. Confirm the new button was added to the format toolbar.
+Let's check that everything is working as expected. Build and reload and then select any block containing text like for example the paragraph block. Confirm the new button was added to the format toolbar.
![Toolbar with custom button](https://developer.wordpress.org/files/2021/12/format-api-toolbar.png)
@@ -125,13 +125,13 @@ registerFormatType( 'my-custom-format/sample-output', {
Confirm it is working: first build and reload, then make a text selection and click the button. Your browser will likely display that selection differently than the surrounding text.
-You can also confirm by switching to HTML view (Code editor Ctrl+Shift+Alt+M) and see the text selection wrapped with `` HTML tags.
+You can also confirm by switching to HTML view (Code editor `Ctrl+Shift+Alt+M`) and see the text selection wrapped with `` HTML tags.
Use the `className` option when registering to add your own custom class to the tag. You can use that class and custom CSS to target that element and style as you wish.
### Step 4: Show the button only for specific blocks (Optional)
-By default, the button is rendered on every rich text toolbar (image captions, buttons, paragraphs, etc). You can render the button only on blocks of a certain type by using `wp.data.withSelect` together with `wp.compose.ifCondition`.
+By default, the button is rendered on every rich text toolbar (image captions, buttons, paragraphs, etc). You can render the button only on blocks of a certain type by using [the data API](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data).
Here is an example that only shows the button for Paragraph blocks:
@@ -173,6 +173,44 @@ registerFormatType( 'my-custom-format/sample-output', {
} );
```
+### Step5: Add a button outside of the dropdown (Optional)
+
+Using the `RichTextToolbarButton` component, the button is added to the default dropdown menu. You can add the button directly to the toolbar by using the `BlockControls` component.
+
+```js
+import { registerFormatType, toggleFormat } from '@wordpress/rich-text';
+import { BlockControls } from '@wordpress/block-editor';
+import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
+
+const MyCustomButton = ( { isActive, onChange, value } ) => {
+ return (
+
+
+ {
+ onChange(
+ toggleFormat( value, {
+ type: 'my-custom-format/sample-output',
+ } )
+ );
+ } }
+ isActive={ isActive }
+ />
+
+
+ );
+};
+
+registerFormatType( 'my-custom-format/sample-output', {
+ title: 'Sample output',
+ tagName: 'samp',
+ className: null,
+ edit: MyCustomButton,
+} );
+```
+
## Troubleshooting
If you run into errors:
@@ -196,4 +234,4 @@ Reference documentation used in this guide:
The guide showed you how to add a button to the toolbar and have it apply a format to the selected text. Try it out and see what you can build with it in your next plugin.
-Download the [format-api example](https://github.com/WordPress/gutenberg-examples/tree/trunk/format-api) from the [gutenberg-examples](https://github.com/WordPress/gutenberg-examples) repository.
+Download the [format-api example](https://github.com/WordPress/gutenberg-examples/tree/trunk/non-block-examples/format-api) from the [gutenberg-examples](https://github.com/WordPress/gutenberg-examples) repository.
diff --git a/docs/how-to-guides/propagating-updates.md b/docs/how-to-guides/propagating-updates.md
new file mode 100644
index 0000000000000..b7d321437634a
--- /dev/null
+++ b/docs/how-to-guides/propagating-updates.md
@@ -0,0 +1,62 @@
+# Propagating updates for block types
+
+This resource seeks to offer direction for those needing to provide updates to content, whether in a template for a theme, pattern, or a block over an entire site. Since each content type allows or disallows certain kind of synchronization, it's important to know what's possible before creating to make maintenance easier in the future.
+
+## Recommendations on managing updates
+
+### Establish early what content you expect to require updates
+
+At a high level, it’s important to recognize that not every piece of content can be updated across the entire site and that the method of creation greatly impacts what’s possible. As a result, it’s critical to spend time ahead of creation determining what you expect to need updates and to put that content in the appropriate format. This will make a huge difference in terms of future maintenance.
+
+### Embrace theme design at the block level
+
+Block theme design requires a mindset shift from the previous approach of designing large sections of a theme and controlling them via updates. While a holistic view of a design is still important when creating a custom theme project, blocks require that themers approach design on a more atomic level. This means starting from the block itself, typically through theme.json customizations. **The goal is that each individual "atom" (i.e., block) can be moved around, edited, deleted, and put back together without the entire design falling apart.**
+
+The more that you approach design at the block level, the less need there is to propagate updates to things like patterns and templates across the entire site. If the atomic pieces are in place, their layout should not matter.
+
+## Content types (and how to properly update them)
+
+### Blocks
+
+How to manage block updates depends on the nature of the block itself. If the block depends on external data, then making it dynamic from start with the `render_callback` function is often a better choice as it provides more control. If the block's structure is expected to change over time, then starting with the static block that uses `save()` method defining a default output is the recommended approach. Over time, it's possible to go hybrid and include also the `render_callback` that can use the output from `save` as a fallback while processing an alternate output. Keep in mind that that flexibility and controls comes at the cost of additional processing during rendering. Another option is using static blocks that rely on managing updates with [block deprecations](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-deprecation/). This will require you to manually update exist blocks. Depending on your needs and comfortability, either approach can work. **To get started on creating blocks and save time, [you can use the Create Block tool](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/).**
+
+### Patterns
+
+**For content that you want updated later on, do not use patterns and instead rely on reusable blocks or template parts.** Patterns cannot be updated after you insert one into your site. For context, you can think of Patterns as more like sample/example/starter content. While Patterns exposed in the Inserter might evolve over time, those changes won't be automatically applied to any current useage of the pattern. Once insertered, patterns become completely detached from the original pattern unlike Reusable block or Template Part block.
+
+If needed, one potential workaround for patterns with custom styles is to add a class name to the wrapping block for a pattern. For example, the following adds a themeslug-special class to a Group block:
+
+```
+
+
+
+
+
+```
+
+It is not fool-proof because users can modify the class via the editor UI. However, because this setting is under the "Advanced" panel it is likely to stay intact in most instances. This gives theme authors some CSS control for some pattern types, allowing them to update existing uses. However, it does not prevent users from making massive alterations that cannot be updated.
+
+### Reusable blocks
+
+As the name suggests, these blocks are inherently synced across your site. Keep in mind that there are currently limitations with relying on reusable blocks to handle certain updates since content, HTML structure, and styles will all stay in sync when updates happen. If you require more nuance than that, this is a key element to factor in and a dynamic block might be a better approach.
+
+### Template Parts and Templates
+
+Because block themes allow users to directly edit templates and template parts, how changes are managed need to be adjusted in light of the greater access given to users. For context, when templates or template parts are changed by the user, the updated templates from the theme update don’t show for the user. Only new users of the theme or users who have not yet edited a template are experiencing the updated template. If users haven’t modified the files then the changes you make on the file system will be reflected on the user’s sites – you just need to update the files and they’ll get the changes. However if they have made changes to their templates then the only way you can update their templates is to:
+
+- Revert all their changes
+- Update the templates and template parts in the database
+
+Generally speaking, if a user has made changes to templates then it’s recommended to leave the templates as is, unless agreed upon with the user (ie in an agency setting).
+
+One thing to be mindful of when updating templates is inserting references to new or different template parts. For example, the templates/page.html template could insert a parts/header.html part in version 1.0 but change that reference to parts/header-alt.html in version 2.0. Some developers may see this as a "workaround" in instances where users modified the original header.html. However, this is likely to break a user's customized design since the page.html template would no longer reference the correct part unless they also modified and saved the page template.
+
+Likewise, it is generally poor practice to delete template parts in theme updates. In this scenario, users could create custom top-level templates that include a call to the part and expect it to continue existing.
+
+### Resources
+
+- [Comparing Patterns, Template Parts, and Reusable Blocks](https://wordpress.org/support/article/comparing-patterns-template-parts-and-reusable-blocks/)
+- [Block deprecation](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-deprecation/)
+- [Create Block tool](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/)
+
+
diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md
index e96c4a5c4ac94..c62ecb3cdbb48 100644
--- a/docs/how-to-guides/themes/theme-json.md
+++ b/docs/how-to-guides/themes/theme-json.md
@@ -428,7 +428,7 @@ The naming schema for the classes and the custom properties is as follows:
"slug": "x-large",
"size": 46,
"name": "Large"
- },
+ }
]
},
"spacing": {
@@ -454,7 +454,7 @@ The naming schema for the classes and the custom properties is as follows:
"slug": "60",
"size": "2rem",
"name": "Large"
- },
+ }
]
},
"blocks": {
diff --git a/docs/how-to-guides/themes/theme-support.md b/docs/how-to-guides/themes/theme-support.md
index e7c015fe62113..562b0c502e58c 100644
--- a/docs/how-to-guides/themes/theme-support.md
+++ b/docs/how-to-guides/themes/theme-support.md
@@ -476,3 +476,17 @@ Use this setting to enable the following Global Styles settings:
```php
add_theme_support( 'appearance-tools' );
```
+
+## Block Based Template Parts
+
+Block Based Template parts allow administrators to edit parts of the site using blocks. This is off by default, and requires the theme to opt in by declaring support:
+
+```php
+add_theme_support( 'block-template-parts' );
+```
+
+This feature is only relevant for non block based themes, as block based themes already support block based template parts as part of the site editor.
+
+The standalone template part editor does not allow editors to create new, or delete existing template parts. This is because the theme manually needs to include the template part in the PHP template.
+
+You can find out more about block based template parts in the [themes handbook block template and template parts section](https://developer.wordpress.org/themes/block-themes/templates-and-template-parts/#block-c5fa39a2-a27d-4bd2-98d0-dc6249a0801a).
diff --git a/docs/manifest.json b/docs/manifest.json
index 78a268132d0a7..69d991c65d3da 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -305,6 +305,12 @@
"markdown_source": "../docs/how-to-guides/plugin-sidebar-0.md",
"parent": "how-to-guides"
},
+ {
+ "title": "Propagating updates for block types ",
+ "slug": "propagating-updates",
+ "markdown_source": "../docs/how-to-guides/propagating-updates.md",
+ "parent": "how-to-guides"
+ },
{
"title": "Themes",
"slug": "themes",
@@ -497,6 +503,12 @@
"markdown_source": "../docs/reference-guides/filters/autocomplete-filters.md",
"parent": "filters"
},
+ {
+ "title": "Global Styles Filters",
+ "slug": "global-styles-filters",
+ "markdown_source": "../docs/reference-guides/filters/global-styles-filters.md",
+ "parent": "filters"
+ },
{
"title": "SlotFills Reference",
"slug": "slotfills",
@@ -1469,6 +1481,12 @@
"markdown_source": "../packages/create-block/README.md",
"parent": "packages"
},
+ {
+ "title": "@wordpress/create-block External Template",
+ "slug": "packages-create-block-external-template",
+ "markdown_source": "../packages/create-block/docs/external-template.md",
+ "parent": "packages-create-block"
+ },
{
"title": "@wordpress/custom-templated-path-webpack-plugin",
"slug": "packages-custom-templated-path-webpack-plugin",
diff --git a/docs/reference-guides/README.md b/docs/reference-guides/README.md
index ab0682044e91f..33fdd9aa60241 100644
--- a/docs/reference-guides/README.md
+++ b/docs/reference-guides/README.md
@@ -23,6 +23,7 @@
- [i18n Hooks](/docs/reference-guides/filters/i18n-filters.md)
- [Parser Hooks](/docs/reference-guides/filters/parser-filters.md)
- [Autocomplete](/docs/reference-guides/filters/autocomplete-filters.md)
+- [Global Styles Hooks](/docs/reference-guides/filters/global-styles-filters.md)
## [SlotFills Reference](/docs/reference-guides/slotfills/README.md)
diff --git a/docs/reference-guides/block-api/block-metadata.md b/docs/reference-guides/block-api/block-metadata.md
index 9967579d22dcf..bf59213e2696b 100644
--- a/docs/reference-guides/block-api/block-metadata.md
+++ b/docs/reference-guides/block-api/block-metadata.md
@@ -46,14 +46,15 @@ Starting in WordPress 5.8 release, we encourage using the `block.json` metadata
"title": "Example",
"attributes": {
"message": "This is an example!"
- },
+ }
}
],
- "editorScript": "file:./build/index.js",
- "script": "file:./build/script.js",
- "viewScript": "file:./build/view.js",
- "editorStyle": "file:./build/index.css",
- "style": "file:./build/style.css"
+ "editorScript": "file:./index.js",
+ "script": "file:./script.js",
+ "viewScript": "file:./view.js",
+ "editorStyle": "file:./index.css",
+ "style": "file:./style.css",
+ "render": "file:./render.php"
}
```
@@ -83,7 +84,7 @@ This function takes two params relevant in this context (`$block_type` accepts m
- `$block_type` (`string`) – path to the folder where the `block.json` file is located or full path to the metadata file if named differently.
- `$args` (`array`) – an optional array of block type arguments. Default value: `[]`. Any arguments may be defined. However, the one described below is supported by default:
- - `$render_callback` (`callable`) – callback used to render blocks of this block type.
+ - `$render_callback` (`callable`) – callback used to render blocks of this block type, it's an alternative to the `render` field in `block.json`.
It returns the registered block type (`WP_Block_Type`) on success or `false` on failure.
@@ -432,11 +433,11 @@ See the [the example documentation](/docs/reference-guides/block-api/block-regis
### Variations
-- Type: `object[]`
-- Optional
-- Localized: Yes (`title`, `description`, and `keywords` of each variation only)
-- Property: `variations`
-- Since: `WordPress 5.9.0`
+- Type: `object[]`
+- Optional
+- Localized: Yes (`title`, `description`, and `keywords` of each variation only)
+- Property: `variations`
+- Since: `WordPress 5.9.0`
```json
{
@@ -469,7 +470,7 @@ See the [the variations documentation](/docs/reference-guides/block-api/block-va
- Property: `editorScript`
```json
-{ "editorScript": "file:./build/index.js" }
+{ "editorScript": "file:./index.js" }
```
Block type editor script definition. It will only be enqueued in the context of the editor.
@@ -482,7 +483,7 @@ Block type editor script definition. It will only be enqueued in the context of
- Property: `script`
```json
-{ "script": "file:./build/script.js" }
+{ "script": "file:./script.js" }
```
Block type frontend and editor script definition. It will be enqueued both in the editor and when viewing the content on the front of the site.
@@ -496,7 +497,7 @@ Block type frontend and editor script definition. It will be enqueued both in th
- Since: `WordPress 5.9.0`
```json
-{ "viewScript": "file:./build/view.js" }
+{ "viewScript": "file:./view.js" }
```
Block type frontend script definition. It will be enqueued only when viewing the content on the front of the site.
@@ -511,7 +512,7 @@ _Note: An option to pass also an array of view scripts exists since WordPress `6
- Property: `editorStyle`
```json
-{ "editorStyle": "file:./build/index.css" }
+{ "editorStyle": "file:./index.css" }
```
Block type editor style definition. It will only be enqueued in the context of the editor.
@@ -526,20 +527,50 @@ _Note: An option to pass also an array of editor styles exists since WordPress `
- Property: `style`
```json
-{ "style": "file:./build/style.css" }
+{ "style": "file:./style.css" }
```
Block type frontend and editor style definition. It will be enqueued both in the editor and when viewing the content on the front of the site.
_Note: An option to pass also an array of styles exists since WordPress `5.9.0`._
+### Render
+
+- Type: `WPDefinedPath` ([learn more](#wpdefinedpath))
+- Optional
+- Localized: No
+- Property: `render`
+- Since: `WordPress 6.1.0`
+
+```json
+{ "render": "file:./render.php" }
+```
+
+PHP file to use when rendering the block type on the server to show on the front end. The following variables are exposed to the file:
+
+- `$attributes` (`array`): The block attributes.
+- `$content` (`string`): The block default content.
+- `$block` (`WP_Block`): The block instance.
+
## Assets
-### `WPDefinedAsset`
+### `WPDefinedPath`
+
+The `WPDefinedPath` type is a subtype of string, where the value represents a path to a JavaScript, CSS or PHP file relative to where `block.json` file is located. The path provided must be prefixed with `file:`. This approach is based on how npm handles [local paths](https://docs.npmjs.com/files/package.json#local-paths) for packages.
+
+**Example:**
+
+In `block.json`:
-The `WPDefinedAsset` type is a subtype of string, where the value represents a path to a JavaScript or CSS file relative to where `block.json` file is located. The path provided must be prefixed with `file:`. This approach is based on how npm handles [local paths](https://docs.npmjs.com/files/package.json#local-paths) for packages.
+```json
+{
+ "render": "file:./render.php"
+}
+```
+
+### `WPDefinedAsset`
-An alternative would be a script or style handle name referencing an already registered asset using WordPress helpers.
+It extends `WPDefinedPath` for JavaScript and CSS files. An alternative to the file path would be a script or style handle name referencing an already registered asset using WordPress helpers.
**Example:**
@@ -570,8 +601,8 @@ The definition is stored inside separate PHP file which ends with `.asset.php` a
**Example:**
```
-block.json
build/
+├─ block.json
├─ index.js
└─ index.asset.php
```
@@ -579,7 +610,7 @@ build/
In `block.json`:
```json
-{ "editorScript": "file:./build/index.js" }
+{ "editorScript": "file:./index.js" }
```
In `build/index.asset.php`:
@@ -601,7 +632,7 @@ return array(
Starting in the WordPress 5.8 release, it is possible to instruct WordPress to enqueue scripts and styles for a block type only when rendered on the frontend. It applies to the following asset fields in the `block.json` file:
- `script`
-- `viewScript` (when the block defines `render_callback` during registration in PHP, then the block author is responsible for enqueuing the script)
+- `viewScript` (when the block defines `render_callback` during registration in PHP or a `render` field in its `block.json`, then the script is registered but the block author is responsible for enqueuing it)
- `style`
## Internationalization
diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 7d5c1629761cd..735d75237618f 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -14,7 +14,7 @@ Display a date archive of your posts. ([Source](https://github.com/WordPress/gut
- **Name:** core/archives
- **Category:** widgets
-- **Supports:** align, spacing (margin, padding), ~~html~~
+- **Supports:** align, spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** displayAsDropdown, showLabel, showPostCounts, type
## Audio
@@ -32,7 +32,7 @@ Add a user's avatar. ([Source](https://github.com/WordPress/gutenberg/tree/trunk
- **Name:** core/avatar
- **Category:** theme
-- **Supports:** align, color (~~background~~, ~~text~~), spacing (margin), ~~alignWide~~, ~~html~~
+- **Supports:** align, color (~~background~~, ~~text~~), spacing (margin, padding), ~~alignWide~~, ~~html~~
- **Attributes:** isLink, linkTarget, size, userId
## Reusable block
@@ -50,7 +50,7 @@ Prompt visitors to take action with a button-style link. ([Source](https://githu
- **Name:** core/button
- **Category:** design
-- **Supports:** align, anchor, color (background, gradients, text), spacing (padding), typography (fontSize), ~~alignWide~~, ~~reusable~~
+- **Supports:** align, anchor, color (background, gradients, text), spacing (padding), typography (fontSize, lineHeight), ~~alignWide~~, ~~reusable~~
- **Attributes:** backgroundColor, gradient, linkTarget, placeholder, rel, text, textColor, title, url, width
## Buttons
@@ -59,7 +59,7 @@ Prompt visitors to take action with a group of button-style links. ([Source](htt
- **Name:** core/buttons
- **Category:** design
-- **Supports:** align (full, wide), anchor, spacing (blockGap, margin)
+- **Supports:** align (full, wide), anchor, spacing (blockGap, margin), typography (fontSize, lineHeight)
- **Attributes:**
## Calendar
@@ -68,7 +68,7 @@ A calendar of your site’s posts. ([Source](https://github.com/WordPress/gutenb
- **Name:** core/calendar
- **Category:** widgets
-- **Supports:** align
+- **Supports:** align, typography (fontSize, lineHeight)
- **Attributes:** month, year
## Categories List
@@ -77,7 +77,7 @@ Display a list of all categories. ([Source](https://github.com/WordPress/gutenbe
- **Name:** core/categories
- **Category:** widgets
-- **Supports:** align, typography (fontSize, lineHeight), ~~html~~
+- **Supports:** align, spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** displayAsDropdown, showEmpty, showHierarchy, showOnlyTopLevel, showPostCounts
## Code
@@ -140,7 +140,7 @@ Displays the date on which the comment was posted. ([Source](https://github.com/
- **Name:** core/comment-date
- **Category:** theme
-- **Supports:** color (background, gradients, link, text), typography (fontSize, lineHeight), ~~html~~
+- **Supports:** color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** format, isLink
## Comment Edit Link
@@ -149,7 +149,7 @@ Displays a link to edit the comment in the WordPress Dashboard. This link is onl
- **Name:** core/comment-edit-link
- **Category:** theme
-- **Supports:** color (background, gradients, link, ~~text~~), typography (fontSize, lineHeight), ~~html~~
+- **Supports:** color (background, gradients, link, ~~text~~), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** linkTarget, textAlign
## Comment Reply Link
@@ -158,7 +158,7 @@ Displays a link to reply to a comment. ([Source](https://github.com/WordPress/gu
- **Name:** core/comment-reply-link
- **Category:** theme
-- **Supports:** color (background, gradients, link, ~~text~~), typography (fontSize, lineHeight), ~~html~~
+- **Supports:** color (background, gradients, link, ~~text~~), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** textAlign
## Comment Template
@@ -266,7 +266,7 @@ Display multiple images in a rich gallery. ([Source](https://github.com/WordPres
- **Name:** core/gallery
- **Category:** media
-- **Supports:** align, anchor, color (background, gradients, ~~text~~), spacing (blockGap), units (em, px, rem, vh, vw), ~~html~~
+- **Supports:** align, anchor, color (background, gradients, ~~text~~), spacing (blockGap, margin, padding), units (em, px, rem, vh, vw), ~~html~~
- **Attributes:** allowResize, caption, columns, fixedHeight, ids, imageCrop, images, linkTarget, linkTo, shortCodeTransforms, sizeSlug
## Group
@@ -293,7 +293,7 @@ Create a link that always points to the homepage of the site. Usually not necess
- **Name:** core/home-link
- **Category:** design
-- **Supports:** ~~html~~, ~~reusable~~
+- **Supports:** typography (fontSize, lineHeight), ~~html~~, ~~reusable~~
- **Attributes:** label
## Custom HTML
@@ -329,7 +329,7 @@ Display a list of your most recent posts. ([Source](https://github.com/WordPress
- **Name:** core/latest-posts
- **Category:** widgets
-- **Supports:** align, ~~html~~
+- **Supports:** align, typography (fontSize, lineHeight), ~~html~~
- **Attributes:** addLinkToFeaturedImage, categories, columns, displayAuthor, displayFeaturedImage, displayPostContent, displayPostContentRadio, displayPostDate, excerptLength, featuredImageAlign, featuredImageSizeHeight, featuredImageSizeSlug, featuredImageSizeWidth, order, orderBy, postLayout, postsToShow, selectedAuthor
## List
@@ -393,7 +393,7 @@ A collection of blocks that allow visitors to get around your site. ([Source](ht
- **Name:** core/navigation
- **Category:** theme
- **Supports:** align (full, wide), anchor, inserter, spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~
-- **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, textColor
+- **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, icon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, textColor
## Custom Link
@@ -401,7 +401,7 @@ Add a page, link, or another item to your navigation. ([Source](https://github.c
- **Name:** core/navigation-link
- **Category:** design
-- **Supports:** ~~html~~, ~~reusable~~
+- **Supports:** typography (fontSize, lineHeight), ~~html~~, ~~reusable~~
- **Attributes:** description, id, isTopLevelLink, kind, label, opensInNewTab, rel, title, type, url
## Submenu
@@ -518,7 +518,7 @@ Displays the contents of a post or page. ([Source](https://github.com/WordPress/
- **Name:** core/post-content
- **Category:** theme
-- **Supports:** align (full, wide), ~~html~~
+- **Supports:** align (full, wide), typography (fontSize, lineHeight), ~~html~~
- **Attributes:**
## Post Date
@@ -546,7 +546,7 @@ Display a post's featured image. ([Source](https://github.com/WordPress/gutenber
- **Name:** core/post-featured-image
- **Category:** theme
- **Supports:** align (center, full, left, right, wide), color (~~background~~, ~~text~~), spacing (margin, padding), ~~html~~
-- **Attributes:** height, isLink, linkTarget, rel, scale, sizeSlug, width
+- **Attributes:** customGradient, customOverlayColor, dimRatio, gradient, height, isLink, linkTarget, overlayColor, rel, scale, sizeSlug, width
## Post Navigation Link
@@ -609,7 +609,7 @@ An advanced block that allows displaying post types based on different query par
- **Name:** core/query
- **Category:** theme
- **Supports:** align (full, wide), color (background, gradients, link, text), ~~html~~
-- **Attributes:** displayLayout, query, queryId, tagName
+- **Attributes:** displayLayout, namespace, query, queryId, tagName
## No results
@@ -617,7 +617,7 @@ Contains the block elements used to render content when no query results are fou
- **Name:** core/query-no-results
- **Category:** theme
-- **Supports:** align, color (background, gradients, link, text), ~~html~~, ~~reusable~~
+- **Supports:** align, color (background, gradients, link, text), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~
- **Attributes:**
## Pagination
@@ -626,7 +626,7 @@ Displays a paginated navigation to next/previous set of posts, when applicable.
- **Name:** core/query-pagination
- **Category:** theme
-- **Supports:** align, color (background, gradients, link, text), ~~html~~, ~~reusable~~
+- **Supports:** align, color (background, gradients, link, text), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~
- **Attributes:** paginationArrow
## Next Page
@@ -698,7 +698,7 @@ Help visitors find your content. ([Source](https://github.com/WordPress/gutenber
- **Name:** core/search
- **Category:** widgets
-- **Supports:** align (center, left, right), color (background, gradients, text), ~~html~~
+- **Supports:** align (center, left, right), color (background, gradients, text), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** buttonPosition, buttonText, buttonUseIcon, label, placeholder, query, showLabel, width, widthUnit
## Separator
@@ -725,7 +725,7 @@ Display a graphic to represent this site. Update the block, and the changes appl
- **Name:** core/site-logo
- **Category:** theme
-- **Supports:** align, color (~~background~~, ~~text~~), ~~alignWide~~, ~~html~~
+- **Supports:** align, color (~~background~~, ~~text~~), spacing (margin, padding), ~~alignWide~~, ~~html~~
- **Attributes:** isLink, linkTarget, shouldSyncIcon, width
## Site Tagline
@@ -761,7 +761,7 @@ Display icons linking to your social media profiles or sites. ([Source](https://
- **Name:** core/social-links
- **Category:** widgets
-- **Supports:** align (center, left, right), anchor, color (background, gradients, ~~enableContrastChecker~~, ~~text~~), spacing (blockGap, margin, units)
+- **Supports:** align (center, left, right), anchor, color (background, gradients, ~~enableContrastChecker~~, ~~text~~), spacing (blockGap, margin, padding, units)
- **Attributes:** customIconBackgroundColor, customIconColor, iconBackgroundColor, iconBackgroundColorValue, iconColor, iconColorValue, openInNewTab, showLabels, size
## Spacer
diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md
index 4c16e90441c4a..c0a180f72adf4 100644
--- a/docs/reference-guides/data/data-core-block-editor.md
+++ b/docs/reference-guides/data/data-core-block-editor.md
@@ -1492,7 +1492,7 @@ Action that enables or disables the navigation mode.
_Parameters_
-- _isNavigationMode_ `string`: Enable/Disable navigation mode.
+- _isNavigationMode_ `boolean`: Enable/Disable navigation mode.
### setTemplateValidity
diff --git a/docs/reference-guides/data/data-core-editor.md b/docs/reference-guides/data/data-core-editor.md
index b1d767284611f..755e9ab8da0a0 100644
--- a/docs/reference-guides/data/data-core-editor.md
+++ b/docs/reference-guides/data/data-core-editor.md
@@ -758,6 +758,18 @@ _Returns_
- `boolean`: Whether current post is scheduled to be posted.
+### isDeletingPost
+
+Returns true if the post is currently being deleted, or false otherwise.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+
+_Returns_
+
+- `boolean`: Whether post is being deleted.
+
### isEditedPostAutosaveable
Returns true if the post can be autosaved, or false otherwise.
diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md
index 61d0f8bd30f26..ba500d10c0038 100644
--- a/docs/reference-guides/data/data-core.md
+++ b/docs/reference-guides/data/data-core.md
@@ -21,7 +21,7 @@ _Parameters_
- _state_ `State`: Data state.
- _action_ `string`: Action to check. One of: 'create', 'read', 'update', 'delete'.
- _resource_ `string`: REST resource to check, e.g. 'media' or 'posts'.
-- _id_ `GenericRecordKey`: Optional ID of the rest resource to check.
+- _id_ `EntityRecordKey`: Optional ID of the rest resource to check.
_Returns_
@@ -39,9 +39,9 @@ Calling this may trigger an OPTIONS request to the REST API via the
_Parameters_
- _state_ `State`: Data state.
-- _kind_ `Kind`: Entity kind.
-- _name_ `Name`: Entity name.
-- _recordId_ `GenericRecordKey`: Record's id.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record's id.
_Returns_
@@ -56,11 +56,11 @@ Returns all available authors.
_Parameters_
- _state_ `State`: Data state.
-- _query_ `EntityQuery< any >`: Optional object of query parameters to include with request.
+- _query_ `GetRecordsHttpQuery`: Optional object of query parameters to include with request.
_Returns_
-- `User< 'edit' >[]`: Authors list.
+- `ET.User[]`: Authors list.
### getAutosave
@@ -70,8 +70,8 @@ _Parameters_
- _state_ `State`: State tree.
- _postType_ `string`: The type of the parent post.
-- _postId_ `GenericRecordKey`: The id of the parent post.
-- _authorId_ `GenericRecordKey`: The id of the author.
+- _postId_ `EntityRecordKey`: The id of the parent post.
+- _authorId_ `EntityRecordKey`: The id of the author.
_Returns_
@@ -88,7 +88,7 @@ _Parameters_
- _state_ `State`: State tree.
- _postType_ `string`: The type of the parent post.
-- _postId_ `GenericRecordKey`: The id of the parent post.
+- _postId_ `EntityRecordKey`: The id of the parent post.
_Returns_
@@ -140,7 +140,7 @@ _Parameters_
_Returns_
-- `User< 'edit' >`: Current user object.
+- `undefined< 'edit' >`: Current user object.
### getEditedEntityRecord
@@ -149,13 +149,13 @@ Returns the specified entity record, merged with its edits.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _recordId_ `KeyOf< K, N >`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
-- `EntityRecord | undefined`: The entity record, merged with its edits.
+- `undefined< EntityRecord > | undefined`: The entity record, merged with its edits.
### getEmbedPreview
@@ -179,7 +179,7 @@ Returns the loaded entities for the given kind.
_Parameters_
- _state_ `State`: Data state.
-- _kind_ `Kind`: Entity kind.
+- _kind_ `string`: Entity kind.
_Returns_
@@ -192,7 +192,7 @@ Returns the loaded entities for the given kind.
_Parameters_
- _state_ `State`: Data state.
-- _kind_ `Kind`: Entity kind.
+- _kind_ `string`: Entity kind.
_Returns_
@@ -207,8 +207,8 @@ Returns the entity config given its kind and name.
_Parameters_
- _state_ `State`: Data state.
-- _kind_ `Kind`: Entity kind.
-- _name_ `Name`: Entity name.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
_Returns_
@@ -221,8 +221,8 @@ Returns the entity config given its kind and name.
_Parameters_
- _state_ `State`: Data state.
-- _kind_ `Kind`: Entity kind.
-- _name_ `Name`: Entity name.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
_Returns_
@@ -237,14 +237,14 @@ entity object if it exists and is received.
_Parameters_
- _state_ `State`: State tree
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _key_ `KeyOf< R >`: Record's key
-- _query_ Optional query. If requesting specific fields, fields must always include the ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _key_ `EntityRecordKey`: Record's key
+- _query_ `GetRecordsHttpQuery`: Optional query. If requesting specific fields, fields must always include the ID.
_Returns_
-- Record.
+- `EntityRecord | undefined`: Record.
### getEntityRecordEdits
@@ -253,9 +253,9 @@ Returns the specified entity record's edits.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _recordId_ `KeyOf< K, N >`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
@@ -272,9 +272,9 @@ They are defined in the entity's config.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _recordId_ `KeyOf< K, N >`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
@@ -287,13 +287,13 @@ Returns the Entity's records.
_Parameters_
- _state_ `State`: State tree
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _query_ Optional terms query. If requesting specific fields, fields must always include the ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _query_ `GetRecordsHttpQuery`: Optional terms query. If requesting specific fields, fields must always include the ID.
_Returns_
-- Records.
+- `EntityRecord[] | null`: Records.
### getLastEntityDeleteError
@@ -302,9 +302,9 @@ Returns the specified entity record's last delete error.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `Kind`: Entity kind.
-- _name_ `Name`: Entity name.
-- _recordId_ `GenericRecordKey`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
@@ -317,9 +317,9 @@ Returns the specified entity record's last save error.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `Kind`: Entity kind.
-- _name_ `Name`: Entity name.
-- _recordId_ `GenericRecordKey`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
@@ -333,9 +333,9 @@ with its attributes mapped to their raw values.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _key_ `KeyOf< K, N >`: Record's key.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _key_ `EntityRecordKey`: Record's key.
_Returns_
@@ -411,7 +411,7 @@ _Parameters_
_Returns_
-- `User< 'edit' >[]`: Users list.
+- `undefined< 'edit' >[]`: Users list.
### hasEditsForEntityRecord
@@ -421,9 +421,9 @@ and false otherwise.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _recordId_ `KeyOf< K, N >`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
@@ -437,9 +437,9 @@ or false otherwise.
_Parameters_
- _state_ `State`: State tree
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _query_ `EntityQuery< C >`: Optional terms query.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _query_ `GetRecordsHttpQuery`: Optional terms query.
_Returns_
@@ -453,7 +453,7 @@ _Parameters_
- _state_ `State`: State tree.
- _postType_ `string`: The type of the parent post.
-- _postId_ `GenericRecordKey`: The id of the parent post.
+- _postId_ `EntityRecordKey`: The id of the parent post.
_Returns_
@@ -492,9 +492,9 @@ Returns true if the specified entity record is autosaving, and false otherwise.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `Kind`: Entity kind.
-- _name_ `Name`: Entity name.
-- _recordId_ `GenericRecordKey`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
@@ -507,9 +507,9 @@ Returns true if the specified entity record is deleting, and false otherwise.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `Kind`: Entity kind.
-- _name_ `Name`: Entity name.
-- _recordId_ `GenericRecordKey`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
@@ -553,9 +553,9 @@ Returns true if the specified entity record is saving, and false otherwise.
_Parameters_
- _state_ `State`: State tree.
-- _kind_ `K`: Entity kind.
-- _name_ `N`: Entity name.
-- _recordId_ `KeyOf< K, N >`: Record ID.
+- _kind_ `string`: Entity kind.
+- _name_ `string`: Entity name.
+- _recordId_ `EntityRecordKey`: Record ID.
_Returns_
diff --git a/docs/reference-guides/filters/global-styles-filters.md b/docs/reference-guides/filters/global-styles-filters.md
new file mode 100644
index 0000000000000..6cbe94ad23535
--- /dev/null
+++ b/docs/reference-guides/filters/global-styles-filters.md
@@ -0,0 +1,42 @@
+# Global Styles Filters
+
+WordPress 6.1 has introduced some server-side filters to hook into the data provided to Global Styles & Settings:
+
+- `global_styles_default`: hooks into the default data provided by WordPress
+- `global_styles_blocks`: hooks into the data provided by the blocks
+- `global_styles_theme`: hooks into the data provided by the theme
+- `global_styles_user`: hooks into the data provided by the user
+
+Each filter receives an instance of the `WP_Theme_JSON_Data` class with the data for the respective layer. To provide new data, the filter callback needs to use the `update_with( $new_data )` method, where `$new_data` is a valid `theme.json`-like structure. As with any `theme.json`, the new data needs to declare which `version` of the `theme.json` is using, so it can correctly be migrated to the runtime one, should it be different.
+
+_Example:_
+
+This is how to pass a new color palette for the theme and disable the text color UI:
+
+```php
+function filter_global_styles_theme( $theme_json ){
+ $new_data = array(
+ 'version' => 2,
+ 'settings' => array(
+ 'color' => array(
+ 'text' => false,
+ 'palette' => array( /* New palette */
+ array(
+ 'slug' => 'foreground',
+ 'color' => 'black',
+ 'name' => __( 'Foreground', 'theme-domain' ),
+ ),
+ array(
+ 'slug' => 'background',
+ 'color' => 'white',
+ 'name' => __( 'Background', 'theme-domain' ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ return $theme_json->update_with( $new_data );
+}
+add_filter( 'global_styles_theme', 'filter_global_styles_theme' );
+```
diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md
index 7c9b7f75da5d4..3b3d40ffd7335 100644
--- a/docs/reference-guides/theme-json-reference/theme-json-living.md
+++ b/docs/reference-guides/theme-json-reference/theme-json-living.md
@@ -4,7 +4,7 @@
>
> There're related documents you may be interested in: the [theme.json v1](/docs/reference-guides/theme-json-reference/theme-json-v1.md) specification and the [reference to migrate from theme.json v1 to v2](/docs/reference-guides/theme-json-reference/theme-json-migrations.md).
-This reference guide lists the settings and style properties defined in the theme.json schema. See the [theme.json how to guide](/docs/how-to-guides/themes/theme-json.md) for examples and guide on how to use the theme.json file in your theme.
+This reference guide lists the settings and style properties defined in the theme.json schema. See the [theme.json how to guide](/docs/how-to-guides/themes/theme-json.md) for examples and guide on how to use the theme.json file in your theme.
## Schema
@@ -158,6 +158,19 @@ Color styles.
---
+### outline
+
+Outline styles.
+
+| Property | Type | Props |
+| --- | --- |--- |
+| color | string | |
+| offset | string | |
+| style | string | |
+| width | string | |
+
+---
+
### spacing
Spacing styles.
@@ -195,6 +208,13 @@ CSS and SVG filter styles.
| --- | --- |--- |
| duotone | string | |
+---
+
+### shadow
+
+Box shadow styles.
+
+
---
diff --git a/docs/toc.json b/docs/toc.json
index eba3b7fd810df..0327ebca199de 100644
--- a/docs/toc.json
+++ b/docs/toc.json
@@ -133,6 +133,7 @@
},
{ "docs/how-to-guides/notices/README.md": [] },
{ "docs/how-to-guides/plugin-sidebar-0.md": [] },
+ { "docs/how-to-guides/propagating-updates.md": [] },
{
"docs/how-to-guides/themes/README.md": [
{ "docs/how-to-guides/themes/block-theme-overview.md": [] },
@@ -197,6 +198,9 @@
{ "docs/reference-guides/filters/parser-filters.md": [] },
{
"docs/reference-guides/filters/autocomplete-filters.md": []
+ },
+ {
+ "docs/reference-guides/filters/global-styles-filters.md": []
}
]
},
diff --git a/docs/tool/manifest.js b/docs/tool/manifest.js
index 256ba292de476..344eaf3552170 100644
--- a/docs/tool/manifest.js
+++ b/docs/tool/manifest.js
@@ -31,15 +31,25 @@ const packagePaths = glob( 'packages/*/package.json' )
* @return {Array} Manifest
*/
function getPackageManifest( packageFolderNames ) {
- return packageFolderNames.map( ( folderName ) => {
+ return packageFolderNames.reduce( ( manifest, folderName ) => {
const path = `${ baseRepoUrl }/packages/${ folderName }/README.md`;
- return {
+ const tocPath = `${ baseRepoUrl }/packages/${ folderName }/docs/toc.json`;
+
+ // First add any README files to the TOC
+ manifest.push( {
title: `@wordpress/${ folderName }`,
slug: `packages-${ folderName }`,
markdown_source: path,
parent: 'packages',
- };
- } );
+ } );
+
+ // Next add any items in the docs/toc.json if found.
+ if ( fs.existsSync( join( __dirname, '..', tocPath ) ) ) {
+ const toc = require( join( __dirname, '..', tocPath ) ).values();
+ manifest.push( ...toc );
+ }
+ return manifest;
+ }, [] );
}
/**
diff --git a/gutenberg.php b/gutenberg.php
index 1d5cd83741484..b0d0d83bb576a 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -5,7 +5,7 @@
* Description: Printing since 1440. This is the development plugin for the new block editor in core.
* Requires at least: 5.9
* Requires PHP: 5.6
- * Version: 14.0.0-rc.1
+ * Version: 14.1.0-rc.1
* Author: Gutenberg Team
* Text Domain: gutenberg
*
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index cfdbac9c47e3f..09e8727743342 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -12,22 +12,18 @@
* @param WP_Block_Type $block_type Block Type.
*/
function gutenberg_register_border_support( $block_type ) {
- // Determine if any border related features are supported.
- $has_border_support = block_has_support( $block_type, array( '__experimentalBorder' ) );
- $has_border_color_support = gutenberg_has_border_feature_support( $block_type, 'color' );
-
// Setup attributes and styles within that if needed.
if ( ! $block_type->attributes ) {
$block_type->attributes = array();
}
- if ( $has_border_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
+ if ( block_has_support( $block_type, array( '__experimentalBorder' ) ) && ! array_key_exists( 'style', $block_type->attributes ) ) {
$block_type->attributes['style'] = array(
'type' => 'object',
);
}
- if ( $has_border_color_support && ! array_key_exists( 'borderColor', $block_type->attributes ) ) {
+ if ( gutenberg_has_border_feature_support( $block_type, 'color' ) && ! array_key_exists( 'borderColor', $block_type->attributes ) ) {
$block_type->attributes['borderColor'] = array(
'type' => 'string',
);
@@ -48,9 +44,7 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
return array();
}
- $sides = array( 'top', 'right', 'bottom', 'left' );
- $border_block_styles = array();
-
+ $border_block_styles = array();
$has_border_color_support = gutenberg_has_border_feature_support( $block_type, 'color' );
$has_border_width_support = gutenberg_has_border_feature_support( $block_type, 'width' );
@@ -106,7 +100,7 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
// Generate styles for individual border sides.
if ( $has_border_color_support || $has_border_width_support ) {
- foreach ( $sides as $side ) {
+ foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) {
$border = _wp_array_get( $block_attributes, array( 'style', 'border', $side ), null );
$border_side_values = array(
'width' => isset( $border['width'] ) && ! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' ) ? $border['width'] : null,
diff --git a/lib/block-supports/colors.php b/lib/block-supports/colors.php
index ab4e9fe661004..8b9702ae44533 100644
--- a/lib/block-supports/colors.php
+++ b/lib/block-supports/colors.php
@@ -11,10 +11,7 @@
* @param WP_Block_Type $block_type Block Type.
*/
function gutenberg_register_colors_support( $block_type ) {
- $color_support = false;
- if ( property_exists( $block_type, 'supports' ) ) {
- $color_support = _wp_array_get( $block_type->supports, array( 'color' ), false );
- }
+ $color_support = property_exists( $block_type, 'supports' ) ? _wp_array_get( $block_type->supports, array( 'color' ), false ) : false;
$has_text_colors_support = true === $color_support || ( is_array( $color_support ) && _wp_array_get( $color_support, array( 'text' ), true ) );
$has_background_colors_support = true === $color_support || ( is_array( $color_support ) && _wp_array_get( $color_support, array( 'background' ), true ) );
$has_gradients_support = _wp_array_get( $color_support, array( 'gradients' ), false );
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index 2581b986508aa..eaca6d4c70cc8 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -46,7 +46,7 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
if ( is_array( $gap_value ) ) {
$gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
}
- if ( $gap_value && ! $should_skip_gap_serialization ) {
+ if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
array_push(
$layout_styles,
array(
@@ -67,8 +67,9 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
}
}
} elseif ( 'constrained' === $layout_type ) {
- $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : '';
- $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : '';
+ $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : '';
+ $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : '';
+ $justify_content = isset( $layout['justifyContent'] ) ? $layout['justifyContent'] : 'center';
$all_max_width_value = $content_size ? $content_size : $wide_size;
$wide_max_width_value = $wide_size ? $wide_size : $content_size;
@@ -78,6 +79,9 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] );
$wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] );
+ $margin_left = 'left' === $justify_content ? '0 !important' : 'auto !important';
+ $margin_right = 'right' === $justify_content ? '0 !important' : 'auto !important';
+
if ( $content_size || $wide_size ) {
array_push(
$layout_styles,
@@ -85,8 +89,8 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
'declarations' => array(
'max-width' => $all_max_width_value,
- 'margin-left' => 'auto !important',
- 'margin-right' => 'auto !important',
+ 'margin-left' => $margin_left,
+ 'margin-right' => $margin_right,
),
),
array(
@@ -125,11 +129,25 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
}
}
+ if ( 'left' === $justify_content ) {
+ $layout_styles[] = array(
+ 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
+ 'declarations' => array( 'margin-left' => '0 !important' ),
+ );
+ }
+
+ if ( 'right' === $justify_content ) {
+ $layout_styles[] = array(
+ 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
+ 'declarations' => array( 'margin-right' => '0 !important' ),
+ );
+ }
+
if ( $has_block_gap_support ) {
if ( is_array( $gap_value ) ) {
$gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
}
- if ( $gap_value && ! $should_skip_gap_serialization ) {
+ if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
// Get spacing CSS variable from preset value if provided.
if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) {
$index_to_splice = strrpos( $gap_value, '|' ) + 1;
@@ -198,7 +216,7 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
}
$gap_value = trim( $combined_gap_value );
- if ( $gap_value && ! $should_skip_gap_serialization ) {
+ if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
$layout_styles[] = array(
'selector' => $selector,
'declarations' => array( 'gap' => $gap_value ),
@@ -291,14 +309,17 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) {
$block_classname = wp_get_block_default_classname( $block['blockName'] );
$container_class = wp_unique_id( 'wp-container-' );
$layout_classname = '';
- $use_global_padding = gutenberg_get_global_settings( array( 'useRootPaddingAwareAlignments' ) ) && ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] );
// Set the correct layout type for blocks using legacy content width.
if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) {
$used_layout['type'] = 'constrained';
}
- if ( $use_global_padding ) {
+ if (
+ gutenberg_get_global_settings( array( 'useRootPaddingAwareAlignments' ) ) &&
+ isset( $used_layout['type'] ) &&
+ 'constrained' === $used_layout['type']
+ ) {
$class_names[] = 'has-global-padding';
}
diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php
index 6cfae5b8cf07e..212b058f822d7 100644
--- a/lib/block-supports/typography.php
+++ b/lib/block-supports/typography.php
@@ -53,6 +53,12 @@ function gutenberg_register_typography_support( $block_type ) {
'type' => 'string',
);
}
+
+ if ( $has_font_family_support && ! array_key_exists( 'fontFamily', $block_type->attributes ) ) {
+ $block_type->attributes['fontFamily'] = array(
+ 'type' => 'string',
+ );
+ }
}
/**
@@ -228,11 +234,13 @@ function gutenberg_typography_get_css_variable_inline_style( $attributes, $featu
* @access private
*
* @param string $raw_value Raw size value from theme.json.
- * @param array $options array(
- * 'coerce_to' => (string) Coerce the value to rem or px. Default `'rem'`.
- * 'root_size_value' => (number) Value of root font size for rem|em <-> px conversion. Default `16`.
- * 'acceptable_units' => (array) An array of font size units. Default `[ 'rem', 'px', 'em' ]`;
- * );.
+ * @param array $options {
+ * Optional. An associative array of options. Default is empty array.
+ *
+ * @type string $coerce_to Coerce the value to rem or px. Default `'rem'`.
+ * @type int $root_size_value Value of root font size for rem|em <-> px conversion. Default `16`.
+ * @type array $acceptable_units An array of font size units. Default `[ 'rem', 'px', 'em' ]`;
+ * }
* @return array An array consisting of `'value'` and `'unit'` properties.
*/
function gutenberg_get_typography_value_and_unit( $raw_value, $options = array() ) {
@@ -283,13 +291,15 @@ function gutenberg_get_typography_value_and_unit( $raw_value, $options = array()
*
* @access private
*
- * @param array $args array(
- * 'maximum_viewport_width' => (string) Maximum size up to which type will have fluidity.
- * 'minimum_viewport_width' => (string) Minimum viewport size from which type will have fluidity.
- * 'maximum_font_size' => (string) Maximum font size for any clamp() calculation.
- * 'minimum_font_size' => (string) Minimum font size for any clamp() calculation.
- * 'scale_factor' => (number) A scale factor to determine how fast a font scales within boundaries.
- * );.
+ * @param array $args {
+ * Optional. An associative array of values to calculate a fluid formula for font size. Default is empty array.
+ *
+ * @type string $maximum_viewport_width Maximum size up to which type will have fluidity.
+ * @type string $minimum_viewport_width Minimum viewport size from which type will have fluidity.
+ * @type string $maximum_font_size Maximum font size for any clamp() calculation.
+ * @type string $minimum_font_size Minimum font size for any clamp() calculation.
+ * @type int $scale_factor A scale factor to determine how fast a font scales within boundaries.
+ * }
* @return string|null A font-size value using clamp().
*/
function gutenberg_get_computed_fluid_typography_value( $args = array() ) {
@@ -355,8 +365,15 @@ function gutenberg_get_computed_fluid_typography_value( $args = array() ) {
* Returns a font-size value based on a given font-size preset.
* Takes into account fluid typography parameters and attempts to return a css formula depending on available, valid values.
*
- * @param array $preset fontSizes preset value as seen in theme.json.
- * @param boolean $should_use_fluid_typography An override to switch fluid typography "on". Can be used for unit testing.
+ * @param array $preset {
+ * Required. fontSizes preset value as seen in theme.json.
+ *
+ * @type string $name Name of the font size preset.
+ * @type string $slug Kebab-case unique identifier for the font size preset.
+ * @type string $size CSS font-size value, including units where applicable.
+ * }
+ * @param bool $should_use_fluid_typography An override to switch fluid typography "on". Can be used for unit testing. Default is `false`.
+ *
* @return string Font-size value.
*/
function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_typography = false ) {
diff --git a/lib/blocks.php b/lib/blocks.php
index e38cbdcf59ae2..842ff6a6ec093 100644
--- a/lib/blocks.php
+++ b/lib/blocks.php
@@ -191,21 +191,22 @@ function gutenberg_register_core_block_assets( $block_name ) {
// else (for development or test) default to use the current time.
$default_version = defined( 'GUTENBERG_VERSION' ) && ! ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? GUTENBERG_VERSION : time();
- $style_path = "build/block-library/blocks/$block_name/style.css";
- $editor_style_path = "build/block-library/blocks/$block_name/style-editor.css";
+ $style_path = "build/block-library/blocks/$block_name/";
+ $stylesheet_url = gutenberg_url( $style_path . 'style.css' );
+ $stylesheet_path = gutenberg_dir_path() . $style_path . ( is_rtl() ? 'style-rtl.css' : 'style.css' );
- if ( file_exists( gutenberg_dir_path() . $style_path ) ) {
+ if ( file_exists( $stylesheet_path ) ) {
wp_deregister_style( "wp-block-{$block_name}" );
wp_register_style(
"wp-block-{$block_name}",
- gutenberg_url( $style_path ),
+ $stylesheet_url,
array(),
$default_version
);
wp_style_add_data( "wp-block-{$block_name}", 'rtl', 'replace' );
// Add a reference to the stylesheet's path to allow calculations for inlining styles in `wp_head`.
- wp_style_add_data( "wp-block-{$block_name}", 'path', gutenberg_dir_path() . $style_path );
+ wp_style_add_data( "wp-block-{$block_name}", 'path', $stylesheet_path );
} else {
wp_register_style( "wp-block-{$block_name}", false, array() );
}
@@ -235,7 +236,7 @@ function() {
// If the file exists, enqueue it.
if ( file_exists( gutenberg_dir_path() . $theme_style_path ) ) {
- if ( file_exists( gutenberg_dir_path() . $style_path ) ) {
+ if ( file_exists( $stylesheet_path ) ) {
// If there is a main stylesheet for this block, append the theme styles to main styles.
wp_add_inline_style(
"wp-block-{$block_name}",
@@ -254,6 +255,7 @@ function() {
}
}
+ $editor_style_path = "build/block-library/blocks/$block_name/style-editor.css";
if ( file_exists( gutenberg_dir_path() . $editor_style_path ) ) {
wp_deregister_style( "wp-block-{$block_name}-editor" );
wp_register_style(
diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php
index 5ccdccb77bde2..6ba5a15035530 100644
--- a/lib/compat/wordpress-6.0/block-patterns.php
+++ b/lib/compat/wordpress-6.0/block-patterns.php
@@ -11,10 +11,6 @@
* `theme.json` file.
*/
function _register_remote_theme_patterns() {
- if ( ! get_theme_support( 'core-block-patterns' ) ) {
- return;
- }
-
if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) {
return;
}
diff --git a/lib/compat/wordpress-6.0/blocks.php b/lib/compat/wordpress-6.0/blocks.php
index be2d739329d02..ed83f62f76d42 100644
--- a/lib/compat/wordpress-6.0/blocks.php
+++ b/lib/compat/wordpress-6.0/blocks.php
@@ -5,127 +5,6 @@
* @package gutenberg
*/
-/**
- * Helper function that constructs a WP_Query args array from
- * a `Query` block properties.
- *
- * It's used in QueryLoop, QueryPaginationNumbers and QueryPaginationNext blocks.
- *
- * `build_query_vars_from_query_block` was introduced in 5.8, for 6.0 we just need
- * to update that function and not create a new one.
- *
- * @param WP_Block $block Block instance.
- * @param int $page Current query's page.
- *
- * @return array Returns the constructed WP_Query arguments.
- */
-function gutenberg_build_query_vars_from_query_block( $block, $page ) {
- $query = array(
- 'post_type' => 'post',
- 'order' => 'DESC',
- 'orderby' => 'date',
- 'post__not_in' => array(),
- );
-
- if ( isset( $block->context['query'] ) ) {
- if ( ! empty( $block->context['query']['postType'] ) ) {
- $post_type_param = $block->context['query']['postType'];
- if ( is_post_type_viewable( $post_type_param ) ) {
- $query['post_type'] = $post_type_param;
- }
- }
- if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) {
- $sticky = get_option( 'sticky_posts' );
- if ( 'only' === $block->context['query']['sticky'] ) {
- /**
- * Passing an empty array to post__in will return have_posts() as true (and all posts will be returned).
- * Logic should be used before hand to determine if WP_Query should be used in the event that the array
- * being passed to post__in is empty.
- *
- * @see https://core.trac.wordpress.org/ticket/28099
- */
- $query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 );
- $query['ignore_sticky_posts'] = 1;
- } else {
- $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky );
- }
- }
- if ( ! empty( $block->context['query']['exclude'] ) ) {
- $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] );
- $excluded_post_ids = array_filter( $excluded_post_ids );
- $query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids );
- }
- if (
- isset( $block->context['query']['perPage'] ) &&
- is_numeric( $block->context['query']['perPage'] )
- ) {
- $per_page = absint( $block->context['query']['perPage'] );
- $offset = 0;
-
- if (
- isset( $block->context['query']['offset'] ) &&
- is_numeric( $block->context['query']['offset'] )
- ) {
- $offset = absint( $block->context['query']['offset'] );
- }
-
- $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset;
- $query['posts_per_page'] = $per_page;
- }
-
- // We need to migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility.
- if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) {
- $tax_query = array();
- if ( ! empty( $block->context['query']['categoryIds'] ) ) {
- $tax_query[] = array(
- 'taxonomy' => 'category',
- 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ),
- 'include_children' => false,
- );
- }
- if ( ! empty( $block->context['query']['tagIds'] ) ) {
- $tax_query[] = array(
- 'taxonomy' => 'post_tag',
- 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ),
- 'include_children' => false,
- );
- }
- $query['tax_query'] = $tax_query;
- }
- if ( ! empty( $block->context['query']['taxQuery'] ) ) {
- $query['tax_query'] = array();
- foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) {
- if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) {
- $query['tax_query'][] = array(
- 'taxonomy' => $taxonomy,
- 'terms' => array_filter( array_map( 'intval', $terms ) ),
- 'include_children' => false,
- );
- }
- }
- }
- if (
- isset( $block->context['query']['order'] ) &&
- in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true )
- ) {
- $query['order'] = strtoupper( $block->context['query']['order'] );
- }
- if ( isset( $block->context['query']['orderBy'] ) ) {
- $query['orderby'] = $block->context['query']['orderBy'];
- }
- if ( ! empty( $block->context['query']['author'] ) ) {
- $query['author'] = $block->context['query']['author'];
- }
- if ( ! empty( $block->context['query']['search'] ) ) {
- $query['s'] = $block->context['query']['search'];
- }
- if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) {
- $query['post_parent__in'] = array_filter( array_map( 'intval', $block->context['query']['parents'] ) );
- }
- }
- return $query;
-}
-
if ( ! function_exists( 'build_comment_query_vars_from_block' ) ) {
/**
* Helper function that constructs a comment query vars array from the passed block properties.
diff --git a/lib/compat/wordpress-6.1/block-template-utils.php b/lib/compat/wordpress-6.1/block-template-utils.php
index 03b93c8393746..ee4016a1a080b 100644
--- a/lib/compat/wordpress-6.1/block-template-utils.php
+++ b/lib/compat/wordpress-6.1/block-template-utils.php
@@ -121,6 +121,13 @@ function gutenberg_get_block_templates( $query = array(), $template_type = 'wp_t
continue;
}
+ if ( $post_type &&
+ isset( $template->post_types ) &&
+ ! in_array( $post_type, $template->post_types, true )
+ ) {
+ continue;
+ }
+
$query_result[] = $template;
}
if ( ! isset( $query['wp_id'] ) ) {
@@ -264,8 +271,8 @@ function gutenberg_build_block_template_result_from_post( $post ) {
$is_wp_suggestion = get_post_meta( $post->ID, 'is_wp_suggestion', true );
$theme = $terms[0]->name;
- $has_theme_file = wp_get_theme()->get_stylesheet() === $theme &&
- null !== _get_block_template_file( $post->post_type, $post->post_name );
+ $template_file = _get_block_template_file( $post->post_type, $post->post_name );
+ $has_theme_file = wp_get_theme()->get_stylesheet() === $theme && null !== $template_file;
$template = new WP_Block_Template();
$template->wp_id = $post->ID;
@@ -288,6 +295,10 @@ function gutenberg_build_block_template_result_from_post( $post ) {
$template->is_custom = false;
}
+ if ( 'wp_template' === $post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) {
+ $template->post_types = $template_file['postTypes'];
+ }
+
if ( 'wp_template_part' === $post->post_type ) {
$type_terms = get_the_terms( $post, 'wp_template_part_area' );
if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) {
@@ -297,62 +308,64 @@ function gutenberg_build_block_template_result_from_post( $post ) {
return $template;
}
-/**
- * Helper function to get the Template Hierarchy for a given slug.
- * We need to Handle special cases here like `front-page`, `singular` and `archive` templates.
- *
- * Noting that we always add `index` as the last fallback template.
- *
- * @param string $slug The template slug to be created.
- * @param boolean $is_custom Indicates if a template is custom or part of the template hierarchy.
- * @param string $template_prefix The template prefix for the created template. This is used to extract the main template type ex. in `taxonomy-books` we extract the `taxonomy`.
- *
- * @return array The template hierarchy.
- */
-function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
- if ( 'index' === $slug ) {
- return array( 'index' );
- }
- if ( $is_custom ) {
- return array( 'page', 'singular', 'index' );
- }
- if ( 'front-page' === $slug ) {
- return array( 'front-page', 'home', 'index' );
- }
- $template_hierarchy = array( $slug );
- // Most default templates don't have `$template_prefix` assigned.
- if ( $template_prefix ) {
- list($type) = explode( '-', $template_prefix );
- // We need these checks because we always add the `$slug` above.
- if ( ! in_array( $template_prefix, array( $slug, $type ), true ) ) {
- $template_hierarchy[] = $template_prefix;
+if ( ! function_exists( 'get_template_hierarchy' ) ) {
+ /**
+ * Helper function to get the Template Hierarchy for a given slug.
+ * We need to Handle special cases here like `front-page`, `singular` and `archive` templates.
+ *
+ * Noting that we always add `index` as the last fallback template.
+ *
+ * @param string $slug The template slug to be created.
+ * @param boolean $is_custom Indicates if a template is custom or part of the template hierarchy.
+ * @param string $template_prefix The template prefix for the created template. This is used to extract the main template type ex. in `taxonomy-books` we extract the `taxonomy`.
+ *
+ * @return array The template hierarchy.
+ */
+ function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
+ if ( 'index' === $slug ) {
+ return array( 'index' );
}
- if ( $slug !== $type ) {
- $template_hierarchy[] = $type;
+ if ( $is_custom ) {
+ return array( 'page', 'singular', 'index' );
}
+ if ( 'front-page' === $slug ) {
+ return array( 'front-page', 'home', 'index' );
+ }
+ $template_hierarchy = array( $slug );
+ // Most default templates don't have `$template_prefix` assigned.
+ if ( $template_prefix ) {
+ list($type) = explode( '-', $template_prefix );
+ // We need these checks because we always add the `$slug` above.
+ if ( ! in_array( $template_prefix, array( $slug, $type ), true ) ) {
+ $template_hierarchy[] = $template_prefix;
+ }
+ if ( $slug !== $type ) {
+ $template_hierarchy[] = $type;
+ }
+ }
+ // Handle `archive` template.
+ if (
+ str_starts_with( $slug, 'author' ) ||
+ str_starts_with( $slug, 'taxonomy' ) ||
+ str_starts_with( $slug, 'category' ) ||
+ str_starts_with( $slug, 'tag' ) ||
+ 'date' === $slug
+ ) {
+ $template_hierarchy[] = 'archive';
+ }
+ // Handle `single` template.
+ if ( 'attachment' === $slug ) {
+ $template_hierarchy[] = 'single';
+ }
+ // Handle `singular` template.
+ if (
+ str_starts_with( $slug, 'single' ) ||
+ str_starts_with( $slug, 'page' ) ||
+ 'attachment' === $slug
+ ) {
+ $template_hierarchy[] = 'singular';
+ }
+ $template_hierarchy[] = 'index';
+ return $template_hierarchy;
}
- // Handle `archive` template.
- if (
- str_starts_with( $slug, 'author' ) ||
- str_starts_with( $slug, 'taxonomy' ) ||
- str_starts_with( $slug, 'category' ) ||
- str_starts_with( $slug, 'tag' ) ||
- 'date' === $slug
- ) {
- $template_hierarchy[] = 'archive';
- }
- // Handle `single` template.
- if ( 'attachment' === $slug ) {
- $template_hierarchy[] = 'single';
- }
- // Handle `singular` template.
- if (
- str_starts_with( $slug, 'single' ) ||
- str_starts_with( $slug, 'page' ) ||
- 'attachment' === $slug
- ) {
- $template_hierarchy[] = 'singular';
- }
- $template_hierarchy[] = 'index';
- return $template_hierarchy;
-};
+}
diff --git a/lib/compat/wordpress-6.1/blocks.php b/lib/compat/wordpress-6.1/blocks.php
index cbbf676cfcb90..a33515632099b 100644
--- a/lib/compat/wordpress-6.1/blocks.php
+++ b/lib/compat/wordpress-6.1/blocks.php
@@ -205,3 +205,215 @@ function gutenberg_block_type_metadata_multiple_view_scripts( $metadata ) {
return $metadata;
}
add_filter( 'block_type_metadata', 'gutenberg_block_type_metadata_multiple_view_scripts' );
+
+/**
+ * Helper function that constructs a WP_Query args array from
+ * a `Query` block properties.
+ *
+ * It's used in QueryLoop, QueryPaginationNumbers and QueryPaginationNext blocks.
+ *
+ * `build_query_vars_from_query_block` was introduced in 5.8, for 6.1 we just need
+ * to update that function and not create a new one.
+ *
+ * @param WP_Block $block Block instance.
+ * @param int $page Current query's page.
+ *
+ * @return array Returns the constructed WP_Query arguments.
+ */
+function gutenberg_build_query_vars_from_query_block( $block, $page ) {
+ $query = array(
+ 'post_type' => 'post',
+ 'order' => 'DESC',
+ 'orderby' => 'date',
+ 'post__not_in' => array(),
+ );
+
+ if ( isset( $block->context['query'] ) ) {
+ if ( ! empty( $block->context['query']['postType'] ) ) {
+ $post_type_param = $block->context['query']['postType'];
+ if ( is_post_type_viewable( $post_type_param ) ) {
+ $query['post_type'] = $post_type_param;
+ }
+ }
+ if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) {
+ $sticky = get_option( 'sticky_posts' );
+ if ( 'only' === $block->context['query']['sticky'] ) {
+ /**
+ * Passing an empty array to post__in will return have_posts() as true (and all posts will be returned).
+ * Logic should be used before hand to determine if WP_Query should be used in the event that the array
+ * being passed to post__in is empty.
+ *
+ * @see https://core.trac.wordpress.org/ticket/28099
+ */
+ $query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 );
+ $query['ignore_sticky_posts'] = 1;
+ } else {
+ $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky );
+ }
+ }
+ if ( ! empty( $block->context['query']['exclude'] ) ) {
+ $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] );
+ $excluded_post_ids = array_filter( $excluded_post_ids );
+ $query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids );
+ }
+ if (
+ isset( $block->context['query']['perPage'] ) &&
+ is_numeric( $block->context['query']['perPage'] )
+ ) {
+ $per_page = absint( $block->context['query']['perPage'] );
+ $offset = 0;
+
+ if (
+ isset( $block->context['query']['offset'] ) &&
+ is_numeric( $block->context['query']['offset'] )
+ ) {
+ $offset = absint( $block->context['query']['offset'] );
+ }
+
+ $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset;
+ $query['posts_per_page'] = $per_page;
+ }
+
+ // We need to migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility.
+ if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) {
+ $tax_query = array();
+ if ( ! empty( $block->context['query']['categoryIds'] ) ) {
+ $tax_query[] = array(
+ 'taxonomy' => 'category',
+ 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ),
+ 'include_children' => false,
+ );
+ }
+ if ( ! empty( $block->context['query']['tagIds'] ) ) {
+ $tax_query[] = array(
+ 'taxonomy' => 'post_tag',
+ 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ),
+ 'include_children' => false,
+ );
+ }
+ $query['tax_query'] = $tax_query;
+ }
+ if ( ! empty( $block->context['query']['taxQuery'] ) ) {
+ $query['tax_query'] = array();
+ foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) {
+ if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) {
+ $query['tax_query'][] = array(
+ 'taxonomy' => $taxonomy,
+ 'terms' => array_filter( array_map( 'intval', $terms ) ),
+ 'include_children' => false,
+ );
+ }
+ }
+ }
+ if (
+ isset( $block->context['query']['order'] ) &&
+ in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true )
+ ) {
+ $query['order'] = strtoupper( $block->context['query']['order'] );
+ }
+ if ( isset( $block->context['query']['orderBy'] ) ) {
+ $query['orderby'] = $block->context['query']['orderBy'];
+ }
+ if ( ! empty( $block->context['query']['author'] ) ) {
+ $query['author'] = $block->context['query']['author'];
+ }
+ if ( ! empty( $block->context['query']['search'] ) ) {
+ $query['s'] = $block->context['query']['search'];
+ }
+ if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) {
+ $query['post_parent__in'] = array_filter( array_map( 'intval', $block->context['query']['parents'] ) );
+ }
+ }
+
+ /**
+ * Filters the arguments which will be passed to `WP_Query` for the Query Loop Block.
+ *
+ * Anything to this filter should be compatible with the `WP_Query` API to form
+ * the query context which will be passed down to the Query Loop Block's children.
+ * This can help, for example, to include additional settings or meta queries not
+ * directly supported by the core Query Loop Block, and extend its capabilities.
+ *
+ * Please note that this will only influence the query that will be rendered on the
+ * front-end. The editor preview is not affected by this filter. Also, worth noting
+ * that the editor preview uses the REST API, so, ideally, one should aim to provide
+ * attributes which are also compatible with the REST API, in order to be able to
+ * implement identical queries on both sides.
+ *
+ * @since 6.1.0
+ *
+ * @param array $query Array containing parameters for `WP_Query` as parsed by the block context.
+ * @param WP_Block $block Block instance.
+ * @param int $page Current query's page.
+ */
+ return apply_filters( 'query_loop_block_query_vars', $query, $block, $page );
+}
+
+/**
+ * Register render template for core blocks if handling is missing in WordPress core.
+ *
+ * @since 6.1.0
+ *
+ * @param array $settings Array of determined settings for registering a block type.
+ * @param array $metadata Metadata provided for registering a block type.
+ *
+ * @return array Array of settings for registering a block type.
+ */
+function gutenberg_block_type_metadata_render_template( $settings, $metadata ) {
+ if ( empty( $metadata['render'] ) || isset( $settings['render_callback'] ) ) {
+ return $settings;
+ }
+
+ $template_path = wp_normalize_path(
+ realpath(
+ dirname( $metadata['file'] ) . '/' .
+ remove_block_asset_path_prefix( $metadata['render'] )
+ )
+ );
+
+ // Bail if the file does not exist.
+ if ( ! file_exists( $template_path ) ) {
+ return $settings;
+ }
+ /**
+ * Renders the block on the server.
+ *
+ * @param array $attributes Block attributes.
+ * @param string $content Block default content.
+ * @param WP_Block $block Block instance.
+ *
+ * @return string Returns the block content.
+ */
+ $settings['render_callback'] = function( $attributes, $content, $block ) use ( $template_path ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ ob_start();
+ require $template_path;
+ return ob_get_clean();
+ };
+
+ return $settings;
+}
+add_filter( 'block_type_metadata_settings', 'gutenberg_block_type_metadata_render_template', 10, 2 );
+
+/**
+ * Registers the metadata block attribute for block types.
+ *
+ * Once 6.1 is the minimum supported WordPress version for the Gutenberg
+ * plugin, this shim can be removed
+ *
+ * @param array $args Array of arguments for registering a block type.
+ * @return array $args
+ */
+function gutenberg_register_metadata_attribute( $args ) {
+ // Setup attributes if needed.
+ if ( ! isset( $args['attributes'] ) || ! is_array( $args['attributes'] ) ) {
+ $args['attributes'] = array();
+ }
+
+ if ( ! array_key_exists( 'metadata', $args['attributes'] ) ) {
+ $args['attributes']['metadata'] = array(
+ 'type' => 'object',
+ );
+ }
+
+ return $args;
+}
+add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' );
diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php
index f1371c04cacd3..0ec0256c83469 100644
--- a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php
+++ b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php
@@ -66,6 +66,10 @@ class WP_Theme_JSON_6_1 extends WP_Theme_JSON_6_0 {
'margin-right' => array( 'spacing', 'margin', 'right' ),
'margin-bottom' => array( 'spacing', 'margin', 'bottom' ),
'margin-left' => array( 'spacing', 'margin', 'left' ),
+ 'outline-color' => array( 'outline', 'color' ),
+ 'outline-offset' => array( 'outline', 'offset' ),
+ 'outline-style' => array( 'outline', 'style' ),
+ 'outline-width' => array( 'outline', 'width' ),
'padding' => array( 'spacing', 'padding' ),
'padding-top' => array( 'spacing', 'padding', 'top' ),
'padding-right' => array( 'spacing', 'padding', 'right' ),
@@ -79,6 +83,7 @@ class WP_Theme_JSON_6_1 extends WP_Theme_JSON_6_0 {
'text-decoration' => array( 'typography', 'textDecoration' ),
'text-transform' => array( 'typography', 'textTransform' ),
'filter' => array( 'filter', 'duotone' ),
+ 'box-shadow' => array( 'shadow' ),
);
/**
@@ -97,6 +102,7 @@ class WP_Theme_JSON_6_1 extends WP_Theme_JSON_6_0 {
'h6' => 'h6',
'button' => '.wp-element-button, .wp-block-button__link', // We have the .wp-block-button__link class so that this will target older buttons that have been serialized.
'caption' => '.wp-element-caption, .wp-block-audio figcaption, .wp-block-embed figcaption, .wp-block-gallery figcaption, .wp-block-image figcaption, .wp-block-table figcaption, .wp-block-video figcaption', // The block classes are necessary to target older content that won't use the new class names.
+ 'cite' => 'cite',
);
const __EXPERIMENTAL_ELEMENT_CLASS_NAMES = array(
@@ -153,9 +159,11 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
// hence, the schema for blocks & elements should not have them.
$styles_non_top_level = static::VALID_STYLES;
foreach ( array_keys( $styles_non_top_level ) as $section ) {
- foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) {
- if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) {
- unset( $styles_non_top_level[ $section ][ $prop ] );
+ if ( array_key_exists( $section, $styles_non_top_level ) && is_array( $styles_non_top_level[ $section ] ) ) {
+ foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) {
+ if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) {
+ unset( $styles_non_top_level[ $section ][ $prop ] );
+ }
}
}
}
@@ -314,9 +322,16 @@ public static function remove_insecure_properties( $theme_json ) {
'gradient' => null,
'text' => null,
),
+ 'shadow' => null,
'filter' => array(
'duotone' => null,
),
+ 'outline' => array(
+ 'color' => null,
+ 'offset' => null,
+ 'style' => null,
+ 'width' => null,
+ ),
'spacing' => array(
'margin' => null,
'padding' => null,
@@ -441,13 +456,11 @@ protected static function get_blocks_metadata() {
$element_selector = array( $el_selector );
break;
}
-
- $element_selector = static::append_to_selector( $el_selector, $selector . ' ', 'left' );
+ $element_selector[] = static::append_to_selector( $el_selector, $selector . ' ', 'left' );
}
- static::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = $element_selector;
+ static::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector );
}
}
-
return static::$blocks_metadata;
}
@@ -487,7 +500,7 @@ protected static function get_style_nodes( $theme_json, $selectors = array() ) {
if ( isset( $theme_json['styles']['elements'] ) ) {
foreach ( self::ELEMENTS as $element => $selector ) {
- if ( ! isset( $theme_json['styles']['elements'][ $element ] ) ) {
+ if ( ! isset( $theme_json['styles']['elements'][ $element ] ) || ! array_key_exists( $element, static::ELEMENTS ) ) {
continue;
}
@@ -642,6 +655,11 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'
}
if ( in_array( 'styles', $types, true ) ) {
+ $root_block_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true );
+
+ if ( false !== $root_block_key ) {
+ $stylesheet .= $this->get_root_layout_rules( static::ROOT_BLOCK_SELECTOR, $style_nodes[ $root_block_key ] );
+ }
$stylesheet .= $this->get_block_classes( $style_nodes );
} elseif ( in_array( 'base-layout-styles', $types, true ) ) {
// Base layout styles are provided as part of `styles`, so only output separately if explicitly requested.
@@ -780,60 +798,80 @@ function( $pseudo_selector ) use ( $selector ) {
$block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations );
}
- if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
- /*
- * Reset default browser margin on the root body element.
- * This is set on the root selector **before** generating the ruleset
- * from the `theme.json`. This is to ensure that if the `theme.json` declares
- * `margin` in its `spacing` declaration for the `body` element then these
- * user-generated values take precedence in the CSS cascade.
- * @link https://github.com/WordPress/gutenberg/issues/36147.
- */
- $block_rules .= 'body { margin: 0;';
-
- /*
- * If there are content and wide widths in theme.json, output them
- * as custom properties on the body element so all blocks can use them.
- */
- if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) {
- $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize'];
- $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial';
- $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize'];
- $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial';
- $block_rules .= '--wp--style--global--content-size: ' . $content_size . ';';
- $block_rules .= '--wp--style--global--wide-size: ' . $wide_size . ';';
- }
+ return $block_rules;
+ }
- $block_rules .= '}';
- }
+ /**
+ * Outputs the CSS for layout rules on the root.
+ *
+ * @param string $selector The root node selector.
+ * @param array $block_metadata The metadata for the root block.
+ * @return string The additional root rules CSS.
+ */
+ public function get_root_layout_rules( $selector, $block_metadata ) {
+ $css = '';
+ $settings = _wp_array_get( $this->theme_json, array( 'settings' ) );
+ $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
- if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
+ /*
+ * Reset default browser margin on the root body element.
+ * This is set on the root selector **before** generating the ruleset
+ * from the `theme.json`. This is to ensure that if the `theme.json` declares
+ * `margin` in its `spacing` declaration for the `body` element then these
+ * user-generated values take precedence in the CSS cascade.
+ * @link https://github.com/WordPress/gutenberg/issues/36147.
+ */
+ $css .= 'body { margin: 0;';
+
+ /*
+ * If there are content and wide widths in theme.json, output them
+ * as custom properties on the body element so all blocks can use them.
+ */
+ if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) {
+ $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize'];
+ $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial';
+ $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize'];
+ $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial';
+ $css .= '--wp--style--global--content-size: ' . $content_size . ';';
+ $css .= '--wp--style--global--wide-size: ' . $wide_size . ';';
+ }
- if ( $use_root_padding ) {
- $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }';
- $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
- $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }';
- $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
- }
+ $css .= '}';
+
+ if ( $use_root_padding ) {
+ // Top and bottom padding are applied to the outer block container.
+ $css .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }';
+ // Right and left padding are applied to the first container with `.has-global-padding` class.
+ $css .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
+ // Nested containers with `.has-global-padding` class do not get padding.
+ $css .= '.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }';
+ // Alignfull children of the container with left and right padding have negative margins so they can still be full width.
+ $css .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }';
+ // The above rule is negated for alignfull children of nested containers.
+ $css .= '.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }';
+ // Some of the children of alignfull blocks without content width should also get padding: text blocks and non-alignfull container blocks.
+ $css .= '.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
+ // The above rule also has to be negated for blocks inside nested `.has-global-padding` blocks.
+ $css .= '.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }';
+ }
- $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
- $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
- $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
+ $css .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
+ $css .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
+ $css .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
- $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' );
- $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
- if ( $has_block_gap_support ) {
- $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) );
- $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
- $block_rules .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }";
+ $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' );
+ $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
+ if ( $has_block_gap_support ) {
+ $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) );
+ $css .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
+ $css .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }";
- // For backwards compatibility, ensure the legacy block gap CSS variable is still available.
- $block_rules .= "$selector { --wp--style--block-gap: $block_gap_value; }";
- }
- $block_rules .= $this->get_layout_styles( $block_metadata );
+ // For backwards compatibility, ensure the legacy block gap CSS variable is still available.
+ $css .= "$selector { --wp--style--block-gap: $block_gap_value; }";
}
+ $css .= $this->get_layout_styles( $block_metadata );
- return $block_rules;
+ return $css;
}
/**
@@ -963,7 +1001,7 @@ protected static function compute_style_properties( $styles, $settings = array()
* @return string Style property value.
*/
protected static function get_property_value( $styles, $path, $theme_json = null ) {
- $value = _wp_array_get( $styles, $path, '' );
+ $value = _wp_array_get( $styles, $path );
// This converts references to a path to the value at that path
// where the values is an array with a "ref" key, pointing to a path.
@@ -983,7 +1021,7 @@ protected static function get_property_value( $styles, $path, $theme_json = null
}
}
- if ( '' === $value || is_array( $value ) ) {
+ if ( is_array( $value ) ) {
return $value;
}
@@ -1340,10 +1378,11 @@ protected function get_layout_styles( $block_metadata ) {
}
}
- if ( $block_gap_value ) {
+ // If the block should have custom gap, add the gap styles.
+ if ( null !== $block_gap_value && false !== $block_gap_value && '' !== $block_gap_value ) {
foreach ( $layout_definitions as $layout_definition_key => $layout_definition ) {
- // Allow skipping default layout for themes that opt-in to block styles, but opt-out of blockGap.
- if ( ! $has_block_gap_support && 'default' === $layout_definition_key ) {
+ // Allow outputting fallback gap styles for flex layout type when block gap support isn't available.
+ if ( ! $has_block_gap_support && 'flex' !== $layout_definition_key ) {
continue;
}
diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-data-gutenberg.php b/lib/compat/wordpress-6.1/class-wp-theme-json-data-gutenberg.php
new file mode 100644
index 0000000000000..9add04bb65b09
--- /dev/null
+++ b/lib/compat/wordpress-6.1/class-wp-theme-json-data-gutenberg.php
@@ -0,0 +1,59 @@
+origin = $origin;
+ $this->theme_json = new WP_Theme_JSON_Gutenberg( $data, $this->origin );
+ }
+
+ /**
+ * Updates the theme.json with the the given data.
+ *
+ * @param array $new_data Array following the theme.json specification.
+ *
+ * @return WP_Theme_JSON_Data_Gutenberg The own instance with access to the modified data.
+ */
+ public function update_with( $new_data ) {
+ $this->theme_json->merge( new WP_Theme_JSON_Gutenberg( $new_data, $this->origin ) );
+ return $this;
+ }
+
+ /**
+ * Returns the underlying data.
+ *
+ * @return array
+ */
+ public function get_data() {
+ return $this->theme_json->get_raw_data();
+ }
+
+}
diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
index f704daaffafb1..f05a4b1f75254 100644
--- a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
+++ b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php
@@ -54,10 +54,70 @@ public static function get_core_data() {
return static::$core;
}
- $config = static::read_json_file( __DIR__ . '/theme.json' );
- $config = static::translate( $config );
+ $config = static::read_json_file( __DIR__ . '/theme.json' );
+ $config = static::translate( $config );
+ /**
+ * Filters the default data provided by WordPress for global styles & settings.
+ *
+ * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data.
+ */
+ $theme_json = apply_filters( 'global_styles_default', new WP_Theme_JSON_Data_Gutenberg( $config, 'default' ) );
+ $config = $theme_json->get_data();
static::$core = new WP_Theme_JSON_Gutenberg( $config, 'default' );
return static::$core;
}
+
+ /**
+ * Returns the user's origin config.
+ *
+ * @return WP_Theme_JSON_Gutenberg Entity that holds styles for user data.
+ */
+ public static function get_user_data() {
+ if ( null !== static::$user ) {
+ return static::$user;
+ }
+
+ $config = array();
+ $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() );
+
+ if ( array_key_exists( 'post_content', $user_cpt ) ) {
+ $decoded_data = json_decode( $user_cpt['post_content'], true );
+
+ $json_decoding_error = json_last_error();
+ if ( JSON_ERROR_NONE !== $json_decoding_error ) {
+ trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() );
+ /**
+ * Filters the data provided by the user for global styles & settings.
+ *
+ * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data.
+ */
+ $theme_json = apply_filters( 'global_styles_user', new WP_Theme_JSON_Data_Gutenberg( $config, 'custom' ) );
+ $config = $theme_json->get_data();
+ return new WP_Theme_JSON_Gutenberg( $config, 'custom' );
+ }
+
+ // Very important to verify if the flag isGlobalStylesUserThemeJSON is true.
+ // If is not true the content was not escaped and is not safe.
+ if (
+ is_array( $decoded_data ) &&
+ isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) &&
+ $decoded_data['isGlobalStylesUserThemeJSON']
+ ) {
+ unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
+ $config = $decoded_data;
+ }
+ }
+
+ /**
+ * Filters the data provided by the user for global styles & settings.
+ *
+ * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data.
+ */
+ $theme_json = apply_filters( 'global_styles_user', new WP_Theme_JSON_Data_Gutenberg( $config, 'custom' ) );
+ $config = $theme_json->get_data();
+ static::$user = new WP_Theme_JSON_Gutenberg( $config, 'custom' );
+
+ return static::$user;
+ }
}
diff --git a/lib/compat/wordpress-6.1/get-global-styles-and-settings.php b/lib/compat/wordpress-6.1/get-global-styles-and-settings.php
index 32fde3442577b..c3b7f2b926e87 100644
--- a/lib/compat/wordpress-6.1/get-global-styles-and-settings.php
+++ b/lib/compat/wordpress-6.1/get-global-styles-and-settings.php
@@ -8,7 +8,7 @@
/**
* Adds global style rules to the inline style for each block.
*/
-function wp_add_global_styles_for_blocks() {
+function gutenberg_add_global_styles_for_blocks() {
$tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
$block_nodes = $tree->get_styles_block_nodes();
foreach ( $block_nodes as $metadata ) {
diff --git a/lib/compat/wordpress-6.1/rest-api.php b/lib/compat/wordpress-6.1/rest-api.php
index 88b6d3bbb9a4d..f3391389d9e38 100644
--- a/lib/compat/wordpress-6.1/rest-api.php
+++ b/lib/compat/wordpress-6.1/rest-api.php
@@ -61,3 +61,35 @@ function gutenberg_add_site_icon_url_to_index( WP_REST_Response $response ) {
return $response;
}
add_action( 'rest_index', 'gutenberg_add_site_icon_url_to_index' );
+
+/**
+ * Returns the has_archive post type field.
+ *
+ * @param array $type The response data.
+ * @param string $field_name The field name. The function handles field has_archive.
+ */
+function gutenberg_get_post_type_has_archive_field( $type, $field_name ) {
+ if ( ! empty( $type ) && ! empty( $type['slug'] ) && 'has_archive' === $field_name ) {
+ $post_type_object = get_post_type_object( $type['slug'] );
+ return $post_type_object->has_archive;
+ }
+}
+
+/**
+ * Registers the has_archive post type REST API field.
+ */
+function gutenberg_register_has_archive_on_post_types_endpoint() {
+ register_rest_field(
+ 'type',
+ 'has_archive',
+ array(
+ 'get_callback' => 'gutenberg_get_post_type_has_archive_field',
+ 'schema' => array(
+ 'description' => __( 'If the value is a string, the value will be used as the archive slug. If the value is false the post type has no archive.', 'gutenberg' ),
+ 'type' => array( 'string', 'boolean' ),
+ 'context' => array( 'view', 'edit' ),
+ ),
+ )
+ );
+}
+add_action( 'rest_api_init', 'gutenberg_register_has_archive_on_post_types_endpoint' );
diff --git a/lib/compat/wordpress-6.1/script-loader.php b/lib/compat/wordpress-6.1/script-loader.php
index 330e6b2614e1f..e6a42faf932f8 100644
--- a/lib/compat/wordpress-6.1/script-loader.php
+++ b/lib/compat/wordpress-6.1/script-loader.php
@@ -65,7 +65,7 @@ function gutenberg_enqueue_stored_styles() {
}
// Chain core store ids to signify what the styles contain.
$style_tag_id .= '-' . $style_key;
- $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_store( $style_key );
+ $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key );
}
// Combine Core styles.
@@ -81,7 +81,7 @@ function gutenberg_enqueue_stored_styles() {
if ( in_array( $store_name, $core_styles_keys, true ) ) {
continue;
}
- $styles = gutenberg_style_engine_get_stylesheet_from_store( $store_name );
+ $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name );
if ( ! empty( $styles ) ) {
$key = "wp-style-engine-$store_name";
wp_register_style( $key, false, array(), true, true );
@@ -156,7 +156,7 @@ function gutenberg_enqueue_global_styles() {
wp_enqueue_style( 'global-styles' );
// add each block as an inline css.
- wp_add_global_styles_for_blocks();
+ gutenberg_add_global_styles_for_blocks();
}
remove_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles' );
diff --git a/lib/compat/wordpress-6.1/template-parts-screen.php b/lib/compat/wordpress-6.1/template-parts-screen.php
new file mode 100644
index 0000000000000..93b68436764b3
--- /dev/null
+++ b/lib/compat/wordpress-6.1/template-parts-screen.php
@@ -0,0 +1,208 @@
+ 'wp_template_part' ),
+ admin_url( 'themes.php?page=gutenberg-template-parts' )
+ );
+ wp_safe_redirect( $redirect_url );
+ exit;
+ }
+}
+add_action( 'load-appearance_page_gutenberg-template-parts', 'gutenberg_template_parts_screen_permissions' );
+
+/**
+ * Initialize the editor for the screen. Most of this is copied from `site-editor.php`.
+ *
+ * Note: Parts that need to be ported back should have inline comments.
+ *
+ * @param string $hook Current page hook.
+ * @return void
+ */
+function gutenberg_template_parts_screen_init( $hook ) {
+ global $current_screen, $editor_styles;
+
+ if ( 'appearance_page_gutenberg-template-parts' !== $hook ) {
+ return;
+ }
+
+ // Flag that we're loading the block editor.
+ $current_screen->is_block_editor( true );
+
+ // Default to is-fullscreen-mode to avoid jumps in the UI.
+ add_filter(
+ 'admin_body_class',
+ static function( $classes ) {
+ return "$classes is-fullscreen-mode";
+ }
+ );
+
+ $indexed_template_types = array();
+ foreach ( get_default_block_template_types() as $slug => $template_type ) {
+ $template_type['slug'] = (string) $slug;
+ $indexed_template_types[] = $template_type;
+ }
+
+ $block_editor_context = new WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) );
+ $custom_settings = array(
+ 'siteUrl' => site_url(),
+ 'postsPerPage' => get_option( 'posts_per_page' ),
+ 'styles' => get_block_editor_theme_styles(),
+ 'defaultTemplateTypes' => $indexed_template_types,
+ 'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(),
+ 'supportsLayout' => WP_Theme_JSON_Resolver::theme_has_support(),
+ 'supportsTemplatePartsMode' => ! wp_is_block_theme() && current_theme_supports( 'block-template-parts' ),
+ '__unstableHomeTemplate' => gutenberg_resolve_home_template(),
+ );
+
+ /**
+ * We don't need home template resolution when block template parts are supported.
+ * Set the value to true to satisfy the editor initialization guard clause.
+ */
+ if ( $custom_settings['supportsTemplatePartsMode'] ) {
+ $custom_settings['__unstableHomeTemplate'] = true;
+ }
+
+ // Add additional back-compat patterns registered by `current_screen` et al.
+ $custom_settings['__experimentalAdditionalBlockPatterns'] = WP_Block_Patterns_Registry::get_instance()->get_all_registered( true );
+ $custom_settings['__experimentalAdditionalBlockPatternCategories'] = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered( true );
+
+ $editor_settings = get_block_editor_settings( $custom_settings, $block_editor_context );
+
+ if ( isset( $_GET['postType'] ) && ! isset( $_GET['postId'] ) ) {
+ $post_type = get_post_type_object( $_GET['postType'] );
+ if ( ! $post_type ) {
+ wp_die( __( 'Invalid post type.', 'gutenberg' ) );
+ }
+ }
+
+ $active_global_styles_id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
+ $active_theme = wp_get_theme()->get_stylesheet();
+ $preload_paths = array(
+ array( '/wp/v2/media', 'OPTIONS' ),
+ '/wp/v2/types?context=view',
+ '/wp/v2/types/wp_template?context=edit',
+ '/wp/v2/types/wp_template-part?context=edit',
+ '/wp/v2/templates?context=edit&per_page=-1',
+ '/wp/v2/template-parts?context=edit&per_page=-1',
+ '/wp/v2/themes?context=edit&status=active',
+ '/wp/v2/global-styles/' . $active_global_styles_id . '?context=edit',
+ '/wp/v2/global-styles/' . $active_global_styles_id,
+ '/wp/v2/global-styles/themes/' . $active_theme,
+ );
+
+ block_editor_rest_api_preload( $preload_paths, $block_editor_context );
+
+ wp_add_inline_script(
+ 'wp-edit-site',
+ sprintf(
+ 'wp.domReady( function() {
+ wp.editSite.initializeEditor( "site-editor", %s );
+ } );',
+ wp_json_encode( $editor_settings )
+ )
+ );
+
+ // Preload server-registered block schemas.
+ wp_add_inline_script(
+ 'wp-blocks',
+ 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');'
+ );
+
+ wp_add_inline_script(
+ 'wp-blocks',
+ sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( get_block_categories( $block_editor_context ) ) )
+ );
+
+ wp_enqueue_script( 'wp-edit-site' );
+ wp_enqueue_script( 'wp-format-library' );
+ wp_enqueue_style( 'wp-edit-site' );
+ wp_enqueue_style( 'wp-format-library' );
+ wp_enqueue_media();
+
+ if (
+ current_theme_supports( 'wp-block-styles' ) ||
+ ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 )
+ ) {
+ wp_enqueue_style( 'wp-block-library-theme' );
+ }
+
+ /** This action is documented in wp-admin/edit-form-blocks.php */
+ do_action( 'enqueue_block_editor_assets' );
+}
+add_action( 'admin_enqueue_scripts', 'gutenberg_template_parts_screen_init' );
+
+/**
+ * The main entry point for the screen.
+ *
+ * @return void
+ */
+function gutenberg_template_parts_screen_render() {
+ echo '';
+}
+
+/**
+ * Register the new theme feature.
+ *
+ * Migrates into `create_initial_theme_features` method.
+ *
+ * @return void
+ */
+function gutenberg_register_template_parts_theme_feature() {
+ register_theme_feature(
+ 'block-template-parts',
+ array(
+ 'description' => __( 'Whether a theme uses block-based template parts.', 'gutenberg' ),
+ 'show_in_rest' => true,
+ )
+ );
+}
+add_action( 'setup_theme', 'gutenberg_register_template_parts_theme_feature', 5 );
diff --git a/lib/compat/wordpress-6.1/theme.php b/lib/compat/wordpress-6.1/theme.php
index 441fc63071436..4787f1057b474 100644
--- a/lib/compat/wordpress-6.1/theme.php
+++ b/lib/compat/wordpress-6.1/theme.php
@@ -29,3 +29,19 @@ function gutenberg_create_initial_theme_features() {
);
}
add_action( 'setup_theme', 'gutenberg_create_initial_theme_features', 0 );
+
+if ( ! function_exists( 'wp_theme_element_class_name' ) ) {
+ /**
+ * Given an element name, returns a class name.
+ * Alias from WP_Theme_JSON_Gutenberg::get_element_class_name.
+ *
+ * @param string $element The name of the element.
+ *
+ * @return string The name of the class.
+ *
+ * @since 6.1.0
+ */
+ function wp_theme_element_class_name( $element ) {
+ return WP_Theme_JSON_Gutenberg::get_element_class_name( $element );
+ }
+}
diff --git a/lib/experimental/block-editor-settings-mobile.php b/lib/experimental/block-editor-settings-mobile.php
index 4319f8766352c..b0da17e929667 100644
--- a/lib/experimental/block-editor-settings-mobile.php
+++ b/lib/experimental/block-editor-settings-mobile.php
@@ -29,8 +29,8 @@ function gutenberg_get_block_editor_settings_mobile( $settings ) {
// To tell mobile that the site uses quote v2 (inner blocks).
// See https://github.com/WordPress/gutenberg/pull/25892.
$settings['__experimentalEnableQuoteBlockV2'] = true;
- // To be set to true when the web makes quote v2 (inner blocks) the default.
- $settings['__experimentalEnableListBlockV2'] = gutenberg_is_list_v2_enabled();
+ // To tell mobile that the site uses list v2 (inner blocks).
+ $settings['__experimentalEnableListBlockV2'] = true;
}
return $settings;
diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php
deleted file mode 100644
index 80691e5d917ab..0000000000000
--- a/lib/experimental/blocks.php
+++ /dev/null
@@ -1,27 +0,0 @@
-get( 'TextDomain' ) );
$theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $theme_json_data );
+
+ /**
+ * Filters the data provided by the theme for global styles & settings.
+ *
+ * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data.
+ */
+ $theme_json = apply_filters( 'global_styles_theme', new WP_Theme_JSON_Data_Gutenberg( $theme_json_data, 'theme' ) );
+ $theme_json_data = $theme_json->get_data();
static::$theme = new WP_Theme_JSON_Gutenberg( $theme_json_data );
if ( wp_get_theme()->parent() ) {
@@ -128,6 +136,14 @@ public static function get_block_data() {
}
}
+ /**
+ * Filters the data provided by the blocks for global styles & settings.
+ *
+ * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data.
+ */
+ $theme_json = apply_filters( 'global_styles_blocks', new WP_Theme_JSON_Data_Gutenberg( $config, 'core' ) );
+ $config = $theme_json->get_data();
+
// Core here means it's the lower level part of the styles chain.
// It can be a core or a third-party block.
return new WP_Theme_JSON_Gutenberg( $config, 'core' );
diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php
index 143d5690b76e9..c93214385752b 100644
--- a/lib/experimental/editor-settings.php
+++ b/lib/experimental/editor-settings.php
@@ -72,3 +72,15 @@ function gutenberg_initialize_editor( $editor_name, $editor_script_handle, $sett
);
}
+
+/**
+ * Sets a global JS variable used to trigger the availability of zoomed out view.
+ */
+function gutenberg_enable_zoomed_out_view() {
+ $gutenberg_experiments = get_option( 'gutenberg-experiments' );
+ if ( $gutenberg_experiments && array_key_exists( 'gutenberg-zoomed-out-view', $gutenberg_experiments ) ) {
+ wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableZoomedOutView = true', 'before' );
+ }
+}
+
+add_action( 'admin_init', 'gutenberg_enable_zoomed_out_view' );
diff --git a/lib/experiments-page.php b/lib/experiments-page.php
index 581b6afebdf21..3d09f05bd655c 100644
--- a/lib/experiments-page.php
+++ b/lib/experiments-page.php
@@ -42,14 +42,14 @@ function gutenberg_initialize_experiments_settings() {
);
add_settings_field(
- 'gutenberg-list-v2',
- __( 'List block v2', 'gutenberg' ),
+ 'gutenberg-zoomed-out-view',
+ __( 'Zoomed out view ', 'gutenberg' ),
'gutenberg_display_experiment_field',
'gutenberg-experiments',
'gutenberg_experiments_section',
array(
- 'label' => __( 'Test a new list block that uses nested list item blocks (Warning: The new block is not ready. You may experience content loss, avoid using it on production sites)', 'gutenberg' ),
- 'id' => 'gutenberg-list-v2',
+ 'label' => __( 'Test a new zoomed out view on the site editor (Warning: The new feature is not ready. You may experience UX issues that are being addressed)', 'gutenberg' ),
+ 'id' => 'gutenberg-zoomed-out-view',
)
);
register_setting(
diff --git a/lib/init.php b/lib/init.php
index b867dd472552f..71b6a276c60f3 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -57,11 +57,3 @@ function gutenberg_menu() {
);
}
add_action( 'admin_menu', 'gutenberg_menu', 9 );
-
-/**
- * Outputs a WP REST API nonce.
- */
-function gutenberg_rest_nonce() {
- exit( wp_create_nonce( 'wp_rest' ) );
-}
-add_action( 'wp_ajax_gutenberg_rest_nonce', 'gutenberg_rest_nonce' );
diff --git a/lib/load.php b/lib/load.php
index 9600d3100d3b7..1493b5ee79791 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -83,6 +83,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-6.1/block-editor-settings.php';
require __DIR__ . '/compat/wordpress-6.1/persisted-preferences.php';
require __DIR__ . '/compat/wordpress-6.1/get-global-styles-and-settings.php';
+require __DIR__ . '/compat/wordpress-6.1/class-wp-theme-json-data-gutenberg.php';
require __DIR__ . '/compat/wordpress-6.1/class-wp-theme-json-6-1.php';
require __DIR__ . '/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php';
require __DIR__ . '/compat/wordpress-6.1/block-template-utils.php';
@@ -91,6 +92,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-6.1/date-settings.php';
require __DIR__ . '/compat/wordpress-6.1/block-patterns.php';
require __DIR__ . '/compat/wordpress-6.1/edit-form-blocks.php';
+require __DIR__ . '/compat/wordpress-6.1/template-parts-screen.php';
require __DIR__ . '/compat/wordpress-6.1/theme.php';
// Experimental features.
@@ -103,7 +105,6 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/class-wp-webfonts-provider.php';
require __DIR__ . '/experimental/class-wp-webfonts-provider-local.php';
require __DIR__ . '/experimental/webfonts.php';
-require __DIR__ . '/experimental/blocks.php';
require __DIR__ . '/experimental/navigation-theme-opt-in.php';
require __DIR__ . '/experimental/navigation-page.php';
diff --git a/package-lock.json b/package-lock.json
index e35bd0491294f..b14186b65b290 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "14.0.0-rc.1",
+ "version": "14.1.0-rc.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -7158,19 +7158,19 @@
"dev": true
},
"@playwright/test": {
- "version": "1.22.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.22.2.tgz",
- "integrity": "sha512-cCl96BEBGPtptFz7C2FOSN3PrTnJ3rPpENe+gYCMx4GNNDlN4tmo2D89y13feGKTMMAIVrXfSQ/UmaQKLy1XLA==",
+ "version": "1.25.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.25.1.tgz",
+ "integrity": "sha512-IJ4X0yOakXtwkhbnNzKkaIgXe6df7u3H3FnuhI9Jqh+CdO0e/lYQlDLYiyI9cnXK8E7UAppAWP+VqAv6VX7HQg==",
"dev": true,
"requires": {
"@types/node": "*",
- "playwright-core": "1.22.2"
+ "playwright-core": "1.25.1"
},
"dependencies": {
"playwright-core": {
- "version": "1.22.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.22.2.tgz",
- "integrity": "sha512-w/hc/Ld0RM4pmsNeE6aL/fPNWw8BWit2tg+TfqJ3+p59c6s3B6C8mXvXrIPmfQEobkcFDc+4KirNzOQ+uBSP1Q==",
+ "version": "1.25.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.25.1.tgz",
+ "integrity": "sha512-lSvPCmA2n7LawD2Hw7gSCLScZ+vYRkhU8xH0AapMyzwN+ojoDqhkH/KIEUxwNu2PjPoE/fcE0wLAksdOhJ2O5g==",
"dev": true
}
}
@@ -16912,6 +16912,7 @@
"version": "file:packages/date",
"requires": {
"@babel/runtime": "^7.16.0",
+ "@wordpress/deprecated": "file:packages/deprecated",
"moment": "^2.22.1",
"moment-timezone": "^0.5.31"
}
@@ -17131,8 +17132,7 @@
"@wordpress/reusable-blocks": "file:packages/reusable-blocks",
"@wordpress/url": "file:packages/url",
"@wordpress/widgets": "file:packages/widgets",
- "classnames": "^2.3.1",
- "lodash": "^4.17.21"
+ "classnames": "^2.3.1"
}
},
"@wordpress/editor": {
@@ -17432,8 +17432,7 @@
"@wordpress/i18n": "file:packages/i18n",
"@wordpress/icons": "file:packages/icons",
"@wordpress/rich-text": "file:packages/rich-text",
- "@wordpress/url": "file:packages/url",
- "lodash": "^4.17.21"
+ "@wordpress/url": "file:packages/url"
}
},
"@wordpress/hooks": {
@@ -17454,7 +17453,6 @@
"@babel/runtime": "^7.16.0",
"@wordpress/hooks": "file:packages/hooks",
"gettext-parser": "^1.3.1",
- "lodash": "^4.17.21",
"memize": "^1.1.0",
"sprintf-js": "^1.1.1",
"tannin": "^1.2.0"
@@ -17749,7 +17747,6 @@
"@babel/runtime": "^7.16.0",
"is-plain-object": "^5.0.0",
"is-promise": "^4.0.0",
- "lodash": "^4.17.21",
"rungen": "^0.3.2"
}
},
diff --git a/package.json b/package.json
index 01a1bb132966a..2943f22f95194 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "14.0.0-rc.1",
+ "version": "14.1.0-rc.1",
"private": true,
"description": "A new WordPress editor experience.",
"author": "The WordPress Contributors",
@@ -98,7 +98,7 @@
"@octokit/rest": "16.26.0",
"@octokit/types": "6.34.0",
"@octokit/webhooks-types": "5.6.0",
- "@playwright/test": "1.22.2",
+ "@playwright/test": "1.25.1",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.2",
"@storybook/addon-a11y": "6.5.7",
"@storybook/addon-actions": "6.5.7",
diff --git a/packages/babel-plugin-makepot/CHANGELOG.md b/packages/babel-plugin-makepot/CHANGELOG.md
index 26ea6ae682756..12cddacaf21f8 100644
--- a/packages/babel-plugin-makepot/CHANGELOG.md
+++ b/packages/babel-plugin-makepot/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### Bug Fix
+
+- Makepot: Fix translations object handling ([#43797](https://github.com/WordPress/gutenberg/pull/43797)).
+
## 5.0.0 (2022-08-24)
### Breaking Change
diff --git a/packages/babel-plugin-makepot/index.js b/packages/babel-plugin-makepot/index.js
index 32cb35dcc40c3..2273fdb4c4264 100644
--- a/packages/babel-plugin-makepot/index.js
+++ b/packages/babel-plugin-makepot/index.js
@@ -33,7 +33,7 @@
*/
const { po } = require( 'gettext-parser' );
-const { pick, reduce, forEach, isEqual, merge, isEmpty } = require( 'lodash' );
+const { pick, reduce, isEqual, merge, isEmpty } = require( 'lodash' );
const { relative, sep } = require( 'path' );
const { writeFileSync } = require( 'fs' );
@@ -122,7 +122,7 @@ function getExtractedComment( path, _originalNodeLine ) {
}
let comment;
- forEach( node.leadingComments, ( commentNode ) => {
+ Object.values( node.leadingComments ?? {} ).forEach( ( commentNode ) => {
let line = 0;
if ( commentNode && commentNode.loc && commentNode.loc.end ) {
line = commentNode.loc.end.line;
@@ -330,44 +330,38 @@ module.exports = () => {
for ( const context in strings[ file ] ) {
// Within the same file, sort translations by line.
const sortedTranslations = sortByReference(
- strings[ file ][ context ]
+ Object.values( strings[ file ][ context ] )
);
- forEach(
- sortedTranslations,
- ( translation ) => {
- const { msgctxt = '', msgid } =
- translation;
- if (
- ! memo.hasOwnProperty( msgctxt )
- ) {
- memo[ msgctxt ] = {};
- }
-
- // Merge references if translation already exists.
- if (
- isSameTranslation(
- translation,
- memo[ msgctxt ][ msgid ]
- )
- ) {
- translation.comments.reference = [
- ...new Set(
- [
- memo[ msgctxt ][ msgid ]
- .comments.reference,
- translation.comments
- .reference,
- ]
- .join( '\n' )
- .split( '\n' )
- ),
- ].join( '\n' );
- }
-
- memo[ msgctxt ][ msgid ] = translation;
+ sortedTranslations.forEach( ( translation ) => {
+ const { msgctxt = '', msgid } = translation;
+ if ( ! memo.hasOwnProperty( msgctxt ) ) {
+ memo[ msgctxt ] = {};
}
- );
+
+ // Merge references if translation already exists.
+ if (
+ isSameTranslation(
+ translation,
+ memo[ msgctxt ][ msgid ]
+ )
+ ) {
+ translation.comments.reference = [
+ ...new Set(
+ [
+ memo[ msgctxt ][ msgid ]
+ .comments.reference,
+ translation.comments
+ .reference,
+ ]
+ .join( '\n' )
+ .split( '\n' )
+ ),
+ ].join( '\n' );
+ }
+
+ memo[ msgctxt ][ msgid ] = translation;
+ } );
}
return memo;
diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss
index 7b3d3faea6117..95373ddd92b1f 100644
--- a/packages/base-styles/_mixins.scss
+++ b/packages/base-styles/_mixins.scss
@@ -202,6 +202,11 @@
}
}
+@mixin placeholder-style() {
+ border: $border-width dashed currentColor;
+ border-radius: $radius-block-ui;
+}
+
/**
* Allows users to opt-out of animations via OS-level preferences.
*/
@@ -226,7 +231,6 @@
animation-delay: 0s;
}
}
-
}
@mixin input-control {
@@ -371,6 +375,7 @@
* Reset default styles for JavaScript UI based pages.
* This is a WP-admin agnostic reset
*/
+
@mixin reset {
box-sizing: border-box;
@@ -384,6 +389,7 @@
/**
* Reset the WP Admin page styles for Gutenberg-like pages.
*/
+
@mixin wp-admin-reset( $content-container ) {
background: $white;
diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss
index f5b6331e6fa58..c14afaedf8add 100644
--- a/packages/base-styles/_z-index.scss
+++ b/packages/base-styles/_z-index.scss
@@ -15,9 +15,6 @@ $z-layers: (
".edit-post-sidebar__panel-tab.is-active": 1,
// These next three share a stacking context
- ".block-editor-inserter__tabs .components-tab-panel__tab-content": 0, // lower scrolling content
- ".block-editor-inserter__tabs .components-tab-panel__tabs": 1, // higher sticky element
- ".block-editor-inserter__search": 1, // higher sticky element
".block-library-template-part__selection-search": 1, // higher sticky element
// These next two share a stacking context
@@ -82,7 +79,7 @@ $z-layers: (
".block-editor-block-contextual-toolbar": 61,
// Ensures content overlay appears higher than resize containers used for image/video/etc.
- ".block-editor-block-content-overlay__overlay": 10,
+ ".block-editor-block-list__block.has-block-overlay": 10,
// Query block setup state.
".block-editor-block-pattern-setup .pattern-slide": 100,
diff --git a/packages/block-directory/src/store/reducer.js b/packages/block-directory/src/store/reducer.js
index 8d36001618097..1e06cedced2ca 100644
--- a/packages/block-directory/src/store/reducer.js
+++ b/packages/block-directory/src/store/reducer.js
@@ -1,9 +1,3 @@
-/**
- * External dependencies
- */
-
-import { omit } from 'lodash';
-
/**
* WordPress dependencies
*/
@@ -100,7 +94,8 @@ export const errorNotices = ( state = {}, action ) => {
},
};
case 'CLEAR_ERROR_NOTICE':
- return omit( state, action.blockId );
+ const { [ action.blockId ]: blockId, ...restState } = state;
+ return restState;
}
return state;
};
diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md
index 4e261a8a5fba9..6ef7ae6571ccb 100644
--- a/packages/block-editor/CHANGELOG.md
+++ b/packages/block-editor/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### Breaking change
+
+- `FontSizePicker`: Deprecate bottom margin style. Add a `__nextHasNoMarginBottom` prop to start opting into the margin-free styles that will become the default in a future version, currently scheduled to be WordPress 6.4 ([#43870](https://github.com/WordPress/gutenberg/pull/43870)).
+
## 9.8.0 (2022-08-24)
## 9.7.0 (2022-08-10)
diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md
index 305c32f9d5d7d..ccdb5b9e37595 100644
--- a/packages/block-editor/README.md
+++ b/packages/block-editor/README.md
@@ -474,6 +474,19 @@ _Returns_
- `string`: returns the cssUnit value in a simple px format.
+### getTypographyClassesAndStyles
+
+Provides the CSS class names and inline styles for a block's typography support
+attributes.
+
+_Parameters_
+
+- _attributes_ `Object`: Block attributes.
+
+_Returns_
+
+- `Object`: Typography block support derived CSS classes & styles.
+
### InnerBlocks
_Related_
@@ -717,7 +730,6 @@ _Parameters_
- _props_ `Object`: Optional. Props to pass to the element. Must contain the ref if one is defined.
- _options_ `Object`: Options for internal use only.
- _options.\_\_unstableIsHtml_ `boolean`:
-- _options.\_\_unstableIsDisabled_ `boolean`: Whether the block should be disabled.
_Returns_
diff --git a/packages/block-editor/src/components/alignment-control/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/alignment-control/test/__snapshots__/index.js.snap
index 4f549af0b2dc1..bf0267e8574d3 100644
--- a/packages/block-editor/src/components/alignment-control/test/__snapshots__/index.js.snap
+++ b/packages/block-editor/src/components/alignment-control/test/__snapshots__/index.js.snap
@@ -1,140 +1,169 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlignmentUI should allow custom alignment controls to be specified 1`] = `
-
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`AlignmentUI should match snapshot when controls are hidden 1`] = `
+
+
+
+
+
`;
-exports[`AlignmentUI should match snapshot 1`] = `
-
+
`;
diff --git a/packages/block-editor/src/components/block-vertical-alignment-control/test/index.js b/packages/block-editor/src/components/block-vertical-alignment-control/test/index.js
index 70cb8caf952fa..2e54ee9026854 100644
--- a/packages/block-editor/src/components/block-vertical-alignment-control/test/index.js
+++ b/packages/block-editor/src/components/block-vertical-alignment-control/test/index.js
@@ -1,7 +1,8 @@
/**
* External dependencies
*/
-import { shallow } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
/**
* Internal dependencies
@@ -12,44 +13,112 @@ describe( 'BlockVerticalAlignmentUI', () => {
const alignment = 'top';
const onChange = jest.fn();
- const wrapper = shallow(
-
- );
-
- const controls = wrapper.props().controls;
-
afterEach( () => {
onChange.mockClear();
} );
- it( 'should match snapshot', () => {
- expect( wrapper ).toMatchSnapshot();
+ test( 'should match snapshot when controls are hidden', () => {
+ const { container } = render(
+
+ );
+ expect( container ).toMatchSnapshot();
+ } );
+
+ test( 'should match snapshot when controls are visible', () => {
+ const { container } = render(
+
+ );
+ expect( container ).toMatchSnapshot();
} );
- it( 'should call onChange with undefined, when the control is already active', () => {
- const activeControl = controls.find( ( { title } ) =>
- title.toLowerCase().includes( alignment )
+ test( 'should expand controls when toggled', async () => {
+ const user = userEvent.setup( {
+ advanceTimers: jest.advanceTimersByTime,
+ } );
+
+ render(
+
+ );
+
+ expect(
+ screen.queryByRole( 'menuitemradio', {
+ name: /^Align \w+$/,
+ } )
+ ).not.toBeInTheDocument();
+
+ await user.click(
+ screen.getByRole( 'button', {
+ name: 'Change vertical alignment',
+ } )
);
- activeControl.onClick();
- expect( activeControl.isActive ).toBe( true );
+ expect(
+ screen.getAllByRole( 'menuitemradio', {
+ name: /^Align \w+$/,
+ } )
+ ).toHaveLength( 3 );
+ } );
+
+ it( 'should call onChange with undefined, when the control is already active', async () => {
+ const user = userEvent.setup( {
+ advanceTimers: jest.advanceTimersByTime,
+ } );
+
+ render(
+
+ );
+
+ const activeControl = screen.getByRole( 'button', {
+ name: `Align ${ alignment }`,
+ pressed: true,
+ } );
+
+ await user.click( activeControl );
+
expect( onChange ).toHaveBeenCalledTimes( 1 );
expect( onChange ).toHaveBeenCalledWith( undefined );
} );
- it( 'should call onChange with alignment value when the control is inactive', () => {
+ it( 'should call onChange with alignment value when the control is inactive', async () => {
// note "middle" alias for "center"
- const inactiveCenterControl = controls.find( ( { title } ) =>
- title.toLowerCase().includes( 'middle' )
+ const user = userEvent.setup( {
+ advanceTimers: jest.advanceTimersByTime,
+ } );
+
+ render(
+
);
- inactiveCenterControl.onClick();
+ const inactiveControl = screen.getByRole( 'button', {
+ name: 'Align middle',
+ pressed: false,
+ } );
+
+ await user.click( inactiveControl );
- expect( inactiveCenterControl.isActive ).toBe( false );
expect( onChange ).toHaveBeenCalledTimes( 1 );
expect( onChange ).toHaveBeenCalledWith( 'center' );
} );
diff --git a/packages/block-editor/src/components/block-vertical-alignment-control/ui.js b/packages/block-editor/src/components/block-vertical-alignment-control/ui.js
index a542cad29eb75..00f4ff6fd993c 100644
--- a/packages/block-editor/src/components/block-vertical-alignment-control/ui.js
+++ b/packages/block-editor/src/components/block-vertical-alignment-control/ui.js
@@ -47,11 +47,12 @@ function BlockVerticalAlignmentUI( {
BLOCK_ALIGNMENTS_CONTROLS[ DEFAULT_CONTROL ];
const UIComponent = isToolbar ? ToolbarGroup : ToolbarDropdownMenu;
- const extraProps = isToolbar ? { isCollapsed } : {};
+ const extraProps = isToolbar
+ ? { isCollapsed }
+ : { popoverProps: { POPOVER_PROPS } };
return (
&,
+ .is-layout-flow.block-editor-block-list__block:not(.is-selected) > & {
+ pointer-events: none;
+
+ &::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ pointer-events: none;
+ @include placeholder-style();
+ }
+
+ .block-editor-inserter {
+ visibility: hidden;
+ }
+ }
+}
diff --git a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap
index 172121132b243..7db5f3ee20eb6 100644
--- a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap
+++ b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap
@@ -29,15 +29,12 @@ exports[`ColorPaletteControl matches the snapshot 1`] = `
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
+ gap: calc(4px * 1);
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
}
-.emotion-4>*+*:not( marquee ) {
- margin-top: calc(4px * 1);
-}
-
.emotion-4>* {
min-height: 0;
}
@@ -64,15 +61,12 @@ exports[`ColorPaletteControl matches the snapshot 1`] = `
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
+ gap: calc(4px * 3);
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
}
-.emotion-8>*+*:not( marquee ) {
- margin-top: calc(4px * 3);
-}
-
.emotion-8>* {
min-height: 0;
}
@@ -89,16 +83,13 @@ exports[`ColorPaletteControl matches the snapshot 1`] = `
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
+ gap: calc(4px * 2);
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
justify-content: space-between;
width: 100%;
}
-.emotion-10>*+*:not( marquee ) {
- margin-left: calc(4px * 2);
-}
-
.emotion-10>* {
min-width: 0;
}
diff --git a/packages/block-editor/src/components/colors-gradients/control.js b/packages/block-editor/src/components/colors-gradients/control.js
index 241da40d5aa3a..34e4f334e3c58 100644
--- a/packages/block-editor/src/components/colors-gradients/control.js
+++ b/packages/block-editor/src/components/colors-gradients/control.js
@@ -92,6 +92,7 @@ function ColorGradientControlInner( {
),
[ TAB_GRADIENT.value ]: (
-`;
diff --git a/packages/block-editor/src/components/colors/test/with-colors.js b/packages/block-editor/src/components/colors/test/with-colors.js
index fefc3c68527d9..dc1efe7e386da 100644
--- a/packages/block-editor/src/components/colors/test/with-colors.js
+++ b/packages/block-editor/src/components/colors/test/with-colors.js
@@ -1,7 +1,8 @@
/**
* External dependencies
*/
-import { shallow, mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
/**
* Internal dependencies
@@ -13,18 +14,37 @@ describe( 'createCustomColorsHOC', () => {
const withCustomColors = createCustomColorsHOC( [
{ name: 'Red', slug: 'red', color: 'ff0000' },
] );
- const EnhancedComponent = withCustomColors( 'backgroundColor' )( () => (
-
- ) );
+ const BaseComponent = jest.fn( () => );
+ const EnhancedComponent =
+ withCustomColors( 'backgroundColor' )( BaseComponent );
- const wrapper = shallow(
+ render(
);
- expect( wrapper.dive() ).toMatchSnapshot();
+ expect( BaseComponent ).toHaveBeenCalledWith(
+ expect.objectContaining( {
+ attributes: {
+ backgroundColor: null,
+ },
+ backgroundColor: {
+ class: undefined,
+ color: undefined,
+ },
+ colorUtils: {
+ getMostReadableColor: expect.any( Function ),
+ },
+ colors: undefined,
+ setBackgroundColor: expect.any( Function ),
+ } ),
+ expect.anything()
+ );
} );
- it( 'setting the color to a value in the provided custom color array updated the backgroundColor attribute', () => {
+ it( 'setting the color to a value in the provided custom color array updated the backgroundColor attribute', async () => {
+ const user = userEvent.setup( {
+ advanceTimers: jest.advanceTimersByTime,
+ } );
const withCustomColors = createCustomColorsHOC( [
{ name: 'Red', slug: 'red', color: 'ff0000' },
] );
@@ -38,21 +58,25 @@ describe( 'createCustomColorsHOC', () => {
const setAttributes = jest.fn();
- const wrapper = mount(
+ render(
);
- wrapper.find( 'button' ).simulate( 'click' );
+ await user.click( screen.getByRole( 'button' ) );
+
expect( setAttributes ).toHaveBeenCalledWith( {
backgroundColor: 'red',
customBackgroundColor: undefined,
} );
} );
- it( 'setting the color to a value not in the provided custom color array updates customBackgroundColor attribute', () => {
+ it( 'setting the color to a value not in the provided custom color array updates customBackgroundColor attribute', async () => {
+ const user = userEvent.setup( {
+ advanceTimers: jest.advanceTimersByTime,
+ } );
const withCustomColors = createCustomColorsHOC( [
{ name: 'Red', slug: 'red', color: 'ff0000' },
] );
@@ -66,14 +90,15 @@ describe( 'createCustomColorsHOC', () => {
const setAttributes = jest.fn();
- const wrapper = mount(
+ render(
);
- wrapper.find( 'button' ).simulate( 'click' );
+ await user.click( screen.getByRole( 'button' ) );
+
expect( setAttributes ).toHaveBeenCalledWith( {
backgroundColor: undefined,
customBackgroundColor: '000000',
diff --git a/packages/block-editor/src/components/contrast-checker/test/index.js b/packages/block-editor/src/components/contrast-checker/test/index.js
index a686762a3c8fc..af5c0b62bbc67 100644
--- a/packages/block-editor/src/components/contrast-checker/test/index.js
+++ b/packages/block-editor/src/components/contrast-checker/test/index.js
@@ -1,14 +1,11 @@
/**
* External dependencies
*/
-import { mount } from 'enzyme';
-import { render } from 'react-dom';
-import { act } from 'react-dom/test-utils';
+import { render, screen } from '@testing-library/react';
/**
* WordPress dependencies
*/
-import { Notice } from '@wordpress/components';
import { speak } from '@wordpress/a11y';
/**
@@ -32,12 +29,13 @@ describe( 'ContrastChecker', () => {
speak.mockReset();
} );
- test( 'should render null when no colors are provided', () => {
- expect( mount( ).html() ).toBeNull();
+ test( 'should render nothing when no colors are provided', () => {
+ const { container } = render( );
+ expect( container ).toBeEmptyDOMElement();
} );
- test( 'should render null when no background or fallback background color is provided', () => {
- const wrapper = mount(
+ test( 'should render nothing when no background or fallback background color is provided', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
- test( 'should render null when the colors meet AA WCAG guidelines.', () => {
- const wrapper = mount(
+ test( 'should render nothing when the colors meet AA WCAG guidelines.', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
test( 'should render component when the text and background colors do not meet AA WCAG guidelines.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
- );
+
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
+ )
+ ).toBeVisible();
} );
test( 'should render component when the link and background colors do not meet AA WCAG guidelines.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.'
+ )
+ ).toBeVisible();
} );
test( 'should render component when the link and text and background colors do not meet AA WCAG guidelines.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
+ )
+ ).toBeVisible();
} );
- test( 'should render render null if background color contains a transparency', () => {
- const wrapper = mount(
+ test( 'should render nothing if background color contains a transparency', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
- test( 'should render render null if text color contains a transparency', () => {
- const wrapper = mount(
+ test( 'should render nothing if text color contains a transparency', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
- test( 'should render render null if link color contains a transparency', () => {
- const wrapper = mount(
+ test( 'should render nothing if link color contains a transparency', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
test( 'should render different message matching snapshot when background color has less brightness than text color.', () => {
const darkerShade = '#555';
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a darker background color and/or a brighter text color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a darker background color and/or a brighter text color.'
+ )
+ ).toBeVisible();
} );
test( 'should take into consideration wherever text is large or not', () => {
- const wrapperSmallText = mount(
+ const { container, rerender } = render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapperSmallText.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
+ )
+ ).toBeVisible();
- const wrapperLargeText = mount(
+ rerender(
{
/>
);
- expect( wrapperLargeText.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
test( 'should take into consideration the font size passed', () => {
- const wrapperSmallFontSize = mount(
+ const { container, rerender } = render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapperSmallFontSize.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
+ )
+ ).toBeVisible();
- const wrapperLargeText = mount(
+ rerender(
{
/>
);
- expect( wrapperLargeText.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
test( 'should use isLargeText to make decisions if both isLargeText and fontSize props are passed', () => {
- const wrapper = mount(
+ const { container, rerender } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
- const wrapperNoLargeText = mount(
+ rerender(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapperNoLargeText.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
+ )
+ ).toBeVisible();
} );
- test( 'should render null when the colors meet AA WCAG guidelines, with only fallback colors.', () => {
- const wrapper = mount(
+ test( 'should render nothing when the colors meet AA WCAG guidelines, with only fallback colors.', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
test( 'should render messages when the textColor is valid, but the fallback backgroundColor conflicts.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.'
+ )
+ ).toBeVisible();
} );
test( 'should render messages when the linkColor is valid, but the fallback backgroundColor conflicts.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.'
+ )
+ ).toBeVisible();
} );
test( 'should re-announce if colors change, but still insufficient contrast', () => {
- const appRoot = document.createElement( 'div' );
-
- act( () => {
- render(
- ,
- appRoot
- );
- } );
-
- act( () => {
- render(
- ,
- appRoot
- );
- } );
+ const { rerender } = render(
+
+ );
+
+ rerender(
+
+ );
expect( speak ).toHaveBeenCalledTimes( 2 );
} );
// enableAlphaChecker tests
- test( 'should render null when the colors meet AA WCAG guidelines and alpha checker enabled.', () => {
- const wrapper = mount(
+ test( 'should render nothing when the colors meet AA WCAG guidelines and alpha checker enabled.', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
test( 'should render component when the colors meet AA WCAG guidelines but the text color only has alpha transparency with alpha checker enabled.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'Transparent text may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'Transparent text may be hard for people to read.'
- );
+ expect(
+ screen.getByText(
+ 'Transparent text may be hard for people to read.'
+ )
+ ).toBeVisible();
} );
test( 'should render component when the colors meet AA WCAG guidelines but the link color only has alpha transparency with alpha checker enabled.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'Transparent text may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'Transparent text may be hard for people to read.'
- );
+ expect(
+ screen.getByText(
+ 'Transparent text may be hard for people to read.'
+ )
+ ).toBeVisible();
} );
- test( 'should render null when the colors meet AA WCAG guidelines but the background color only has alpha transparency with alpha checker enabled.', () => {
- const wrapper = mount(
+ test( 'should render nothing when the colors meet AA WCAG guidelines but the background color only has alpha transparency with alpha checker enabled.', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
- test( 'should render null if background color contains a transparency with alpha checker enabled.', () => {
- const wrapper = mount(
+ test( 'should render nothing if background color contains a transparency with alpha checker enabled.', () => {
+ const { container } = render(
{
);
expect( speak ).not.toHaveBeenCalled();
- expect( wrapper.html() ).toBeNull();
+ expect( container ).toBeEmptyDOMElement();
} );
test( 'should render transparency warning only if one text is not readable but the other is transparent and the background color contains a transparency with alpha checker enabled.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'Transparent text may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'Transparent text may be hard for people to read.'
- );
+ expect(
+ screen.getByText(
+ 'Transparent text may be hard for people to read.'
+ )
+ ).toBeVisible();
} );
test( 'should render component and prioritize contrast warning when the colors do no meet AA WCAG guidelines and text has alpha transparency with the alpha checker enabled.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'This color combination may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.'
- );
+ expect(
+ screen.getByText(
+ 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.'
+ )
+ ).toBeVisible();
} );
test( 'should render component when the colors meet AA WCAG guidelines but all colors have alpha transparency with alpha checker enabled.', () => {
- const wrapper = mount(
+ render(
{
expect( speak ).toHaveBeenCalledWith(
'Transparent text may be hard for people to read.'
);
- expect( wrapper.find( Notice ).children().text() ).toBe(
- 'Transparent text may be hard for people to read.'
- );
+ expect(
+ screen.getByText(
+ 'Transparent text may be hard for people to read.'
+ )
+ ).toBeVisible();
} );
} );
diff --git a/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap
index 344bb95ea044b..3a0a98578a2a5 100644
--- a/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap
+++ b/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap
@@ -1,73 +1,55 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DefaultBlockAppender should append a default block when input focused 1`] = `
-
-
+
- Type / to choose a block
-
-
+
+ Type / to choose a block
+
+
`;
exports[`DefaultBlockAppender should match snapshot 1`] = `
-
-
+
- Type / to choose a block
-
-
+
+ Type / to choose a block
+
+
`;
exports[`DefaultBlockAppender should optionally show without prompt 1`] = `
-
-
+
-
-
-
+
+
+
+
`;
diff --git a/packages/block-editor/src/components/default-block-appender/test/index.js b/packages/block-editor/src/components/default-block-appender/test/index.js
index b14463d335163..0a00a00d5d881 100644
--- a/packages/block-editor/src/components/default-block-appender/test/index.js
+++ b/packages/block-editor/src/components/default-block-appender/test/index.js
@@ -1,7 +1,8 @@
/**
* External dependencies
*/
-import { shallow } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
/**
* Internal dependencies
@@ -9,42 +10,55 @@ import { shallow } from 'enzyme';
import { DefaultBlockAppender, ZWNBSP } from '../';
describe( 'DefaultBlockAppender', () => {
- const expectOnAppendCalled = ( onAppend ) => {
- expect( onAppend ).toHaveBeenCalledTimes( 1 );
- expect( onAppend ).toHaveBeenCalledWith();
- };
-
it( 'should match snapshot', () => {
const onAppend = jest.fn();
- const wrapper = shallow(
+
+ const { container } = render(
);
- expect( wrapper ).toMatchSnapshot();
+ expect( container ).toMatchSnapshot();
} );
- it( 'should append a default block when input focused', () => {
+ it( 'should append a default block when input focused', async () => {
+ const user = userEvent.setup( {
+ advanceTimers: jest.advanceTimersByTime,
+ } );
const onAppend = jest.fn();
- const wrapper = shallow(
+
+ const { container } = render(
);
- wrapper.find( 'p' ).simulate( 'click' );
+ await user.click(
+ screen.getByRole( 'button', { name: 'Add default block' } )
+ );
- expect( wrapper ).toMatchSnapshot();
+ expect( container ).toMatchSnapshot();
- expectOnAppendCalled( onAppend );
+ // Called once for focusing and once for clicking.
+ expect( onAppend ).toHaveBeenCalledTimes( 2 );
+ expect( onAppend ).toHaveBeenCalledWith();
} );
- it( 'should optionally show without prompt', () => {
+ it( 'should optionally show without prompt', async () => {
+ const user = userEvent.setup( {
+ advanceTimers: jest.advanceTimersByTime,
+ } );
const onAppend = jest.fn();
- const wrapper = shallow(
+
+ const { container } = render(
);
- const input = wrapper.find( 'p' );
- expect( input.prop( 'children' ) ).toEqual( ZWNBSP );
+ const appender = screen.getByRole( 'button', {
+ name: 'Add default block',
+ } );
+
+ await user.click( appender );
+
+ expect( appender ).toContainHTML( ZWNBSP );
- expect( wrapper ).toMatchSnapshot();
+ expect( container ).toMatchSnapshot();
} );
} );
diff --git a/packages/block-editor/src/components/font-sizes/README.MD b/packages/block-editor/src/components/font-sizes/README.MD
index 69354b90f2a35..adfe0ca8853d8 100644
--- a/packages/block-editor/src/components/font-sizes/README.MD
+++ b/packages/block-editor/src/components/font-sizes/README.MD
@@ -31,6 +31,7 @@ const MyFontSizePicker = () => {
return (
{
@@ -79,3 +80,11 @@ If `true`, the UI will contain a slider, instead of a numeric text input field.
- Type: `Boolean`
- Required: no
- Default: `false`
+
+### __nextHasNoMarginBottom
+
+Start opting into the new margin-free styles that will become the default in a future version, currently scheduled to be WordPress 6.4. (The prop can be safely removed once this happens.)
+
+- Type: `Boolean`
+- Required: no
+- Default: `false`
diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js
index 84abe873ce9a1..29fe41f891636 100644
--- a/packages/block-editor/src/components/iframe/index.js
+++ b/packages/block-editor/src/components/iframe/index.js
@@ -14,7 +14,11 @@ import {
useReducer,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
-import { useMergeRefs, useRefEffect } from '@wordpress/compose';
+import {
+ useResizeObserver,
+ useMergeRefs,
+ useRefEffect,
+} from '@wordpress/compose';
import { __experimentalStyleProvider as StyleProvider } from '@wordpress/components';
/**
@@ -184,7 +188,7 @@ async function loadScript( head, { id, src } ) {
}
function Iframe(
- { contentRef, children, head, tabIndex = 0, assets, ...props },
+ { contentRef, children, head, tabIndex = 0, assets, isZoomedOut, ...props },
ref
) {
const [ , forceRender ] = useReducer( () => ( {} ) );
@@ -194,10 +198,18 @@ function Iframe(
const scripts = useParsedAssets( assets?.scripts );
const clearerRef = useBlockSelectionClearer();
const [ before, writingFlowRef, after ] = useWritingFlow();
+ const [ contentResizeListener, { height: contentHeight } ] =
+ useResizeObserver();
const setRef = useRefEffect( ( node ) => {
+ let iFrameDocument;
+ // Prevent the default browser action for files dropped outside of dropzones.
+ function preventFileDropDefault( event ) {
+ event.preventDefault();
+ }
function setDocumentIfReady() {
const { contentDocument, ownerDocument } = node;
const { readyState, documentElement } = contentDocument;
+ iFrameDocument = contentDocument;
if ( readyState !== 'interactive' && readyState !== 'complete' ) {
return false;
@@ -223,14 +235,35 @@ function Iframe(
documentElement.removeChild( contentDocument.head );
documentElement.removeChild( contentDocument.body );
+ iFrameDocument.addEventListener(
+ 'dragover',
+ preventFileDropDefault,
+ false
+ );
+ iFrameDocument.addEventListener(
+ 'drop',
+ preventFileDropDefault,
+ false
+ );
return true;
}
// Document set with srcDoc is not immediately ready.
node.addEventListener( 'load', setDocumentIfReady );
- return () => node.removeEventListener( 'load', setDocumentIfReady );
+ return () => {
+ node.removeEventListener( 'load', setDocumentIfReady );
+ iFrameDocument?.removeEventListener(
+ 'dragover',
+ preventFileDropDefault
+ );
+ iFrameDocument?.removeEventListener(
+ 'drop',
+ preventFileDropDefault
+ );
+ };
}, [] );
+
const headRef = useRefEffect( ( element ) => {
scripts
.reduce(
@@ -285,14 +318,40 @@ function Iframe(
{ iframeDocument &&
createPortal(
<>
- { head }
+
+ { head }
+
+
+ { contentResizeListener }
{ /*
* This is a wrapper for the extra styles and scripts
* rendered imperatively by cloning the parent,
diff --git a/packages/block-editor/src/components/inner-blocks/README.md b/packages/block-editor/src/components/inner-blocks/README.md
index 029d6cf1dad94..73cc5bef95db8 100644
--- a/packages/block-editor/src/components/inner-blocks/README.md
+++ b/packages/block-editor/src/components/inner-blocks/README.md
@@ -125,11 +125,12 @@ Template locking of `InnerBlocks` is similar to [Custom Post Type templates lock
Template locking allows locking the `InnerBlocks` area for the current template.
_Options:_
+- `noContent` — prevents all operations. Additionally, the block types that don't have content are hidden from the list view and can't gain focus within the block list. Unlike the other lock types, this is not overrideable by children.
- `'all'` — prevents all operations. It is not possible to insert new blocks. Move existing blocks or delete them.
- `'insert'` — prevents inserting or removing blocks, but allows moving existing ones.
- `false` — prevents locking from being applied to an `InnerBlocks` area even if a parent block contains locking. ( Boolean )
-If locking is not set in an `InnerBlocks` area: the locking of the parent `InnerBlocks` area is used.
+If locking is not set in an `InnerBlocks` area: the locking of the parent `InnerBlocks` area is used. Note that `noContent` can't be overriden: it's present, the `templateLock` value of any children is ignored.
If the block is a top level block: the locking of the Custom Post Type is used.
diff --git a/packages/block-editor/src/components/inner-blocks/default-block-appender.js b/packages/block-editor/src/components/inner-blocks/default-block-appender.js
index b6fc392cdcaef..d2e137004d83b 100644
--- a/packages/block-editor/src/components/inner-blocks/default-block-appender.js
+++ b/packages/block-editor/src/components/inner-blocks/default-block-appender.js
@@ -1,8 +1,3 @@
-/**
- * External dependencies
- */
-import { last } from 'lodash';
-
/**
* WordPress dependencies
*/
@@ -28,7 +23,7 @@ export default compose( [
const blockClientIds = getBlockOrder( clientId );
return {
- lastBlockClientId: last( blockClientIds ),
+ lastBlockClientId: blockClientIds[ blockClientIds.length - 1 ],
};
} ),
] )( DefaultBlockAppender );
diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js
index 8a75060b434b6..a0d5c89cec99e 100644
--- a/packages/block-editor/src/components/inner-blocks/index.js
+++ b/packages/block-editor/src/components/inner-blocks/index.js
@@ -155,10 +155,11 @@ export function useInnerBlocksProps( props = {}, options = {} ) {
getBlockName,
isBlockSelected,
hasSelectedInnerBlock,
- isNavigationMode,
+ __unstableGetEditorMode,
} = select( blockEditorStore );
const blockName = getBlockName( clientId );
- const enableClickThrough = isNavigationMode() || isSmallScreen;
+ const enableClickThrough =
+ __unstableGetEditorMode() === 'navigation' || isSmallScreen;
return {
__experimentalCaptureToolbars: select(
blocksStore
diff --git a/packages/block-editor/src/components/inner-blocks/index.native.js b/packages/block-editor/src/components/inner-blocks/index.native.js
index 27bfec37adaf5..2b5ffe01aaf08 100644
--- a/packages/block-editor/src/components/inner-blocks/index.native.js
+++ b/packages/block-editor/src/components/inner-blocks/index.native.js
@@ -21,6 +21,7 @@ import getBlockContext from './get-block-context';
* Internal dependencies
*/
import BlockList from '../block-list';
+import BlockListCompact from '../block-list/block-list-compact';
import { useBlockEditContext } from '../block-edit/context';
import useBlockSync from '../provider/use-block-sync';
import { BlockContextProvider } from '../block-context';
@@ -96,6 +97,7 @@ function UncontrolledInnerBlocks( props ) {
blockWidth,
__experimentalLayout: layout = defaultLayout,
gridProperties,
+ useCompactList,
} = props;
const block = useSelect(
@@ -112,8 +114,10 @@ function UncontrolledInnerBlocks( props ) {
templateInsertUpdatesSelection
);
+ const BlockListComponent = useCompactList ? BlockListCompact : BlockList;
+
let blockList = (
- {
- // Only synchronize innerBlocks with template if innerBlocks are empty or
- // a locking all exists directly on the block.
- if ( innerBlocks.length === 0 || templateLock === 'all' ) {
+ // Only synchronize innerBlocks with template if innerBlocks are empty
+ // or a locking "all" or "noContent" exists directly on the block.
+ if (
+ innerBlocks.length === 0 ||
+ templateLock === 'all' ||
+ templateLock === 'noContent'
+ ) {
const hasTemplateChanged = ! isEqual(
template,
existingTemplate.current
diff --git a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js
index d0bf71d1e72d2..c8e2e3443ae15 100644
--- a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js
+++ b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js
@@ -69,7 +69,9 @@ export default function useNestedSettingsUpdate(
const newSettings = {
allowedBlocks: _allowedBlocks,
templateLock:
- templateLock === undefined ? parentLock : templateLock,
+ templateLock === undefined || parentLock === 'noContent'
+ ? parentLock
+ : templateLock,
};
// These values are not defined for RN, so only include them if they
diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js
index 08ba4e209955e..17183d13a8709 100644
--- a/packages/block-editor/src/components/inserter/index.js
+++ b/packages/block-editor/src/components/inserter/index.js
@@ -168,6 +168,7 @@ class Inserter extends Component {
clientId={ clientId }
isAppender={ isAppender }
showInserterHelpPanel={ showInserterHelpPanel }
+ prioritizePatterns={ prioritizePatterns }
/>
);
}
@@ -212,8 +213,6 @@ export default compose( [
hasInserterItems,
__experimentalGetAllowedBlocks,
__experimentalGetDirectInsertBlock,
- getBlockIndex,
- getBlockCount,
getSettings,
} = select( blockEditorStore );
@@ -227,8 +226,6 @@ export default compose( [
const directInsertBlock =
__experimentalGetDirectInsertBlock( rootClientId );
- const index = getBlockIndex( clientId );
- const blockCount = getBlockCount();
const settings = getSettings();
const hasSingleBlockType =
@@ -249,10 +246,7 @@ export default compose( [
directInsertBlock,
rootClientId,
prioritizePatterns:
- settings.__experimentalPreferPatternsOnRoot &&
- ! rootClientId &&
- index > 0 &&
- ( index < blockCount || blockCount === 0 ),
+ settings.__experimentalPreferPatternsOnRoot && ! rootClientId,
};
} ),
withDispatch( ( dispatch, ownProps, { select } ) => {
diff --git a/packages/block-editor/src/components/inserter/library.js b/packages/block-editor/src/components/inserter/library.js
index aba22e3e1f3fb..23dfdb7fd7dc6 100644
--- a/packages/block-editor/src/components/inserter/library.js
+++ b/packages/block-editor/src/components/inserter/library.js
@@ -26,13 +26,18 @@ function InserterLibrary(
},
ref
) {
- const destinationRootClientId = useSelect(
+ const { destinationRootClientId, prioritizePatterns } = useSelect(
( select ) => {
- const { getBlockRootClientId } = select( blockEditorStore );
+ const { getBlockRootClientId, getSettings } =
+ select( blockEditorStore );
- return (
- rootClientId || getBlockRootClientId( clientId ) || undefined
- );
+ const _rootClientId =
+ rootClientId || getBlockRootClientId( clientId ) || undefined;
+ return {
+ destinationRootClientId: _rootClientId,
+ prioritizePatterns:
+ getSettings().__experimentalPreferPatternsOnRoot,
+ };
},
[ clientId, rootClientId ]
);
@@ -48,6 +53,7 @@ function InserterLibrary(
__experimentalInsertionIndex={ __experimentalInsertionIndex }
__experimentalFilterValue={ __experimentalFilterValue }
shouldFocusBlock={ shouldFocusBlock }
+ prioritizePatterns={ prioritizePatterns }
ref={ ref }
/>
);
diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js
index 5c33cf5dd4604..5a10d07dd1caa 100644
--- a/packages/block-editor/src/components/inserter/menu.js
+++ b/packages/block-editor/src/components/inserter/menu.js
@@ -1,3 +1,8 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
/**
* WordPress dependencies
*/
@@ -37,6 +42,7 @@ function InserterMenu(
showMostUsedBlocks,
__experimentalFilterValue = '',
shouldFocusBlock = true,
+ prioritizePatterns,
},
ref
) {
@@ -180,23 +186,28 @@ function InserterMenu(
},
} ) );
+ const showAsTabs = ! filterValue && ( showPatterns || hasReusableBlocks );
+
return (
-
- { /* The following div is necessary to fix the sticky position of the search form. */ }
-
We\'re a studio in Berlin with an international practice in architecture, urban planning and interior design. We believe in sharing knowledge and promoting dialogue to increase the creative potential of collaboration.
- { !! inherit ||
- layoutType?.name === 'constrained'
- ? __(
- 'Nested blocks use theme content width with options for full and wide widths.'
- )
- : __(
- 'Nested blocks will fill the width of this container.'
- ) }
-
>
) }
@@ -200,7 +206,7 @@ function LayoutPanel( { setAttributes, attributes, name: blockName } ) {
layoutBlockSupport={ layoutBlockSupport }
/>
) }
- { constrainedType && !! contentSize && (
+ { constrainedType && displayControlsForLegacyLayouts && (
);
}
diff --git a/packages/block-editor/src/hooks/line-height.js b/packages/block-editor/src/hooks/line-height.js
index 7f4c2d5a4aa01..c8397d850a1e5 100644
--- a/packages/block-editor/src/hooks/line-height.js
+++ b/packages/block-editor/src/hooks/line-height.js
@@ -42,6 +42,7 @@ export function LineHeightEdit( props ) {
__nextHasNoMarginBottom={ true }
value={ style?.typography?.lineHeight }
onChange={ onChange }
+ size="__unstable-large"
/>
);
}
diff --git a/packages/block-editor/src/hooks/lock.js b/packages/block-editor/src/hooks/lock.js
index 24ac0feeaa2ef..7e9423edcc4bb 100644
--- a/packages/block-editor/src/hooks/lock.js
+++ b/packages/block-editor/src/hooks/lock.js
@@ -1,8 +1,3 @@
-/**
- * External dependencies
- */
-import { has } from 'lodash';
-
/**
* WordPress dependencies
*/
@@ -17,7 +12,7 @@ import { addFilter } from '@wordpress/hooks';
*/
export function addAttribute( settings ) {
// Allow blocks to specify their own attribute definition with default values if needed.
- if ( has( settings.attributes, [ 'lock', 'type' ] ) ) {
+ if ( 'type' in ( settings.attributes?.lock ?? {} ) ) {
return settings;
}
// Gracefully handle if settings.attributes is undefined.
diff --git a/packages/block-editor/src/hooks/metadata-name.js b/packages/block-editor/src/hooks/metadata-name.js
new file mode 100644
index 0000000000000..6eecb0ce3667c
--- /dev/null
+++ b/packages/block-editor/src/hooks/metadata-name.js
@@ -0,0 +1,48 @@
+/**
+ * WordPress dependencies
+ */
+import { addFilter } from '@wordpress/hooks';
+/**
+ * Internal dependencies
+ */
+import { hasBlockMetadataSupport } from './metadata';
+
+/**
+ * Filters registered block settings, adding an `__experimentalLabel` callback if one does not already exist.
+ *
+ * @param {Object} settings Original block settings.
+ *
+ * @return {Object} Filtered block settings.
+ */
+export function addLabelCallback( settings ) {
+ // If blocks provide their own label callback, do not override it.
+ if ( settings.__experimentalLabel ) {
+ return settings;
+ }
+
+ const supportsBlockNaming = hasBlockMetadataSupport(
+ settings,
+ 'name',
+ false // default value
+ );
+
+ // Check whether block metadata is supported before using it.
+ if ( supportsBlockNaming ) {
+ settings.__experimentalLabel = ( attributes, { context } ) => {
+ const { metadata } = attributes;
+
+ // In the list view, use the block's name attribute as the label.
+ if ( context === 'list-view' && metadata?.name ) {
+ return metadata.name;
+ }
+ };
+ }
+
+ return settings;
+}
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'core/metadata/addLabelCallback',
+ addLabelCallback
+);
diff --git a/packages/block-editor/src/hooks/metadata.js b/packages/block-editor/src/hooks/metadata.js
new file mode 100644
index 0000000000000..5ed4870d56e4f
--- /dev/null
+++ b/packages/block-editor/src/hooks/metadata.js
@@ -0,0 +1,64 @@
+/**
+ * WordPress dependencies
+ */
+import { addFilter } from '@wordpress/hooks';
+import { getBlockSupport } from '@wordpress/blocks';
+
+const META_ATTRIBUTE_NAME = 'metadata';
+
+export function hasBlockMetadataSupport( blockType, feature = '' ) {
+ const support = getBlockSupport( blockType, '__experimentalMetadata' );
+ return !! ( true === support || support?.[ feature ] );
+}
+
+/**
+ * Filters registered block settings, extending attributes to include `metadata`.
+ *
+ * see: https://github.com/WordPress/gutenberg/pull/40393/files#r864632012
+ *
+ * @param {Object} blockTypeSettings Original block settings.
+ * @return {Object} Filtered block settings.
+ */
+export function addMetaAttribute( blockTypeSettings ) {
+ // Allow blocks to specify their own attribute definition with default values if needed.
+ if ( blockTypeSettings?.attributes?.[ META_ATTRIBUTE_NAME ]?.type ) {
+ return blockTypeSettings;
+ }
+
+ const supportsBlockNaming = hasBlockMetadataSupport(
+ blockTypeSettings,
+ 'name',
+ false
+ );
+
+ if ( supportsBlockNaming ) {
+ blockTypeSettings.attributes = {
+ ...blockTypeSettings.attributes,
+ [ META_ATTRIBUTE_NAME ]: {
+ type: 'object',
+ },
+ };
+ }
+
+ return blockTypeSettings;
+}
+
+export function addSaveProps( extraProps, blockType, attributes ) {
+ if ( hasBlockMetadataSupport( blockType ) ) {
+ extraProps[ META_ATTRIBUTE_NAME ] = attributes[ META_ATTRIBUTE_NAME ];
+ }
+
+ return extraProps;
+}
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'core/metadata/addMetaAttribute',
+ addMetaAttribute
+);
+
+addFilter(
+ 'blocks.getSaveContent.extraProps',
+ 'core/metadata/save-props',
+ addSaveProps
+);
diff --git a/packages/block-editor/src/hooks/test/gap.js b/packages/block-editor/src/hooks/test/gap.js
index 1e9d76079cb00..a08e5a6759b2a 100644
--- a/packages/block-editor/src/hooks/test/gap.js
+++ b/packages/block-editor/src/hooks/test/gap.js
@@ -27,28 +27,6 @@ describe( 'gap', () => {
...blockGapValue,
} );
} );
- it( 'should unwrap var: values from a string into a CSS var() function', () => {
- const expectedValue = {
- top: 'var(--wp--preset--spacing--60)',
- left: 'var(--wp--preset--spacing--60)',
- };
- expect(
- getGapBoxControlValueFromStyle( 'var:preset|spacing|60' )
- ).toEqual( expectedValue );
- } );
- it( 'should unwrap var: values from an object into a CSS var() function', () => {
- const expectedValue = {
- top: 'var(--wp--preset--spacing--20)',
- left: 'var(--wp--preset--spacing--60)',
- };
- const blockGapValue = {
- top: 'var:preset|spacing|20',
- left: 'var:preset|spacing|60',
- };
- expect( getGapBoxControlValueFromStyle( blockGapValue ) ).toEqual(
- expectedValue
- );
- } );
} );
describe( 'getGapCSSValue()', () => {
it( 'should return `null` if argument is falsey', () => {
@@ -84,5 +62,21 @@ describe( 'gap', () => {
'88px 1px'
);
} );
+
+ it( 'should unwrap var: values from a string into a CSS var() function', () => {
+ expect( getGapCSSValue( 'var:preset|spacing|60' ) ).toEqual(
+ 'var(--wp--preset--spacing--60)'
+ );
+ } );
+
+ it( 'should unwrap var: values from an object into a CSS var() function and return shorthand values', () => {
+ const blockGapValue = {
+ top: 'var:preset|spacing|20',
+ left: 'var:preset|spacing|60',
+ };
+ expect( getGapCSSValue( blockGapValue ) ).toEqual(
+ 'var(--wp--preset--spacing--20) var(--wp--preset--spacing--60)'
+ );
+ } );
} );
} );
diff --git a/packages/block-editor/src/hooks/test/use-typography-props.js b/packages/block-editor/src/hooks/test/use-typography-props.js
new file mode 100644
index 0000000000000..93e4de29bc734
--- /dev/null
+++ b/packages/block-editor/src/hooks/test/use-typography-props.js
@@ -0,0 +1,28 @@
+/**
+ * Internal dependencies
+ */
+import { getTypographyClassesAndStyles } from '../use-typography-props';
+
+describe( 'getTypographyClassesAndStyles', () => {
+ it( 'should return styles and classes', () => {
+ const attributes = {
+ fontFamily: 'tofu',
+ fontSize: 'large',
+ style: {
+ typography: {
+ letterSpacing: '22px',
+ fontSize: '2rem',
+ textTransform: 'uppercase',
+ },
+ },
+ };
+ expect( getTypographyClassesAndStyles( attributes ) ).toEqual( {
+ className: 'has-tofu-font-family has-large-font-size',
+ style: {
+ letterSpacing: '22px',
+ fontSize: '2rem',
+ textTransform: 'uppercase',
+ },
+ } );
+ } );
+} );
diff --git a/packages/block-editor/src/hooks/text-decoration.js b/packages/block-editor/src/hooks/text-decoration.js
index 65f0aadf77d1e..17ba9ee73f698 100644
--- a/packages/block-editor/src/hooks/text-decoration.js
+++ b/packages/block-editor/src/hooks/text-decoration.js
@@ -46,6 +46,7 @@ export function TextDecorationEdit( props ) {
);
}
diff --git a/packages/block-editor/src/hooks/text-transform.js b/packages/block-editor/src/hooks/text-transform.js
index b2710b5f654b5..588327633ecb8 100644
--- a/packages/block-editor/src/hooks/text-transform.js
+++ b/packages/block-editor/src/hooks/text-transform.js
@@ -46,6 +46,7 @@ export function TextTransformEdit( props ) {
);
}
diff --git a/packages/block-editor/src/hooks/typography.scss b/packages/block-editor/src/hooks/typography.scss
index 7d7d44c24b4e2..ea939116dfba3 100644
--- a/packages/block-editor/src/hooks/typography.scss
+++ b/packages/block-editor/src/hooks/typography.scss
@@ -1,10 +1,4 @@
.typography-block-support-panel {
- .components-font-size-picker__controls,
- .block-editor-text-decoration-control__buttons,
- .block-editor-text-transform-control__buttons {
- margin-bottom: 0;
- }
-
.single-column {
grid-column: span 1;
}
diff --git a/packages/block-editor/src/hooks/use-typography-props.js b/packages/block-editor/src/hooks/use-typography-props.js
new file mode 100644
index 0000000000000..d08105d8d90c1
--- /dev/null
+++ b/packages/block-editor/src/hooks/use-typography-props.js
@@ -0,0 +1,41 @@
+/**
+ * External dependencies
+ */
+import { kebabCase } from 'lodash';
+import classnames from 'classnames';
+
+/**
+ * Internal dependencies
+ */
+import { getInlineStyles } from './style';
+import { getFontSizeClass } from '../components/font-sizes';
+
+// This utility is intended to assist where the serialization of the typography
+// block support is being skipped for a block but the typography related CSS
+// styles still need to be generated so they can be applied to inner elements.
+
+/**
+ * Provides the CSS class names and inline styles for a block's typography support
+ * attributes.
+ *
+ * @param {Object} attributes Block attributes.
+ *
+ * @return {Object} Typography block support derived CSS classes & styles.
+ */
+export function getTypographyClassesAndStyles( attributes ) {
+ const typographyStyles = attributes?.style?.typography || {};
+ const style = getInlineStyles( { typography: typographyStyles } );
+ const fontFamilyClassName = !! attributes?.fontFamily
+ ? `has-${ kebabCase( attributes.fontFamily ) }-font-family`
+ : '';
+
+ const className = classnames(
+ fontFamilyClassName,
+ getFontSizeClass( attributes?.fontSize )
+ );
+
+ return {
+ className,
+ style,
+ };
+}
diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js
index 6b0c9be6e7084..8d638e997d828 100644
--- a/packages/block-editor/src/hooks/utils.js
+++ b/packages/block-editor/src/hooks/utils.js
@@ -1,16 +1,7 @@
/**
* External dependencies
*/
-import {
- pickBy,
- isEmpty,
- mapValues,
- forEach,
- get,
- setWith,
- clone,
- every,
-} from 'lodash';
+import { pickBy, isEmpty, mapValues, get, setWith, clone, every } from 'lodash';
/**
* WordPress dependencies
@@ -77,7 +68,7 @@ export function transformStyles(
}
}
let returnBlock = result;
- forEach( activeSupports, ( isActive, support ) => {
+ Object.entries( activeSupports ).forEach( ( [ support, isActive ] ) => {
if ( isActive ) {
migrationPaths[ support ].forEach( ( path ) => {
const styleValue = get( referenceBlockAttributes, path );
diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js
index c3d55ce8962c7..ec0f20a8f9c89 100644
--- a/packages/block-editor/src/index.js
+++ b/packages/block-editor/src/index.js
@@ -6,6 +6,7 @@ export {
getBorderClassesAndStyles as __experimentalGetBorderClassesAndStyles,
useBorderProps as __experimentalUseBorderProps,
getColorClassesAndStyles as __experimentalGetColorClassesAndStyles,
+ getTypographyClassesAndStyles,
useColorProps as __experimentalUseColorProps,
useCustomSides as __experimentalUseCustomSides,
getSpacingClassesAndStyles as __experimentalGetSpacingClassesAndStyles,
diff --git a/packages/block-editor/src/layouts/constrained.js b/packages/block-editor/src/layouts/constrained.js
index 8517382defcf8..0922ee3398a45 100644
--- a/packages/block-editor/src/layouts/constrained.js
+++ b/packages/block-editor/src/layouts/constrained.js
@@ -2,12 +2,20 @@
* WordPress dependencies
*/
import {
- Button,
__experimentalUseCustomUnits as useCustomUnits,
__experimentalUnitControl as UnitControl,
+ __experimentalToggleGroupControl as ToggleGroupControl,
+ __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
-import { Icon, positionCenter, stretchWide } from '@wordpress/icons';
+import {
+ Icon,
+ positionCenter,
+ stretchWide,
+ justifyLeft,
+ justifyCenter,
+ justifyRight,
+} from '@wordpress/icons';
import { getCSSRules } from '@wordpress/style-engine';
/**
@@ -15,7 +23,7 @@ import { getCSSRules } from '@wordpress/style-engine';
*/
import useSetting from '../components/use-setting';
import { appendSelectors, getBlockGapCSS, getAlignmentsInfo } from './utils';
-import { getGapBoxControlValueFromStyle } from '../hooks/gap';
+import { getGapCSSValue } from '../hooks/gap';
import { shouldSkipSerialization } from '../hooks/utils';
export default {
@@ -25,7 +33,30 @@ export default {
layout,
onChange,
} ) {
- const { wideSize, contentSize } = layout;
+ const { wideSize, contentSize, justifyContent = 'center' } = layout;
+ const onJustificationChange = ( value ) => {
+ onChange( {
+ ...layout,
+ justifyContent: value,
+ } );
+ };
+ const justificationOptions = [
+ {
+ value: 'left',
+ icon: justifyLeft,
+ label: __( 'Justify items left' ),
+ },
+ {
+ value: 'center',
+ icon: justifyCenter,
+ label: __( 'Justify items center' ),
+ },
+ {
+ value: 'right',
+ icon: justifyRight,
+ label: __( 'Justify items right' ),
+ },
+ ];
const units = useCustomUnits( {
availableUnits: useSetting( 'spacing.units' ) || [
'%',
@@ -35,7 +66,6 @@ export default {
'vw',
],
} );
-
return (
<>