diff --git a/.eslintrc.js b/.eslintrc.js
index 484387a285e78b..caf01b7cbd71d4 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -330,6 +330,7 @@ module.exports = {
'SelectControl',
'TextControl',
'ToggleGroupControl',
+ 'UnitControl',
].map( ( componentName ) => ( {
// Falsy `__next40pxDefaultSize` without a non-default `size` prop.
selector: `JSXOpeningElement[name.name="${ componentName }"]:not(:has(JSXAttribute[name.name="__next40pxDefaultSize"][value.expression.value!=false])):not(:has(JSXAttribute[name.name="size"][value.value!="default"]))`,
@@ -345,7 +346,7 @@ module.exports = {
'FormFileUpload should have the `__next40pxDefaultSize` prop to opt-in to the new default size.',
},
// Temporary rules until all existing components have the `__next40pxDefaultSize` prop.
- ...[ 'Button', 'UnitControl' ].map( ( componentName ) => ( {
+ ...[ 'Button' ].map( ( componentName ) => ( {
// Not strict. Allows pre-existing __next40pxDefaultSize={ false } usage until they are all manually updated.
selector: `JSXOpeningElement[name.name="${ componentName }"]:not(:has(JSXAttribute[name.name="__next40pxDefaultSize"])):not(:has(JSXAttribute[name.name="size"]))`,
message:
diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml
index 6106eee492c32a..499a2c020255cb 100644
--- a/.github/workflows/bundle-size.yml
+++ b/.github/workflows/bundle-size.yml
@@ -52,5 +52,5 @@ jobs:
- uses: preactjs/compressed-size-action@f780fd104362cfce9e118f9198df2ee37d12946c # v2.6.0
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
- pattern: '{build/**/*.min.js,build/**/*.css}'
+ pattern: '{build/**/*.min.js,build/**/*.css,build-module/**/*.min.js}'
clean-script: 'distclean'
diff --git a/backport-changelog/6.7/7258.md b/backport-changelog/6.7/7258.md
new file mode 100644
index 00000000000000..6714b13b70b8d2
--- /dev/null
+++ b/backport-changelog/6.7/7258.md
@@ -0,0 +1,3 @@
+https://github.com/WordPress/wordpress-develop/pull/7258
+
+* https://github.com/WordPress/gutenberg/pull/64570
\ No newline at end of file
diff --git a/bin/build-plugin-zip.sh b/bin/build-plugin-zip.sh
index 4ba931c4a4aeb6..ad627e05f0c693 100755
--- a/bin/build-plugin-zip.sh
+++ b/bin/build-plugin-zip.sh
@@ -98,6 +98,7 @@ zip -r gutenberg.zip \
packages/block-serialization-default-parser/*.php \
post-content.php \
$build_files \
+ build-module \
readme.txt \
changelog.txt \
README.md
diff --git a/changelog.txt b/changelog.txt
index 0fb2b93056b9bc..dc0a2487f725ff 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,361 @@
== Changelog ==
+= 19.2.0-rc.1 =
+
+
+## Changelog
+
+### Enhancements
+
+- Add: Reorder control at the field level on the new view configuration UI. ([64381](https://github.com/WordPress/gutenberg/pull/64381))
+- Core Data Types: `recordId` can be a number. ([64796](https://github.com/WordPress/gutenberg/pull/64796))
+- Core Data: Derive collection totals for unbound queries. ([64772](https://github.com/WordPress/gutenberg/pull/64772))
+- Create Block: Set minimum supported WordPress version to 6.6. ([64920](https://github.com/WordPress/gutenberg/pull/64920))
+- Dataviews Filter search widget: Do not use Composite store. ([64985](https://github.com/WordPress/gutenberg/pull/64985))
+- Dataviews list view: Do not use Composite store. ([64987](https://github.com/WordPress/gutenberg/pull/64987))
+- Move bulk actions menu to the Footer, consolidate with floating toolbar and total items display. ([64268](https://github.com/WordPress/gutenberg/pull/64268))
+- Try: Update block warnings. ([64997](https://github.com/WordPress/gutenberg/pull/64997))
+
+#### Components
+- Add variants to InputControl prefix/suffix wrappers. ([64824](https://github.com/WordPress/gutenberg/pull/64824))
+- AlignmentMatrixControl: Do not use Composite store. ([64850](https://github.com/WordPress/gutenberg/pull/64850))
+- CircularOptionPicker: Stop using composite store. ([64833](https://github.com/WordPress/gutenberg/pull/64833))
+- Composite: Accept store props on top level component. ([64832](https://github.com/WordPress/gutenberg/pull/64832))
+- DataViews: Adds two new stories for edge cases. ([64975](https://github.com/WordPress/gutenberg/pull/64975))
+- Decrease standard padding to 12px. ([64708](https://github.com/WordPress/gutenberg/pull/64708))
+- DropdownMenuV2: Add GroupLabel subcomponent. ([64854](https://github.com/WordPress/gutenberg/pull/64854))
+- DropdownMenuV2: Update animation. ([64868](https://github.com/WordPress/gutenberg/pull/64868))
+- DropdownMenuV2: Use overloaded naming conventions. ([64654](https://github.com/WordPress/gutenberg/pull/64654))
+- InputControl: Tighten gap between input and prefix/suffix. ([64908](https://github.com/WordPress/gutenberg/pull/64908))
+- Navigator: Polish Storybook examples. ([64798](https://github.com/WordPress/gutenberg/pull/64798))
+- Navigator: Remove location history, simplify internal logic. ([64675](https://github.com/WordPress/gutenberg/pull/64675))
+- UnitControl: Update unit select styles. ([64712](https://github.com/WordPress/gutenberg/pull/64712))
+- Update hard-coded border-radius instances. ([64693](https://github.com/WordPress/gutenberg/pull/64693))
+- Update modal animation. ([64580](https://github.com/WordPress/gutenberg/pull/64580))
+
+#### Block bindings
+- Add warning in attributes connected to invalid sources. ([65002](https://github.com/WordPress/gutenberg/pull/65002))
+- Allow only admin users to create and modify bindings by default. ([64570](https://github.com/WordPress/gutenberg/pull/64570))
+- Lock editing in fields in editor if meta fields panel is opened. ([64738](https://github.com/WordPress/gutenberg/pull/64738))
+- Rely on `Text` component instead of `Truncate` in bindings panel. ([65007](https://github.com/WordPress/gutenberg/pull/65007))
+- Remove `getPlaceholder` API and rely on `key` argument or source label. ([64910](https://github.com/WordPress/gutenberg/pull/64910))
+
+#### Block Editor
+- Add 'Reset' option to MediaReplaceFlow component. ([64826](https://github.com/WordPress/gutenberg/pull/64826))
+- Block Patterns List: Do not use Composite store. ([64983](https://github.com/WordPress/gutenberg/pull/64983))
+- Remove the Shuffle block toolbar button. ([64954](https://github.com/WordPress/gutenberg/pull/64954))
+- Show block icon in contentOnly toolbar. ([64694](https://github.com/WordPress/gutenberg/pull/64694))
+
+#### Block Library
+- Cover Block: Move Clear Media button from Inspector Controls to Block Controls. ([64630](https://github.com/WordPress/gutenberg/pull/64630))
+- Improve Social Icons setup and appending. ([64877](https://github.com/WordPress/gutenberg/pull/64877))
+- Pagination Block: Fix inconsistent margins between editor and frontend. ([64874](https://github.com/WordPress/gutenberg/pull/64874))
+- Tag Cloud: Improve state of block with no tags. ([63774](https://github.com/WordPress/gutenberg/pull/63774))
+
+#### Block Locking
+- ContentOnly: Add support for block styles on top-level contentOnly locked blocks. ([64872](https://github.com/WordPress/gutenberg/pull/64872))
+- Only show title in content only toolbar if has title value. ([64840](https://github.com/WordPress/gutenberg/pull/64840))
+- Remove ability to crop image if content only mode. ([64838](https://github.com/WordPress/gutenberg/pull/64838))
+- Rename Alt to Alternative Text in content only image toolbar. ([64841](https://github.com/WordPress/gutenberg/pull/64841))
+
+#### Interactivity API
+- Categories Block: Add iAPI directive for client-side routing. ([64907](https://github.com/WordPress/gutenberg/pull/64907))
+- Improve internal `deepMerge` function. ([64879](https://github.com/WordPress/gutenberg/pull/64879))
+
+#### Global Styles
+- Hide typeset button when there are no typesets available. ([64515](https://github.com/WordPress/gutenberg/pull/64515))
+- Use four color palette colors instead of five for useStylesPreviewColors. ([64700](https://github.com/WordPress/gutenberg/pull/64700))
+
+#### Zoom Out
+- Add "Edit" button to Zoom Out mode toolbar. ([64571](https://github.com/WordPress/gutenberg/pull/64571))
+- Double click block to exit zoom out mode. ([64573](https://github.com/WordPress/gutenberg/pull/64573))
+
+#### Design Tools
+- Comment Edit Link: Add Border Block Support. ([64239](https://github.com/WordPress/gutenberg/pull/64239))
+- Comment Reply Link: Add border support. ([64271](https://github.com/WordPress/gutenberg/pull/64271))
+
+#### Icons
+- Add thumbs up and down icons. ([65004](https://github.com/WordPress/gutenberg/pull/65004))
+
+#### Site Editor
+- Apply radius scale in the editor. ([64930](https://github.com/WordPress/gutenberg/pull/64930))
+
+#### Post Editor
+- Post publish upload media dialog: Handle upload errors. ([64823](https://github.com/WordPress/gutenberg/pull/64823))
+
+#### Typography
+- Fluid typography: Allow individual preset overrides. ([64790](https://github.com/WordPress/gutenberg/pull/64790))
+
+#### Media
+- Add experiment for client-side media processing. ([64650](https://github.com/WordPress/gutenberg/pull/64650))
+
+#### REST API
+- Core Data: Resolve entity collection user permissions. ([64504](https://github.com/WordPress/gutenberg/pull/64504))
+
+#### Block Transforms
+- Details block: Add transform from any block type. ([63422](https://github.com/WordPress/gutenberg/pull/63422))
+
+
+### New APIs
+
+#### Extensibility
+- Editor: Add extensibility to PreviewOptions v2. ([64644](https://github.com/WordPress/gutenberg/pull/64644))
+
+
+### Bug Fixes
+
+- Add safeguard to `mediaUploadMiddleware`. ([64843](https://github.com/WordPress/gutenberg/pull/64843))
+- Allow multi-select on iOS Safari/touch devices. ([63671](https://github.com/WordPress/gutenberg/pull/63671))
+- Fix Modify content-locked menu item not showing if the block is not selected. ([61605](https://github.com/WordPress/gutenberg/pull/61605))
+- Fix editor error in Safari due to availability of checkVisibility method. ([65069](https://github.com/WordPress/gutenberg/pull/65069))
+- Fix: Pagination arrows are pointing in the wrong direction in RTL languages. ([64962](https://github.com/WordPress/gutenberg/pull/64962))
+- Footnotes: Only replace attribute if footnotes were detected. ([63935](https://github.com/WordPress/gutenberg/pull/63935))
+- Paste: Fix image paste from Google Forms. ([64502](https://github.com/WordPress/gutenberg/pull/64502))
+- Revert Focus pattern inserter search when activating zoom out inserter. ([64748](https://github.com/WordPress/gutenberg/pull/64748))
+
+#### Block Library
+- De-duplicate block toolbar icons for patterns. ([65054](https://github.com/WordPress/gutenberg/pull/65054))
+- Fix: Page list: Pages without a title has no link text. ([64297](https://github.com/WordPress/gutenberg/pull/64297))
+- Position BlockToolbar below all of the selected block's descendants. ([62711](https://github.com/WordPress/gutenberg/pull/62711))
+- Site Logo Block: Fix non-admin users seeing zero character. ([65010](https://github.com/WordPress/gutenberg/pull/65010))
+- Site Logo: Fix loader alignment issue. ([64919](https://github.com/WordPress/gutenberg/pull/64919))
+- Template Part: Hide Advanced panel for non-admin users. ([64721](https://github.com/WordPress/gutenberg/pull/64721))
+- Video Block: Fix layout issue. ([64834](https://github.com/WordPress/gutenberg/pull/64834))
+
+#### Components
+- ColorPalette utils: Do not normalize undefined color values. ([64969](https://github.com/WordPress/gutenberg/pull/64969))
+- DatePicker: Restore round radius for event dot. ([65031](https://github.com/WordPress/gutenberg/pull/65031))
+- DropdownMenuV2: Fix active and focus-visible item glitches. ([64942](https://github.com/WordPress/gutenberg/pull/64942))
+- DropdownMenuV2: Remove flashing styles when moving focus with keyboard. ([64873](https://github.com/WordPress/gutenberg/pull/64873))
+- Fixes "delete" action in DataViews' storybook. ([64901](https://github.com/WordPress/gutenberg/pull/64901))
+- Navigator: Fix isInitial, refine focusSelector logic. ([64786](https://github.com/WordPress/gutenberg/pull/64786))
+- Range control: Restore bottom margin rule. ([65035](https://github.com/WordPress/gutenberg/pull/65035))
+
+#### Post Editor
+- Add back editor-post-locked-modal to post lock component. ([64257](https://github.com/WordPress/gutenberg/pull/64257))
+- Add context to `View` string in post actions. ([65046](https://github.com/WordPress/gutenberg/pull/65046))
+- Apply space below content using a pseudo-element instead of padding-bottom. ([64639](https://github.com/WordPress/gutenberg/pull/64639))
+- Post Title: Fix pasting in Safari. ([64671](https://github.com/WordPress/gutenberg/pull/64671))
+- Post Title: Move selection at the end after pasting over the text. ([64665](https://github.com/WordPress/gutenberg/pull/64665))
+- Post publish upload media dialog: Fix silent failure. ([64741](https://github.com/WordPress/gutenberg/pull/64741))
+
+#### Data Views
+- DataViews: Fix field reordering and visibility logic. ([64999](https://github.com/WordPress/gutenberg/pull/64999))
+- Fix actions scrim in list layout. ([64696](https://github.com/WordPress/gutenberg/pull/64696))
+- Fix data views style inheritance. ([64933](https://github.com/WordPress/gutenberg/pull/64933))
+- Fix: Impossible to see pagination on viewports between small and medium. ([64844](https://github.com/WordPress/gutenberg/pull/64844))
+- List layout: Update broken styles. ([64837](https://github.com/WordPress/gutenberg/pull/64837))
+
+#### Block Editor
+- Add conditions when the Shuffle button can be displayed. ([64888](https://github.com/WordPress/gutenberg/pull/64888))
+- Inserter: Fix subtle media insertion error. ([65057](https://github.com/WordPress/gutenberg/pull/65057))
+- Post Editor: Fix click space after post content to append. ([64992](https://github.com/WordPress/gutenberg/pull/64992))
+- Writing flow: Fix triple click inside text blocks. ([64928](https://github.com/WordPress/gutenberg/pull/64928))
+
+#### Global Styles
+- Adjust spacing of background panel. ([64880](https://github.com/WordPress/gutenberg/pull/64880))
+- Cast globalFluid value to boolean. ([64882](https://github.com/WordPress/gutenberg/pull/64882))
+- Fix site editor broken when fontWeight is not defined or is an integer in theme.json or theme styles. ([64953](https://github.com/WordPress/gutenberg/pull/64953))
+- Fixes the default fluid value on the UI based on the global typography fluid value. ([64803](https://github.com/WordPress/gutenberg/pull/64803))
+
+#### Block bindings
+- Change placeholder when attribute is bound. ([64903](https://github.com/WordPress/gutenberg/pull/64903))
+- Fix empty custom fields not being editable in bindings. ([64881](https://github.com/WordPress/gutenberg/pull/64881))
+
+#### CSS & Styling
+- Featured Image Block: Reduce CSS specificity. ([64463](https://github.com/WordPress/gutenberg/pull/64463))
+- Retain the same specificity for non iframed selectors. ([64534](https://github.com/WordPress/gutenberg/pull/64534))
+
+#### Patterns
+- Pass 'blocks' as inner blocks value. ([65029](https://github.com/WordPress/gutenberg/pull/65029))
+
+#### Synced Patterns
+- Pattern: Don't render block controls when an entity is missing. ([65028](https://github.com/WordPress/gutenberg/pull/65028))
+
+#### Site Editor
+- DataViews: Fix pattern title direction in RTL languages. ([64967](https://github.com/WordPress/gutenberg/pull/64967))
+
+#### NUX
+- Fix visibility of the template Welcome Guide in the Site Editor. ([64789](https://github.com/WordPress/gutenberg/pull/64789))
+
+#### Document Settings
+- Fix: Adjust Site URL Styles to Prevent Overflow in Pre-Publish Component. ([64745](https://github.com/WordPress/gutenberg/pull/64745))
+
+#### Zoom Out
+- Focus selected block in editor canvas when clicking edit button on zoom out mode toolbar. ([64725](https://github.com/WordPress/gutenberg/pull/64725))
+
+#### Templates API
+- Make plugin-registered templates overriden by themes to fall back to plugin-registered title and description. ([64610](https://github.com/WordPress/gutenberg/pull/64610))
+
+#### Block Style Variations
+- Block Styles: Ensure unique classname generation for variations. ([64511](https://github.com/WordPress/gutenberg/pull/64511))
+
+#### Distraction Free
+- Make Distraction Free not conditional on viewport width. ([63949](https://github.com/WordPress/gutenberg/pull/63949))
+
+#### Media
+- Limit the max width of image to its container size. ([63341](https://github.com/WordPress/gutenberg/pull/63341))
+
+
+### Accessibility
+
+#### Components
+- AlignmentMatrixControl: Simplify styles and markup. ([64827](https://github.com/WordPress/gutenberg/pull/64827))
+- TimePicker: Use ToggleGroupControl for AM/PM toggle. ([64800](https://github.com/WordPress/gutenberg/pull/64800))
+
+#### Block Editor
+- Layout content and wide width controls: Remove confusing icon and clarify labels. ([64891](https://github.com/WordPress/gutenberg/pull/64891))
+
+#### Font Library
+- Font Library Modal: Group font variations as a list. ([64029](https://github.com/WordPress/gutenberg/pull/64029))
+
+#### Post Editor
+- Fix the post summary Status toggle button accessibility. ([63988](https://github.com/WordPress/gutenberg/pull/63988))
+
+
+### Performance
+
+- Core Data: Avoid loops in 'registry.batch' calls. ([64955](https://github.com/WordPress/gutenberg/pull/64955))
+- Core data: Performance: Fix receive user permissions. ([64894](https://github.com/WordPress/gutenberg/pull/64894))
+- Reusable blocks: Fix performance of __experimentalGetAllowedPatterns. ([64871](https://github.com/WordPress/gutenberg/pull/64871))
+
+#### Site Editor
+- Add 'OPTIONS /page' to preloaded paths. ([64890](https://github.com/WordPress/gutenberg/pull/64890))
+- Editor: Don't use selector shortcuts for the Site data. ([64884](https://github.com/WordPress/gutenberg/pull/64884))
+
+#### Block Library
+- Media & Text: Don't use background-image. ([64981](https://github.com/WordPress/gutenberg/pull/64981))
+
+#### Post Editor
+- Editor: Remove create template permission check in 'VisualEditor'. ([64905](https://github.com/WordPress/gutenberg/pull/64905))
+
+#### Block Editor
+- Inserter: Use lighter grammar parse to check allowed status. ([64902](https://github.com/WordPress/gutenberg/pull/64902))
+
+#### Patterns
+- Shuffle: Don't call '__experimentalGetAllowedPatterns' for every block. ([64736](https://github.com/WordPress/gutenberg/pull/64736))
+
+
+### Experiments
+
+#### Zoom Out
+- Add new zoom out experiment. ([65048](https://github.com/WordPress/gutenberg/pull/65048))
+- Remove the experiment that connects zoom out to the pattern inserter. ([65045](https://github.com/WordPress/gutenberg/pull/65045))
+
+
+### Documentation
+
+- Add a new section to the SlotFill reference to show how to conditionally render Fills. ([64807](https://github.com/WordPress/gutenberg/pull/64807))
+- Added Global Documentation in several php file. ([64956](https://github.com/WordPress/gutenberg/pull/64956))
+- Components: Move displayName assignment to top-level files. ([64793](https://github.com/WordPress/gutenberg/pull/64793))
+- Composite: Add context-forwarding with SlotFill example. ([65051](https://github.com/WordPress/gutenberg/pull/65051))
+- Composite: Fix Storybook docgen. ([64682](https://github.com/WordPress/gutenberg/pull/64682))
+- Corrected HTML Syntax for Closing Tags in api-reference.md file. ([64778](https://github.com/WordPress/gutenberg/pull/64778))
+- DataViews docs: Fix typo in `direction` values. ([64973](https://github.com/WordPress/gutenberg/pull/64973))
+- DataViews: Add story about combining fields. ([64984](https://github.com/WordPress/gutenberg/pull/64984))
+- DataViews: Document combined fields. ([64904](https://github.com/WordPress/gutenberg/pull/64904))
+- Dataviews docs: Layout properties checks and link. ([64918](https://github.com/WordPress/gutenberg/pull/64918))
+- Docs/iAPI: Fix wrong code snippets in API reference. ([64416](https://github.com/WordPress/gutenberg/pull/64416))
+- Docs: Update design resources to indicate edit isn't free. ([64792](https://github.com/WordPress/gutenberg/pull/64792))
+- PluginSidebarMoreMenuItem: Update example, screenshot and description. ([64761](https://github.com/WordPress/gutenberg/pull/64761))
+- Provide better examples and remove outdating site edit references for the MainDashboardButton SlotFill. ([64753](https://github.com/WordPress/gutenberg/pull/64753))
+- Removing ryanwelcher as a documentation codeowner because my inbox is dead. ([64762](https://github.com/WordPress/gutenberg/pull/64762))
+- Storybook: Hide deprecated `__next36pxDefaultSize` prop. ([64806](https://github.com/WordPress/gutenberg/pull/64806))
+- Update screenshot and description for PluginSidebar slot. ([64759](https://github.com/WordPress/gutenberg/pull/64759))
+- Update text to match code examples. ([64751](https://github.com/WordPress/gutenberg/pull/64751))
+- Update the import for PluginBlockSettingsMenuItem. ([64758](https://github.com/WordPress/gutenberg/pull/64758))
+- Updated Several Typos in Doc files. ([64787](https://github.com/WordPress/gutenberg/pull/64787))
+- [Docs]: Update Usage Example for block variation picker: Fix Import from Wrong Package. ([55555](https://github.com/WordPress/gutenberg/pull/55555))
+
+
+### Code Quality
+
+- Button: Add lint rule for 40px size prop usage. ([64835](https://github.com/WordPress/gutenberg/pull/64835))
+- Dataviews filter: Move resetValueOnSelect prop to combobox item. ([64852](https://github.com/WordPress/gutenberg/pull/64852))
+- Rename refs to fix tons of 'Mutating a value' errors in react-compiler. ([64718](https://github.com/WordPress/gutenberg/pull/64718))
+- Rich text: Add comment on placeholder approach. ([64945](https://github.com/WordPress/gutenberg/pull/64945))
+- SelectControl: Fix remaining 40px size violations. ([64831](https://github.com/WordPress/gutenberg/pull/64831))
+- Simplify useResizeObserver. ([64820](https://github.com/WordPress/gutenberg/pull/64820))
+- Typography: Backport comment changes only. ([64859](https://github.com/WordPress/gutenberg/pull/64859))
+- UnitControl: Add lint rule for 40px size prop usage. ([64520](https://github.com/WordPress/gutenberg/pull/64520))
+- UnitControl: Move to stricter lint rule for 40px size adherence. ([65017](https://github.com/WordPress/gutenberg/pull/65017))
+- Use rectIntersect instead of a custom argument to rectUnion. ([64855](https://github.com/WordPress/gutenberg/pull/64855))
+
+#### Site Editor
+- Add Custom Template modal: Do not use Composite store. ([65044](https://github.com/WordPress/gutenberg/pull/65044))
+- Add units to avoid console warning. ([64810](https://github.com/WordPress/gutenberg/pull/64810))
+- Edit Site Layout: Remove redundant fullResizer. ([64821](https://github.com/WordPress/gutenberg/pull/64821))
+- Remove unused 'useSiteEditorSettings' hook. ([64892](https://github.com/WordPress/gutenberg/pull/64892))
+- Style Book: Do not use Composite store. ([65047](https://github.com/WordPress/gutenberg/pull/65047))
+
+#### Block Editor
+- Block Inserter Listbox: Do not use Composite store. ([65042](https://github.com/WordPress/gutenberg/pull/65042))
+- Block Inserter Media List: Do not use Composite store. ([65043](https://github.com/WordPress/gutenberg/pull/65043))
+- Block Pattern Setup: Do not use Composite store. ([65039](https://github.com/WordPress/gutenberg/pull/65039))
+- Global Styles Shadow Panel: Do not use Composite store. ([65041](https://github.com/WordPress/gutenberg/pull/65041))
+- Pattern Transformations Menu: Do not use Composite store. ([65040](https://github.com/WordPress/gutenberg/pull/65040))
+
+#### Zoom Out
+- Add selector for getting section root clientId. ([65001](https://github.com/WordPress/gutenberg/pull/65001))
+- Don't pass 'rootClientId' to block lock selectors. ([64887](https://github.com/WordPress/gutenberg/pull/64887))
+- Fix error and improve privacy of sectionRootClientId setting. ([65000](https://github.com/WordPress/gutenberg/pull/65000))
+
+#### Components
+- AlignmentMatrixControl: Promote to stable. ([60913](https://github.com/WordPress/gutenberg/pull/60913))
+- Deprecate `DimensionControl`. ([64951](https://github.com/WordPress/gutenberg/pull/64951))
+
+#### Block Library
+- Block Bindings: Fix ESLint warnings. ([64684](https://github.com/WordPress/gutenberg/pull/64684))
+- Video Block: Remove custom CSS code for placeholder style. ([64861](https://github.com/WordPress/gutenberg/pull/64861))
+
+#### Global Styles
+- Allow referenced zero value and simplify getValueFromObjectPath calls. ([64836](https://github.com/WordPress/gutenberg/pull/64836))
+- Navigator: Replace deprecated NavigatorToParentButton with NavigatorBackButton. ([64775](https://github.com/WordPress/gutenberg/pull/64775))
+
+#### Block Directory
+- Downloadable Block List: Do not use composite store. ([65038](https://github.com/WordPress/gutenberg/pull/65038))
+
+#### Design Tools
+- Color panel hook: Rename to remove ambiguity. ([64993](https://github.com/WordPress/gutenberg/pull/64993))
+
+
+### Tools
+
+- Add remaining i18n rules to recommended ESLint ruleset. ([64710](https://github.com/WordPress/gutenberg/pull/64710))
+- Scripts: Added chunk filename in webpack configuration to avoid reading stale files. ([58176](https://github.com/WordPress/gutenberg/pull/58176))
+- Scripts: Import CSS files before optimization. ([61121](https://github.com/WordPress/gutenberg/pull/61121))
+- Scripts: Update `puppeteer-core` dependency. ([64597](https://github.com/WordPress/gutenberg/pull/64597))
+
+#### Testing
+- Flaky Test: Fix "Sorting" test in new-templates-list.spec.js. ([64776](https://github.com/WordPress/gutenberg/pull/64776))
+- Revert "Downgrade node 22(.5) unit tests to 22.4 (#63728)". ([63758](https://github.com/WordPress/gutenberg/pull/63758))
+
+
+### Various
+
+- Dataviews docs: Fixed property name for defaultLayouts settings. ([64897](https://github.com/WordPress/gutenberg/pull/64897))
+- task: Remove dcalhoun code owner. ([64886](https://github.com/WordPress/gutenberg/pull/64886))
+
+
+## First-time contributors
+
+The following PRs were merged by first-time contributors:
+
+- @Imran92: Fix site editor broken when fontWeight is not defined or is an integer in theme.json or theme styles. ([64953](https://github.com/WordPress/gutenberg/pull/64953))
+- @jacobcassidy: Scripts: Update `puppeteer-core` dependency. ([64597](https://github.com/WordPress/gutenberg/pull/64597))
+- @jawadmalikdev: [Docs]: Update Usage Example for block variation picker: Fix Import from Wrong Package. ([55555](https://github.com/WordPress/gutenberg/pull/55555))
+- @lezama: Editor: Add extensibility to PreviewOptions v2. ([64644](https://github.com/WordPress/gutenberg/pull/64644))
+- @rithik56: Scripts: Added chunk filename in webpack configuration to avoid reading stale files. ([58176](https://github.com/WordPress/gutenberg/pull/58176))
+- @rohitmathur-7: Cover Block: Move Clear Media button from Inspector Controls to Block Controls. ([64630](https://github.com/WordPress/gutenberg/pull/64630))
+
+
+## Contributors
+
+The following contributors merged PRs in this release:
+
+@aaronrobertshaw @afercia @akasunil @Aljullu @andrewserong @atachibana @benoitchantre @carolinan @cbravobernal @ciampo @DAreRodz @dcalhoun @desrosj @dsas @ellatrix @fullofcaffeine @getdave @gziolo @Imran92 @imrraaj @jacobcassidy @jameskoster @jasmussen @jawadmalikdev @jeryj @jorgefilipecosta @jsnajdr @juanmaguitar @kevin940726 @lezama @Mamaduka @matiasbenedetto @mirka @noisysocks @ntsekouras @oandregal @ockham @ramonjd @richtabor @rithik56 @rohitmathur-7 @ryanwelcher @SantosGuillamot @scruffian @sgomes @shail-mehta @spacedmonkey @stokesman @swissspidy @t-hamano @talldan @tjcafferkey @tyxla
+
+
= 19.1.0 =
## Changelog
diff --git a/docs/manifest.json b/docs/manifest.json
index e4eba19d99fa29..d7f74d47995b63 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -1697,6 +1697,12 @@
"markdown_source": "../packages/eslint-plugin/README.md",
"parent": "packages"
},
+ {
+ "title": "@wordpress/fields",
+ "slug": "packages-fields",
+ "markdown_source": "../packages/fields/README.md",
+ "parent": "packages"
+ },
{
"title": "@wordpress/format-library",
"slug": "packages-format-library",
diff --git a/gutenberg.php b/gutenberg.php
index f0e2f94ef57fd6..117f4168524d8a 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -5,7 +5,7 @@
* Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality.
* Requires at least: 6.5
* Requires PHP: 7.2
- * Version: 19.1.0
+ * Version: 19.2.0-rc.1
* Author: Gutenberg Team
* Text Domain: gutenberg
*
diff --git a/lib/compat/wordpress-6.7/block-bindings.php b/lib/compat/wordpress-6.7/block-bindings.php
index 398b53b340673b..9e82c1843f35a0 100644
--- a/lib/compat/wordpress-6.7/block-bindings.php
+++ b/lib/compat/wordpress-6.7/block-bindings.php
@@ -38,3 +38,18 @@ function gutenberg_add_server_block_bindings_sources_to_editor_settings( $editor
}
add_filter( 'block_editor_settings_all', 'gutenberg_add_server_block_bindings_sources_to_editor_settings', 10 );
+
+/**
+ * Initialize `canUpdateBlockBindings` editor setting if it doesn't exist. By default, it is `true` only for admin users.
+ *
+ * @param array $settings The block editor settings from the `block_editor_settings_all` filter.
+ * @return array The editor settings including `canUpdateBlockBindings`.
+ */
+function gutenberg_add_can_update_block_bindings_editor_setting( $editor_settings ) {
+ if ( empty( $editor_settings['canUpdateBlockBindings'] ) ) {
+ $editor_settings['canUpdateBlockBindings'] = current_user_can( 'manage_options' );
+ }
+ return $editor_settings;
+}
+
+add_filter( 'block_editor_settings_all', 'gutenberg_add_can_update_block_bindings_editor_setting', 10 );
diff --git a/lib/compat/wordpress-6.7/block-templates.php b/lib/compat/wordpress-6.7/block-templates.php
index e270ab226c1d9f..d1f2859070b8b4 100644
--- a/lib/compat/wordpress-6.7/block-templates.php
+++ b/lib/compat/wordpress-6.7/block-templates.php
@@ -10,7 +10,6 @@
* Register a template.
*
* @param string $template_name Template name in the form of `plugin_uri//template_name`.
- * @param array|string $args Object type or array of object types with which the taxonomy should be associated.
* @param array|string $args {
* @type string $title Optional. Title of the template as it will be shown in the Site Editor
* and other UI elements.
@@ -33,7 +32,8 @@ function wp_register_block_template( $template_name, $args = array() ) {
* Unregister a template.
*
* @param string $template_name Template name in the form of `plugin_uri//template_name`.
- * @return true|WP_Error True on success, WP_Error on failure or if the template doesn't exist.
+ * @return WP_Block_Template|WP_Error The unregistered template object on success, WP_Error object on failure or if
+ * the template doesn't exist.
*/
function wp_unregister_block_template( $template_name ) {
return WP_Block_Templates_Registry::get_instance()->unregister( $template_name );
diff --git a/lib/compat/wordpress-6.7/class-wp-block-templates-registry.php b/lib/compat/wordpress-6.7/class-wp-block-templates-registry.php
index db53f735e13b3d..92673c0bf50f90 100644
--- a/lib/compat/wordpress-6.7/class-wp-block-templates-registry.php
+++ b/lib/compat/wordpress-6.7/class-wp-block-templates-registry.php
@@ -36,7 +36,7 @@ final class WP_Block_Templates_Registry {
*
* @param string $template_name Template name including namespace.
* @param array $args Optional. Array of template arguments.
- * @return WP_Block_Template|WP_Error The registered template on success, or false on failure.
+ * @return WP_Block_Template|WP_Error The registered template on success, or WP_Error on failure.
*/
public function register( $template_name, $args = array() ) {
@@ -100,7 +100,7 @@ public function register( $template_name, $args = array() ) {
*
* @since 6.7.0
*
- * @return WP_Block_Template[]|false Associative array of `$template_name => $template` pairs.
+ * @return WP_Block_Template[] Associative array of `$template_name => $template` pairs.
*/
public function get_all_registered() {
return $this->registered_templates;
@@ -112,7 +112,7 @@ public function get_all_registered() {
* @since 6.7.0
*
* @param string $template_name Template name including namespace.
- * @return WP_Block_Template|null|false The registered template, or null if it is not registered.
+ * @return WP_Block_Template|null The registered template, or null if it is not registered.
*/
public function get_registered( $template_name ) {
if ( ! $this->is_registered( $template_name ) ) {
@@ -216,7 +216,7 @@ public function is_registered( $template_name ) {
* @since 6.7.0
*
* @param string $template_name Template name including namespace.
- * @return WP_Block_Template|false The unregistered template on success, or false on failure.
+ * @return WP_Block_Template|WP_Error The unregistered template on success, or WP_Error on failure.
*/
public function unregister( $template_name ) {
if ( ! $this->is_registered( $template_name ) ) {
diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php
index c6bd99a18bf4c7..919be2e6e34a45 100644
--- a/lib/experimental/editor-settings.php
+++ b/lib/experimental/editor-settings.php
@@ -28,18 +28,15 @@ function gutenberg_enable_experiments() {
if ( gutenberg_is_experiment_enabled( 'gutenberg-full-page-client-side-navigation' ) ) {
wp_add_inline_script( 'wp-block-library', 'window.__experimentalFullPageClientSideNavigation = true', 'before' );
}
- if ( $gutenberg_experiments && array_key_exists( 'gutenberg-zoomed-out-patterns-tab', $gutenberg_experiments ) ) {
- wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableZoomedOutPatternsTab = true', 'before' );
- }
if ( $gutenberg_experiments && array_key_exists( 'gutenberg-quick-edit-dataviews', $gutenberg_experiments ) ) {
wp_add_inline_script( 'wp-block-editor', 'window.__experimentalQuickEditDataViews = true', 'before' );
}
- if ( $gutenberg_experiments && array_key_exists( 'gutenberg-block-bindings-ui', $gutenberg_experiments ) ) {
- wp_add_inline_script( 'wp-block-editor', 'window.__experimentalBlockBindingsUI = true', 'before' );
- }
if ( $gutenberg_experiments && array_key_exists( 'gutenberg-media-processing', $gutenberg_experiments ) ) {
wp_add_inline_script( 'wp-block-editor', 'window.__experimentalMediaProcessing = true', 'before' );
}
+ if ( $gutenberg_experiments && array_key_exists( 'gutenberg-zoom-out-experiment', $gutenberg_experiments ) ) {
+ wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableZoomOutExperiment = true', 'before' );
+ }
}
add_action( 'admin_init', 'gutenberg_enable_experiments' );
diff --git a/lib/experiments-page.php b/lib/experiments-page.php
index f76dcdca7d18cb..5acd5f0f192364 100644
--- a/lib/experiments-page.php
+++ b/lib/experiments-page.php
@@ -139,18 +139,6 @@ function gutenberg_initialize_experiments_settings() {
)
);
- add_settings_field(
- 'gutenberg-zoomed-out-patterns-tab',
- __( 'Enable zoomed out view when patterns are browsed in the inserter', 'gutenberg' ),
- 'gutenberg_display_experiment_field',
- 'gutenberg-experiments',
- 'gutenberg_experiments_section',
- array(
- 'label' => __( 'Enable zoomed out view when selecting a pattern category in the main inserter.', 'gutenberg' ),
- 'id' => 'gutenberg-zoomed-out-patterns-tab',
- )
- );
-
add_settings_field(
'gutenberg-new-posts-dashboard',
__( 'Redesigned posts dashboard', 'gutenberg' ),
@@ -176,29 +164,28 @@ function gutenberg_initialize_experiments_settings() {
);
add_settings_field(
- 'gutenberg-block-bindings-ui',
- __( 'UI to create block bindings', 'gutenberg' ),
+ 'gutenberg-media-processing',
+ __( 'Client-side media processing', 'gutenberg' ),
'gutenberg_display_experiment_field',
'gutenberg-experiments',
'gutenberg_experiments_section',
array(
- 'label' => __( 'Add UI to create and update block bindings in block inspector controls.', 'gutenberg' ),
- 'id' => 'gutenberg-block-bindings-ui',
+ 'label' => __( 'Enable client-side media processing.', 'gutenberg' ),
+ 'id' => 'gutenberg-media-processing',
)
);
add_settings_field(
- 'gutenberg-media-processing',
- __( 'Client-side media processing', 'gutenberg' ),
+ 'gutenberg-zoom-out-experiment',
+ __( 'Zoom out experiments', 'gutenberg' ),
'gutenberg_display_experiment_field',
'gutenberg-experiments',
'gutenberg_experiments_section',
array(
- 'label' => __( 'Enable client-side media processing.', 'gutenberg' ),
- 'id' => 'gutenberg-media-processing',
+ 'label' => __( 'Enable zoom out experiments; shows zoom out in the device preview and other zoom out experiments.', 'gutenberg' ),
+ 'id' => 'gutenberg-zoom-out-experiment',
)
);
-
register_setting(
'gutenberg-experiments',
'gutenberg-experiments'
diff --git a/lib/interactivity-api.php b/lib/interactivity-api.php
index 6f04a3ba8fc927..90535f1ebaa42f 100644
--- a/lib/interactivity-api.php
+++ b/lib/interactivity-api.php
@@ -16,14 +16,14 @@ function gutenberg_reregister_interactivity_script_modules() {
wp_register_script_module(
'@wordpress/interactivity',
- gutenberg_url( '/build/interactivity/' . ( SCRIPT_DEBUG ? 'debug.min.js' : 'index.min.js' ) ),
+ gutenberg_url( '/build-module/' . ( SCRIPT_DEBUG ? 'interactivity/debug.min.js' : 'interactivity/index.min.js' ) ),
array(),
$default_version
);
wp_register_script_module(
'@wordpress/interactivity-router',
- gutenberg_url( '/build/interactivity/router.min.js' ),
+ gutenberg_url( '/build-module/interactivity-router/index.min.js' ),
array( '@wordpress/interactivity' ),
$default_version
);
diff --git a/package-lock.json b/package-lock.json
index a5841a06e163ed..5f393e700c2892 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "gutenberg",
- "version": "19.1.0",
+ "version": "19.2.0-rc.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "gutenberg",
- "version": "19.1.0",
+ "version": "19.2.0-rc.1",
"hasInstallScript": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -40,12 +40,14 @@
"@wordpress/editor": "file:packages/editor",
"@wordpress/element": "file:packages/element",
"@wordpress/escape-html": "file:packages/escape-html",
+ "@wordpress/fields": "file:packages/fields",
"@wordpress/format-library": "file:packages/format-library",
"@wordpress/hooks": "file:packages/hooks",
"@wordpress/html-entities": "file:packages/html-entities",
"@wordpress/i18n": "file:packages/i18n",
"@wordpress/icons": "file:packages/icons",
"@wordpress/interactivity": "file:packages/interactivity",
+ "@wordpress/interactivity-router": "file:packages/interactivity-router",
"@wordpress/interface": "file:packages/interface",
"@wordpress/is-shallow-equal": "file:packages/is-shallow-equal",
"@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts",
@@ -79,8 +81,7 @@
"@wordpress/viewport": "file:packages/viewport",
"@wordpress/warning": "file:packages/warning",
"@wordpress/widgets": "file:packages/widgets",
- "@wordpress/wordcount": "file:packages/wordcount",
- "es-module-shims": "^1.8.2"
+ "@wordpress/wordcount": "file:packages/wordcount"
},
"devDependencies": {
"@actions/core": "1.9.1",
@@ -99,7 +100,7 @@
"@octokit/rest": "16.26.0",
"@octokit/types": "6.34.0",
"@octokit/webhooks-types": "5.8.0",
- "@playwright/test": "1.46.0",
+ "@playwright/test": "1.47.0",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
"@react-native/babel-preset": "0.73.10",
"@react-native/metro-babel-transformer": "0.73.10",
@@ -221,6 +222,7 @@
"postcss-local-keyframes": "^0.0.2",
"prettier": "npm:wp-prettier@3.0.3",
"progress": "2.0.3",
+ "puppeteer-core": "23.1.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.73.3",
@@ -6949,12 +6951,12 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz",
- "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.0.tgz",
+ "integrity": "sha512-SgAdlSwYVpToI4e/IH19IHHWvoijAYH5hu2MWSXptRypLSnzj51PcGD+rsOXFayde4P9ZLi+loXVwArg6IUkCA==",
"dev": true,
"dependencies": {
- "playwright": "1.46.0"
+ "playwright": "1.47.0"
},
"bin": {
"playwright": "cli.js"
@@ -17028,6 +17030,10 @@
"resolved": "packages/eslint-plugin",
"link": true
},
+ "node_modules/@wordpress/fields": {
+ "resolved": "packages/fields",
+ "link": true
+ },
"node_modules/@wordpress/format-library": {
"resolved": "packages/format-library",
"link": true
@@ -22569,15 +22575,6 @@
"node": ">=4.0"
}
},
- "node_modules/cross-fetch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
- "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
- "dev": true,
- "dependencies": {
- "node-fetch": "2.6.7"
- }
- },
"node_modules/cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -24111,10 +24108,11 @@
}
},
"node_modules/devtools-protocol": {
- "version": "0.0.981744",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz",
- "integrity": "sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==",
- "dev": true
+ "version": "0.0.1312386",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz",
+ "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==",
+ "dev": true,
+ "license": "BSD-3-Clause"
},
"node_modules/diff": {
"version": "4.0.2",
@@ -24850,11 +24848,6 @@
"integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==",
"dev": true
},
- "node_modules/es-module-shims": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/es-module-shims/-/es-module-shims-1.8.2.tgz",
- "integrity": "sha512-7vIYVzpOhXtpc3Yn03itB+GSgVZFW7oL4kdydA+iL+IEi7HiSLBUxM05QFw4SxTl6e++pMpGqZPo2+vdNs3TbA=="
- },
"node_modules/es-set-tostringtag": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
@@ -41088,12 +41081,12 @@
"dev": true
},
"node_modules/playwright": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
- "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz",
+ "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==",
"dev": true,
"dependencies": {
- "playwright-core": "1.46.0"
+ "playwright-core": "1.47.0"
},
"bin": {
"playwright": "cli.js"
@@ -41106,9 +41099,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
- "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz",
+ "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -42325,33 +42318,120 @@
}
},
"node_modules/puppeteer-core": {
- "version": "13.7.0",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.7.0.tgz",
- "integrity": "sha512-rXja4vcnAzFAP1OVLq/5dWNfwBGuzcOARJ6qGV7oAZhnLmVRU8G5MsdeQEAOy332ZhkIOnn9jp15R89LKHyp2Q==",
+ "version": "23.1.0",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.1.0.tgz",
+ "integrity": "sha512-SvAsu+xnLN2FMXE/59bp3s3WXp8ewqUGzVV4AQtml/2xmsciZnU/bXcCW+eETHPWQ6Agg2vTI7QzWXPpEARK2g==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "cross-fetch": "3.1.5",
- "debug": "4.3.4",
- "devtools-protocol": "0.0.981744",
- "extract-zip": "2.0.1",
- "https-proxy-agent": "5.0.1",
- "pkg-dir": "4.2.0",
- "progress": "2.0.3",
- "proxy-from-env": "1.1.0",
- "rimraf": "3.0.2",
- "tar-fs": "2.1.1",
- "unbzip2-stream": "1.4.3",
- "ws": "8.5.0"
+ "@puppeteer/browsers": "2.3.1",
+ "chromium-bidi": "0.6.4",
+ "debug": "^4.3.6",
+ "devtools-protocol": "0.0.1312386",
+ "typed-query-selector": "^2.12.0",
+ "ws": "^8.18.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.1.tgz",
+ "integrity": "sha512-uK7o3hHkK+naEobMSJ+2ySYyXtQkBxIH8Gn4MK9ciePjNV+Pf+PgY/W7iPzn2MTjl3stcYB5AlcTmPYw7AXDwA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.3.6",
+ "extract-zip": "^2.0.1",
+ "progress": "^2.0.3",
+ "proxy-agent": "^6.4.0",
+ "semver": "^7.6.3",
+ "tar-fs": "^3.0.6",
+ "unbzip2-stream": "^1.4.3",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "browsers": "lib/cjs/main-cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/agent-base": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+ "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
},
"engines": {
- "node": ">=10.18.1"
+ "node": ">= 14"
}
},
+ "node_modules/puppeteer-core/node_modules/chromium-bidi": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.4.tgz",
+ "integrity": "sha512-8zoq6ogmhQQkAKZVKO2ObFTl4uOkqoX1PlKQX3hZQ5E9cbUotcAb7h4pTNVAGGv8Z36PF3CtdOriEp/Rz82JqQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "mitt": "3.0.1",
+ "urlpattern-polyfill": "10.0.0",
+ "zod": "3.23.8"
+ },
+ "peerDependencies": {
+ "devtools-protocol": "*"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/puppeteer-core/node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"debug": "^4.1.1",
"get-stream": "^5.1.0",
@@ -42367,99 +42447,199 @@
"@types/yauzl": "^2.9.1"
}
},
- "node_modules/puppeteer-core/node_modules/find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "node_modules/puppeteer-core/node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
},
"engines": {
- "node": ">=8"
+ "node": ">= 14"
}
},
- "node_modules/puppeteer-core/node_modules/locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "node_modules/puppeteer-core/node_modules/https-proxy-agent": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
+ "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "p-locate": "^4.1.0"
+ "agent-base": "^7.0.2",
+ "debug": "4"
},
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
- "node_modules/puppeteer-core/node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "node_modules/puppeteer-core/node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"dev": true,
+ "license": "MIT"
+ },
+ "node_modules/puppeteer-core/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/puppeteer-core/node_modules/proxy-agent": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz",
+ "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "p-try": "^2.0.0"
+ "agent-base": "^7.0.2",
+ "debug": "^4.3.4",
+ "http-proxy-agent": "^7.0.1",
+ "https-proxy-agent": "^7.0.3",
+ "lru-cache": "^7.14.1",
+ "pac-proxy-agent": "^7.0.1",
+ "proxy-from-env": "^1.1.0",
+ "socks-proxy-agent": "^8.0.2"
},
"engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">= 14"
}
},
- "node_modules/puppeteer-core/node_modules/p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "node_modules/puppeteer-core/node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "p-limit": "^2.2.0"
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
},
"engines": {
- "node": ">=8"
+ "node": ">=10"
}
},
- "node_modules/puppeteer-core/node_modules/p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "node_modules/puppeteer-core/node_modules/socks-proxy-agent": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
+ "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.1",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
"engines": {
- "node": ">=6"
+ "node": ">= 14"
}
},
- "node_modules/puppeteer-core/node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "node_modules/puppeteer-core/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
"engines": {
"node": ">=8"
}
},
- "node_modules/puppeteer-core/node_modules/pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "node_modules/puppeteer-core/node_modules/tar-fs": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz",
+ "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "find-up": "^4.0.0"
+ "pump": "^3.0.0",
+ "tar-stream": "^3.1.5"
+ },
+ "optionalDependencies": {
+ "bare-fs": "^2.1.1",
+ "bare-path": "^2.1.0"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/puppeteer-core/node_modules/ws": {
- "version": "8.5.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
- "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
+ "utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
@@ -42470,6 +42650,45 @@
}
}
},
+ "node_modules/puppeteer-core/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/pure-rand": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz",
@@ -48633,6 +48852,12 @@
"node": ">= 10"
}
},
+ "node_modules/typed-query-selector": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
+ "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
+ "dev": true
+ },
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -49318,6 +49543,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/urlpattern-polyfill": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz",
+ "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==",
+ "dev": true
+ },
"node_modules/use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@@ -49355,18 +49586,6 @@
"react": ">=16.8"
}
},
- "node_modules/use-lilius": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/use-lilius/-/use-lilius-2.0.5.tgz",
- "integrity": "sha512-IbPjJe4T6B0zQV6ahftVtHvCAxi6RAuDpEcO8TmnHh4nBtx7JbGdpbgXWOUj/9YjrzEbdT/lW7JWcBVbX3MbrA==",
- "dependencies": {
- "date-fns": "^3.6.0"
- },
- "peerDependencies": {
- "react": "*",
- "react-dom": "*"
- }
- },
"node_modules/use-memo-one": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz",
@@ -52052,7 +52271,7 @@
},
"packages/a11y": {
"name": "@wordpress/a11y",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52066,7 +52285,7 @@
},
"packages/annotations": {
"name": "@wordpress/annotations",
- "version": "3.6.0",
+ "version": "3.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52094,7 +52313,7 @@
},
"packages/api-fetch": {
"name": "@wordpress/api-fetch",
- "version": "7.6.0",
+ "version": "7.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52108,7 +52327,7 @@
},
"packages/autop": {
"name": "@wordpress/autop",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -52120,7 +52339,7 @@
},
"packages/babel-plugin-import-jsx-pragma": {
"name": "@wordpress/babel-plugin-import-jsx-pragma",
- "version": "5.6.0",
+ "version": "5.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -52133,7 +52352,7 @@
},
"packages/babel-plugin-makepot": {
"name": "@wordpress/babel-plugin-makepot",
- "version": "6.6.0",
+ "version": "6.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -52151,7 +52370,7 @@
},
"packages/babel-preset-default": {
"name": "@wordpress/babel-preset-default",
- "version": "8.6.0",
+ "version": "8.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -52174,7 +52393,7 @@
},
"packages/base-styles": {
"name": "@wordpress/base-styles",
- "version": "5.6.0",
+ "version": "5.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -52184,7 +52403,7 @@
},
"packages/blob": {
"name": "@wordpress/blob",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -52196,7 +52415,7 @@
},
"packages/block-directory": {
"name": "@wordpress/block-directory",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52231,7 +52450,7 @@
},
"packages/block-editor": {
"name": "@wordpress/block-editor",
- "version": "14.1.0",
+ "version": "14.2.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52330,7 +52549,7 @@
},
"packages/block-library": {
"name": "@wordpress/block-library",
- "version": "9.6.1",
+ "version": "9.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52396,7 +52615,7 @@
},
"packages/block-serialization-default-parser": {
"name": "@wordpress/block-serialization-default-parser",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -52408,7 +52627,7 @@
},
"packages/block-serialization-spec-parser": {
"name": "@wordpress/block-serialization-spec-parser",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"pegjs": "^0.10.0",
@@ -52421,7 +52640,7 @@
},
"packages/blocks": {
"name": "@wordpress/blocks",
- "version": "13.6.0",
+ "version": "13.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52475,7 +52694,7 @@
},
"packages/browserslist-config": {
"name": "@wordpress/browserslist-config",
- "version": "6.6.0",
+ "version": "6.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -52485,7 +52704,7 @@
},
"packages/commands": {
"name": "@wordpress/commands",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52658,7 +52877,7 @@
},
"packages/components": {
"name": "@wordpress/components",
- "version": "28.6.0",
+ "version": "28.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@ariakit/react": "^0.4.10",
@@ -52705,7 +52924,6 @@
"re-resizable": "^6.4.0",
"react-colorful": "^5.3.1",
"remove-accents": "^0.5.0",
- "use-lilius": "^2.0.5",
"uuid": "^9.0.1"
},
"engines": {
@@ -52779,7 +52997,7 @@
},
"packages/compose": {
"name": "@wordpress/compose",
- "version": "7.6.0",
+ "version": "7.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52816,7 +53034,7 @@
},
"packages/core-commands": {
"name": "@wordpress/core-commands",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52844,7 +53062,7 @@
},
"packages/core-data": {
"name": "@wordpress/core-data",
- "version": "7.6.0",
+ "version": "7.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52889,7 +53107,7 @@
},
"packages/create-block": {
"name": "@wordpress/create-block",
- "version": "4.49.0",
+ "version": "4.50.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -52917,7 +53135,7 @@
},
"packages/create-block-tutorial-template": {
"name": "@wordpress/create-block-tutorial-template",
- "version": "4.6.0",
+ "version": "4.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -52927,7 +53145,7 @@
},
"packages/customize-widgets": {
"name": "@wordpress/customize-widgets",
- "version": "5.6.1",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52965,7 +53183,7 @@
},
"packages/data": {
"name": "@wordpress/data",
- "version": "10.6.0",
+ "version": "10.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -52994,7 +53212,7 @@
},
"packages/data-controls": {
"name": "@wordpress/data-controls",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53012,7 +53230,7 @@
},
"packages/dataviews": {
"name": "@wordpress/dataviews",
- "version": "4.2.0",
+ "version": "4.3.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@ariakit/react": "^0.4.10",
@@ -53069,7 +53287,7 @@
},
"packages/date": {
"name": "@wordpress/date",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53084,7 +53302,7 @@
},
"packages/dependency-extraction-webpack-plugin": {
"name": "@wordpress/dependency-extraction-webpack-plugin",
- "version": "6.6.0",
+ "version": "6.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53100,7 +53318,7 @@
},
"packages/deprecated": {
"name": "@wordpress/deprecated",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53113,7 +53331,7 @@
},
"packages/docgen": {
"name": "@wordpress/docgen",
- "version": "2.6.0",
+ "version": "2.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53135,7 +53353,7 @@
},
"packages/dom": {
"name": "@wordpress/dom",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53148,7 +53366,7 @@
},
"packages/dom-ready": {
"name": "@wordpress/dom-ready",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -53160,7 +53378,7 @@
},
"packages/e2e-test-utils": {
"name": "@wordpress/e2e-test-utils",
- "version": "11.6.0",
+ "version": "11.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53183,7 +53401,7 @@
},
"packages/e2e-test-utils-playwright": {
"name": "@wordpress/e2e-test-utils-playwright",
- "version": "1.6.0",
+ "version": "1.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53211,7 +53429,7 @@
},
"packages/e2e-tests": {
"name": "@wordpress/e2e-tests",
- "version": "8.6.0",
+ "version": "8.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53251,7 +53469,7 @@
},
"packages/edit-post": {
"name": "@wordpress/edit-post",
- "version": "8.6.1",
+ "version": "8.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53298,7 +53516,7 @@
},
"packages/edit-site": {
"name": "@wordpress/edit-site",
- "version": "6.6.1",
+ "version": "6.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53360,7 +53578,7 @@
},
"packages/edit-widgets": {
"name": "@wordpress/edit-widgets",
- "version": "6.6.1",
+ "version": "6.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53403,7 +53621,7 @@
},
"packages/editor": {
"name": "@wordpress/editor",
- "version": "14.6.0",
+ "version": "14.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53464,7 +53682,7 @@
},
"packages/element": {
"name": "@wordpress/element",
- "version": "6.6.0",
+ "version": "6.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53483,7 +53701,7 @@
},
"packages/env": {
"name": "@wordpress/env",
- "version": "10.6.0",
+ "version": "10.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53625,7 +53843,7 @@
},
"packages/escape-html": {
"name": "@wordpress/escape-html",
- "version": "3.6.0",
+ "version": "3.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -53637,7 +53855,7 @@
},
"packages/eslint-plugin": {
"name": "@wordpress/eslint-plugin",
- "version": "20.3.0",
+ "version": "21.0.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53678,9 +53896,24 @@
}
}
},
+ "packages/fields": {
+ "name": "@wordpress/fields",
+ "version": "0.0.1",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@babel/runtime": "^7.16.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
"packages/format-library": {
"name": "@wordpress/format-library",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53708,7 +53941,7 @@
},
"packages/hooks": {
"name": "@wordpress/hooks",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -53720,7 +53953,7 @@
},
"packages/html-entities": {
"name": "@wordpress/html-entities",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -53732,7 +53965,7 @@
},
"packages/i18n": {
"name": "@wordpress/i18n",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53752,7 +53985,7 @@
},
"packages/icons": {
"name": "@wordpress/icons",
- "version": "10.6.0",
+ "version": "10.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53766,7 +53999,7 @@
},
"packages/interactivity": {
"name": "@wordpress/interactivity",
- "version": "6.6.0",
+ "version": "6.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@preact/signals": "^1.2.2",
@@ -53779,7 +54012,7 @@
},
"packages/interactivity-router": {
"name": "@wordpress/interactivity-router",
- "version": "2.6.0",
+ "version": "2.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@wordpress/interactivity": "file:../interactivity"
@@ -53815,7 +54048,7 @@
},
"packages/interface": {
"name": "@wordpress/interface",
- "version": "6.6.0",
+ "version": "6.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53844,7 +54077,7 @@
},
"packages/is-shallow-equal": {
"name": "@wordpress/is-shallow-equal",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -53856,7 +54089,7 @@
},
"packages/jest-console": {
"name": "@wordpress/jest-console",
- "version": "8.6.0",
+ "version": "8.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53873,7 +54106,7 @@
},
"packages/jest-preset-default": {
"name": "@wordpress/jest-preset-default",
- "version": "12.6.0",
+ "version": "12.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53891,7 +54124,7 @@
},
"packages/jest-puppeteer-axe": {
"name": "@wordpress/jest-puppeteer-axe",
- "version": "7.6.0",
+ "version": "7.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53914,7 +54147,7 @@
},
"packages/keyboard-shortcuts": {
"name": "@wordpress/keyboard-shortcuts",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53932,7 +54165,7 @@
},
"packages/keycodes": {
"name": "@wordpress/keycodes",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53945,7 +54178,7 @@
},
"packages/lazy-import": {
"name": "@wordpress/lazy-import",
- "version": "2.6.0",
+ "version": "2.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -53960,7 +54193,7 @@
},
"packages/list-reusable-blocks": {
"name": "@wordpress/list-reusable-blocks",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53983,7 +54216,7 @@
},
"packages/media-utils": {
"name": "@wordpress/media-utils",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -53999,7 +54232,7 @@
},
"packages/notices": {
"name": "@wordpress/notices",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54016,7 +54249,7 @@
},
"packages/npm-package-json-lint-config": {
"name": "@wordpress/npm-package-json-lint-config",
- "version": "5.6.0",
+ "version": "5.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -54029,7 +54262,7 @@
},
"packages/nux": {
"name": "@wordpress/nux",
- "version": "9.6.0",
+ "version": "9.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54052,7 +54285,7 @@
},
"packages/patterns": {
"name": "@wordpress/patterns",
- "version": "2.6.0",
+ "version": "2.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54082,7 +54315,7 @@
},
"packages/plugins": {
"name": "@wordpress/plugins",
- "version": "7.6.0",
+ "version": "7.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54105,7 +54338,7 @@
},
"packages/postcss-plugins-preset": {
"name": "@wordpress/postcss-plugins-preset",
- "version": "5.6.0",
+ "version": "5.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -54122,7 +54355,7 @@
},
"packages/postcss-themes": {
"name": "@wordpress/postcss-themes",
- "version": "6.6.0",
+ "version": "6.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -54135,7 +54368,7 @@
},
"packages/preferences": {
"name": "@wordpress/preferences",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54161,7 +54394,7 @@
},
"packages/preferences-persistence": {
"name": "@wordpress/preferences-persistence",
- "version": "2.6.0",
+ "version": "2.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54174,7 +54407,7 @@
},
"packages/prettier-config": {
"name": "@wordpress/prettier-config",
- "version": "4.6.0",
+ "version": "4.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -54187,7 +54420,7 @@
},
"packages/primitives": {
"name": "@wordpress/primitives",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54204,7 +54437,7 @@
},
"packages/priority-queue": {
"name": "@wordpress/priority-queue",
- "version": "3.6.0",
+ "version": "3.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54217,7 +54450,7 @@
},
"packages/private-apis": {
"name": "@wordpress/private-apis",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -54229,7 +54462,7 @@
},
"packages/project-management-automation": {
"name": "@wordpress/project-management-automation",
- "version": "2.6.0",
+ "version": "2.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -54259,7 +54492,7 @@
},
"packages/react-i18n": {
"name": "@wordpress/react-i18n",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54415,7 +54648,7 @@
},
"packages/readable-js-assets-webpack-plugin": {
"name": "@wordpress/readable-js-assets-webpack-plugin",
- "version": "3.6.0",
+ "version": "3.7.0",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -54428,7 +54661,7 @@
},
"packages/redux-routine": {
"name": "@wordpress/redux-routine",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54473,7 +54706,7 @@
},
"packages/reusable-blocks": {
"name": "@wordpress/reusable-blocks",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54500,7 +54733,7 @@
},
"packages/rich-text": {
"name": "@wordpress/rich-text",
- "version": "7.6.0",
+ "version": "7.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54524,7 +54757,7 @@
},
"packages/router": {
"name": "@wordpress/router",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -54543,7 +54776,7 @@
},
"packages/scripts": {
"name": "@wordpress/scripts",
- "version": "28.6.0",
+ "version": "29.0.0",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -54591,7 +54824,7 @@
"postcss-import": "^16.1.0",
"postcss-loader": "^6.2.1",
"prettier": "npm:wp-prettier@3.0.3",
- "puppeteer-core": "^13.2.0",
+ "puppeteer-core": "^23.1.0",
"react-refresh": "^0.14.0",
"read-pkg-up": "^7.0.1",
"resolve-bin": "^0.4.0",
@@ -54616,7 +54849,7 @@
"npm": ">=8.19.2"
},
"peerDependencies": {
- "@playwright/test": "^1.46.0",
+ "@playwright/test": "^1.47.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
@@ -54998,7 +55231,7 @@
},
"packages/server-side-render": {
"name": "@wordpress/server-side-render",
- "version": "5.6.0",
+ "version": "5.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55024,7 +55257,7 @@
},
"packages/shortcode": {
"name": "@wordpress/shortcode",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55037,7 +55270,7 @@
},
"packages/style-engine": {
"name": "@wordpress/style-engine",
- "version": "2.6.0",
+ "version": "2.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55050,7 +55283,7 @@
},
"packages/stylelint-config": {
"name": "@wordpress/stylelint-config",
- "version": "22.6.0",
+ "version": "22.7.0",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -55067,7 +55300,7 @@
},
"packages/sync": {
"name": "@wordpress/sync",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55088,7 +55321,7 @@
},
"packages/token-list": {
"name": "@wordpress/token-list",
- "version": "3.6.0",
+ "version": "3.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -55100,7 +55333,7 @@
},
"packages/undo-manager": {
"name": "@wordpress/undo-manager",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55113,7 +55346,7 @@
},
"packages/url": {
"name": "@wordpress/url",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55126,7 +55359,7 @@
},
"packages/viewport": {
"name": "@wordpress/viewport",
- "version": "6.6.0",
+ "version": "6.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55144,7 +55377,7 @@
},
"packages/warning": {
"name": "@wordpress/warning",
- "version": "3.6.0",
+ "version": "3.7.0",
"license": "GPL-2.0-or-later",
"engines": {
"node": ">=18.12.0",
@@ -55153,7 +55386,7 @@
},
"packages/widgets": {
"name": "@wordpress/widgets",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0",
@@ -55181,7 +55414,7 @@
},
"packages/wordcount": {
"name": "@wordpress/wordcount",
- "version": "4.6.0",
+ "version": "4.7.0",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "^7.16.0"
@@ -60029,12 +60262,12 @@
}
},
"@playwright/test": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz",
- "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.0.tgz",
+ "integrity": "sha512-SgAdlSwYVpToI4e/IH19IHHWvoijAYH5hu2MWSXptRypLSnzj51PcGD+rsOXFayde4P9ZLi+loXVwArg6IUkCA==",
"dev": true,
"requires": {
- "playwright": "1.46.0"
+ "playwright": "1.47.0"
}
},
"@pmmmwh/react-refresh-webpack-plugin": {
@@ -67572,7 +67805,6 @@
"re-resizable": "^6.4.0",
"react-colorful": "^5.3.1",
"remove-accents": "^0.5.0",
- "use-lilius": "^2.0.5",
"uuid": "^9.0.1"
},
"dependencies": {
@@ -68236,6 +68468,12 @@
"requireindex": "^1.2.0"
}
},
+ "@wordpress/fields": {
+ "version": "file:packages/fields",
+ "requires": {
+ "@babel/runtime": "^7.16.0"
+ }
+ },
"@wordpress/format-library": {
"version": "file:packages/format-library",
"requires": {
@@ -68770,7 +69008,7 @@
"postcss-import": "^16.1.0",
"postcss-loader": "^6.2.1",
"prettier": "npm:wp-prettier@3.0.3",
- "puppeteer-core": "^13.2.0",
+ "puppeteer-core": "^23.1.0",
"react-refresh": "^0.14.0",
"read-pkg-up": "^7.0.1",
"resolve-bin": "^0.4.0",
@@ -73319,15 +73557,6 @@
"is-windows": "^1.0.0"
}
},
- "cross-fetch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
- "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
- "dev": true,
- "requires": {
- "node-fetch": "2.6.7"
- }
- },
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -74451,9 +74680,9 @@
}
},
"devtools-protocol": {
- "version": "0.0.981744",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz",
- "integrity": "sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==",
+ "version": "0.0.1312386",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz",
+ "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==",
"dev": true
},
"diff": {
@@ -75056,11 +75285,6 @@
"integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==",
"dev": true
},
- "es-module-shims": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/es-module-shims/-/es-module-shims-1.8.2.tgz",
- "integrity": "sha512-7vIYVzpOhXtpc3Yn03itB+GSgVZFW7oL4kdydA+iL+IEi7HiSLBUxM05QFw4SxTl6e++pMpGqZPo2+vdNs3TbA=="
- },
"es-set-tostringtag": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
@@ -87363,19 +87587,19 @@
"dev": true
},
"playwright": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
- "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz",
+ "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==",
"dev": true,
"requires": {
"fsevents": "2.3.2",
- "playwright-core": "1.46.0"
+ "playwright-core": "1.47.0"
}
},
"playwright-core": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
- "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz",
+ "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==",
"dev": true
},
"please-upgrade-node": {
@@ -88284,25 +88508,81 @@
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
},
"puppeteer-core": {
- "version": "13.7.0",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.7.0.tgz",
- "integrity": "sha512-rXja4vcnAzFAP1OVLq/5dWNfwBGuzcOARJ6qGV7oAZhnLmVRU8G5MsdeQEAOy332ZhkIOnn9jp15R89LKHyp2Q==",
+ "version": "23.1.0",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.1.0.tgz",
+ "integrity": "sha512-SvAsu+xnLN2FMXE/59bp3s3WXp8ewqUGzVV4AQtml/2xmsciZnU/bXcCW+eETHPWQ6Agg2vTI7QzWXPpEARK2g==",
"dev": true,
"requires": {
- "cross-fetch": "3.1.5",
- "debug": "4.3.4",
- "devtools-protocol": "0.0.981744",
- "extract-zip": "2.0.1",
- "https-proxy-agent": "5.0.1",
- "pkg-dir": "4.2.0",
- "progress": "2.0.3",
- "proxy-from-env": "1.1.0",
- "rimraf": "3.0.2",
- "tar-fs": "2.1.1",
- "unbzip2-stream": "1.4.3",
- "ws": "8.5.0"
+ "@puppeteer/browsers": "2.3.1",
+ "chromium-bidi": "0.6.4",
+ "debug": "^4.3.6",
+ "devtools-protocol": "0.0.1312386",
+ "typed-query-selector": "^2.12.0",
+ "ws": "^8.18.0"
},
"dependencies": {
+ "@puppeteer/browsers": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.1.tgz",
+ "integrity": "sha512-uK7o3hHkK+naEobMSJ+2ySYyXtQkBxIH8Gn4MK9ciePjNV+Pf+PgY/W7iPzn2MTjl3stcYB5AlcTmPYw7AXDwA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.6",
+ "extract-zip": "^2.0.1",
+ "progress": "^2.0.3",
+ "proxy-agent": "^6.4.0",
+ "semver": "^7.6.3",
+ "tar-fs": "^3.0.6",
+ "unbzip2-stream": "^1.4.3",
+ "yargs": "^17.7.2"
+ }
+ },
+ "agent-base": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+ "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4"
+ }
+ },
+ "chromium-bidi": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.4.tgz",
+ "integrity": "sha512-8zoq6ogmhQQkAKZVKO2ObFTl4uOkqoX1PlKQX3hZQ5E9cbUotcAb7h4pTNVAGGv8Z36PF3CtdOriEp/Rz82JqQ==",
+ "dev": true,
+ "requires": {
+ "mitt": "3.0.1",
+ "urlpattern-polyfill": "10.0.0",
+ "zod": "3.23.8"
+ }
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
"extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
@@ -88315,68 +88595,169 @@
"yauzl": "^2.10.0"
}
},
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
}
},
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "https-proxy-agent": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
+ "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
"dev": true,
"requires": {
- "p-locate": "^4.1.0"
+ "agent-base": "^7.0.2",
+ "debug": "4"
}
},
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "dev": true
+ },
+ "mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "proxy-agent": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz",
+ "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==",
"dev": true,
"requires": {
- "p-try": "^2.0.0"
+ "agent-base": "^7.0.2",
+ "debug": "^4.3.4",
+ "http-proxy-agent": "^7.0.1",
+ "https-proxy-agent": "^7.0.3",
+ "lru-cache": "^7.14.1",
+ "pac-proxy-agent": "^7.0.1",
+ "proxy-from-env": "^1.1.0",
+ "socks-proxy-agent": "^8.0.2"
}
},
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
- "p-limit": "^2.2.0"
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"dev": true
},
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
+ "socks-proxy-agent": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
+ "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.1",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ }
},
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"requires": {
- "find-up": "^4.0.0"
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "tar-fs": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz",
+ "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==",
+ "dev": true,
+ "requires": {
+ "bare-fs": "^2.1.1",
+ "bare-path": "^2.1.0",
+ "pump": "^3.0.0",
+ "tar-stream": "^3.1.5"
+ }
+ },
+ "tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "dev": true,
+ "requires": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
}
},
"ws": {
- "version": "8.5.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
- "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "dev": true
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true
}
}
@@ -93104,6 +93485,12 @@
"integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==",
"dev": true
},
+ "typed-query-selector": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
+ "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
+ "dev": true
+ },
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -93631,6 +94018,12 @@
"integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==",
"dev": true
},
+ "urlpattern-polyfill": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz",
+ "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==",
+ "dev": true
+ },
"use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@@ -93650,14 +94043,6 @@
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.9.tgz",
"integrity": "sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw=="
},
- "use-lilius": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/use-lilius/-/use-lilius-2.0.5.tgz",
- "integrity": "sha512-IbPjJe4T6B0zQV6ahftVtHvCAxi6RAuDpEcO8TmnHh4nBtx7JbGdpbgXWOUj/9YjrzEbdT/lW7JWcBVbX3MbrA==",
- "requires": {
- "date-fns": "^3.6.0"
- }
- },
"use-memo-one": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz",
diff --git a/package.json b/package.json
index c0a7853a418178..9236dbfb47adee 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "19.1.0",
+ "version": "19.2.0-rc.1",
"private": true,
"description": "A new WordPress editor experience.",
"author": "The WordPress Contributors",
@@ -52,12 +52,14 @@
"@wordpress/editor": "file:packages/editor",
"@wordpress/element": "file:packages/element",
"@wordpress/escape-html": "file:packages/escape-html",
+ "@wordpress/fields": "file:packages/fields",
"@wordpress/format-library": "file:packages/format-library",
"@wordpress/hooks": "file:packages/hooks",
"@wordpress/html-entities": "file:packages/html-entities",
"@wordpress/i18n": "file:packages/i18n",
"@wordpress/icons": "file:packages/icons",
"@wordpress/interactivity": "file:packages/interactivity",
+ "@wordpress/interactivity-router": "file:packages/interactivity-router",
"@wordpress/interface": "file:packages/interface",
"@wordpress/is-shallow-equal": "file:packages/is-shallow-equal",
"@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts",
@@ -91,8 +93,7 @@
"@wordpress/viewport": "file:packages/viewport",
"@wordpress/warning": "file:packages/warning",
"@wordpress/widgets": "file:packages/widgets",
- "@wordpress/wordcount": "file:packages/wordcount",
- "es-module-shims": "^1.8.2"
+ "@wordpress/wordcount": "file:packages/wordcount"
},
"devDependencies": {
"@actions/core": "1.9.1",
@@ -111,7 +112,7 @@
"@octokit/rest": "16.26.0",
"@octokit/types": "6.34.0",
"@octokit/webhooks-types": "5.8.0",
- "@playwright/test": "1.46.0",
+ "@playwright/test": "1.47.0",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
"@react-native/babel-preset": "0.73.10",
"@react-native/metro-babel-transformer": "0.73.10",
@@ -233,6 +234,7 @@
"postcss-local-keyframes": "^0.0.2",
"prettier": "npm:wp-prettier@3.0.3",
"progress": "2.0.3",
+ "puppeteer-core": "23.1.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.73.3",
diff --git a/packages/a11y/CHANGELOG.md b/packages/a11y/CHANGELOG.md
index 5a90a6cc98f0de..36973cce020f33 100644
--- a/packages/a11y/CHANGELOG.md
+++ b/packages/a11y/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/a11y/package.json b/packages/a11y/package.json
index 0e307e813c56ad..88123b3c6c7126 100644
--- a/packages/a11y/package.json
+++ b/packages/a11y/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/a11y",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Accessibility (a11y) utilities for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/annotations/CHANGELOG.md b/packages/annotations/CHANGELOG.md
index b3e0dbab635b8b..6211cf30688e5c 100644
--- a/packages/annotations/CHANGELOG.md
+++ b/packages/annotations/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 3.7.0 (2024-09-05)
+
## 3.6.0 (2024-08-21)
## 3.5.0 (2024-08-07)
diff --git a/packages/annotations/package.json b/packages/annotations/package.json
index 95ceadb1d990ed..c189dcc11fd09f 100644
--- a/packages/annotations/package.json
+++ b/packages/annotations/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/annotations",
- "version": "3.6.0",
+ "version": "3.7.0",
"description": "Annotate content in the Gutenberg editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md
index 49319c18ab7543..01b37d4e14a7d1 100644
--- a/packages/api-fetch/CHANGELOG.md
+++ b/packages/api-fetch/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 7.7.0 (2024-09-05)
+
## 7.6.0 (2024-08-21)
## 7.5.0 (2024-08-07)
diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json
index abbe8cce374c31..76d797f4ddfa57 100644
--- a/packages/api-fetch/package.json
+++ b/packages/api-fetch/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/api-fetch",
- "version": "7.6.0",
+ "version": "7.7.0",
"description": "Utility to make WordPress REST API requests.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/autop/CHANGELOG.md b/packages/autop/CHANGELOG.md
index 353495eae8983d..ed19f1ceafbc58 100644
--- a/packages/autop/CHANGELOG.md
+++ b/packages/autop/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/autop/package.json b/packages/autop/package.json
index 03bb56412195ec..4e1cf577ebd09c 100644
--- a/packages/autop/package.json
+++ b/packages/autop/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/autop",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "WordPress's automatic paragraph functions `autop` and `removep`.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
index d96d2315e2a51e..c916b788243a06 100644
--- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
+++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json
index be1dbc4235f289..1af6bdf93cb044 100644
--- a/packages/babel-plugin-import-jsx-pragma/package.json
+++ b/packages/babel-plugin-import-jsx-pragma/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/babel-plugin-import-jsx-pragma",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/babel-plugin-makepot/CHANGELOG.md b/packages/babel-plugin-makepot/CHANGELOG.md
index 98da250e2ba6b6..e7345f314d5788 100644
--- a/packages/babel-plugin-makepot/CHANGELOG.md
+++ b/packages/babel-plugin-makepot/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json
index 4d439baf28163d..a7c18d891538d1 100644
--- a/packages/babel-plugin-makepot/package.json
+++ b/packages/babel-plugin-makepot/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/babel-plugin-makepot",
- "version": "6.6.0",
+ "version": "6.7.0",
"description": "WordPress Babel internationalization (i18n) plugin.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md
index 43c9675fcb885b..70a740f60cad38 100644
--- a/packages/babel-preset-default/CHANGELOG.md
+++ b/packages/babel-preset-default/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 8.7.0 (2024-09-05)
+
## 8.6.0 (2024-08-21)
## 8.5.0 (2024-08-07)
diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json
index 2559b60401790e..be5b3786346eb2 100644
--- a/packages/babel-preset-default/package.json
+++ b/packages/babel-preset-default/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/babel-preset-default",
- "version": "8.6.0",
+ "version": "8.7.0",
"description": "Default Babel preset for WordPress development.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/base-styles/CHANGELOG.md b/packages/base-styles/CHANGELOG.md
index ce98cebeae1c2a..cad2f94c98da7b 100644
--- a/packages/base-styles/CHANGELOG.md
+++ b/packages/base-styles/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss
index 69735d75aac71a..ebccbe0e5e8aee 100644
--- a/packages/base-styles/_mixins.scss
+++ b/packages/base-styles/_mixins.scss
@@ -74,7 +74,7 @@
@mixin input-style__neutral() {
box-shadow: 0 0 0 transparent;
transition: box-shadow 0.1s linear;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
border: $border-width solid $gray-600;
@include reduce-motion("transition");
}
@@ -227,7 +227,7 @@
border: $border-width solid $gray-900;
margin-right: $grid-unit-15;
transition: none;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
&:focus {
box-shadow: 0 0 0 ($border-width * 2) $white, 0 0 0 ($border-width * 2 + $border-width-focus-fallback) var(--wp-admin-theme-color);
@@ -363,7 +363,7 @@
&:focus {
color: var(--wp-admin-theme-color--rgb);
box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color, #007cba);
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
}
}
@@ -375,7 +375,7 @@
padding: $grid-unit-15 !important;
border: $border-width solid $gray-900 !important;
box-shadow: none !important;
- border-radius: $radius-block-ui !important;
+ border-radius: $radius-small !important;
// Fonts smaller than 16px causes mobile safari to zoom.
font-size: $mobile-text-min-font-size !important;
diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json
index fbe3bf149b1cdf..6839a8081e04a8 100644
--- a/packages/base-styles/package.json
+++ b/packages/base-styles/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/base-styles",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Base SCSS utilities and variables for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/blob/CHANGELOG.md b/packages/blob/CHANGELOG.md
index 5c21e96d7269cd..f95598025a8b68 100644
--- a/packages/blob/CHANGELOG.md
+++ b/packages/blob/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/blob/package.json b/packages/blob/package.json
index 58feea189bed0e..3dea70c9771976 100644
--- a/packages/blob/package.json
+++ b/packages/blob/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/blob",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Blob utilities for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/block-directory/CHANGELOG.md b/packages/block-directory/CHANGELOG.md
index 4d15343f0c0926..70914f293ca998 100644
--- a/packages/block-directory/CHANGELOG.md
+++ b/packages/block-directory/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json
index 85222ecaa38827..39f239edfcb44a 100644
--- a/packages/block-directory/package.json
+++ b/packages/block-directory/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/block-directory",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Extend editor with block directory features to search, download and install blocks.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/block-directory/src/components/downloadable-block-list-item/index.js b/packages/block-directory/src/components/downloadable-block-list-item/index.js
index 03e6e79d8928e9..ac587dc2d6d0cc 100644
--- a/packages/block-directory/src/components/downloadable-block-list-item/index.js
+++ b/packages/block-directory/src/components/downloadable-block-list-item/index.js
@@ -6,7 +6,7 @@ import {
Button,
Spinner,
VisuallyHidden,
- privateApis as componentsPrivateApis,
+ Composite,
} from '@wordpress/components';
import { createInterpolateElement } from '@wordpress/element';
import { decodeEntities } from '@wordpress/html-entities';
@@ -20,9 +20,6 @@ import BlockRatings from '../block-ratings';
import DownloadableBlockIcon from '../downloadable-block-icon';
import DownloadableBlockNotice from '../downloadable-block-notice';
import { store as blockDirectoryStore } from '../../store';
-import { unlock } from '../../lock-unlock';
-
-const { CompositeItemV2: CompositeItem } = unlock( componentsPrivateApis );
// Return the appropriate block item label, given the block data and status.
function getDownloadableBlockLabel(
@@ -65,7 +62,7 @@ function getDownloadableBlockLabel(
);
}
-function DownloadableBlockListItem( { composite, item, onClick } ) {
+function DownloadableBlockListItem( { item, onClick } ) {
const { author, description, icon, rating, title } = item;
// getBlockType returns a block object if this block exists, or null if not.
const isInstalled = !! getBlockType( item.name );
@@ -93,7 +90,7 @@ function DownloadableBlockListItem( { composite, item, onClick } ) {
}
return (
-
}
- store={ composite }
disabled={ isInstalling || ! isInstallable }
>
@@ -163,7 +159,7 @@ function DownloadableBlockListItem( { composite, item, onClick } ) {
>
) }
-
+
);
}
diff --git a/packages/block-directory/src/components/downloadable-blocks-list/index.js b/packages/block-directory/src/components/downloadable-blocks-list/index.js
index 09f509c4ed49c9..3e44aa423bfa5e 100644
--- a/packages/block-directory/src/components/downloadable-blocks-list/index.js
+++ b/packages/block-directory/src/components/downloadable-blocks-list/index.js
@@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { privateApis as componentsPrivateApis } from '@wordpress/components';
+import { Composite } from '@wordpress/components';
import { getBlockType } from '@wordpress/blocks';
import { useDispatch } from '@wordpress/data';
@@ -11,14 +11,10 @@ import { useDispatch } from '@wordpress/data';
*/
import DownloadableBlockListItem from '../downloadable-block-list-item';
import { store as blockDirectoryStore } from '../../store';
-import { unlock } from '../../lock-unlock';
-const { CompositeV2: Composite, useCompositeStoreV2: useCompositeStore } =
- unlock( componentsPrivateApis );
const noop = () => {};
function DownloadableBlocksList( { items, onHover = noop, onSelect } ) {
- const composite = useCompositeStore();
const { installBlockType } = useDispatch( blockDirectoryStore );
if ( ! items.length ) {
@@ -27,7 +23,6 @@ function DownloadableBlocksList( { items, onHover = noop, onSelect } ) {
return (
{
// Check if the block is registered (`getBlockType`
// will return an object). If so, insert the block.
diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md
index 0b365c18ae8ad4..da44ebc4149f58 100644
--- a/packages/block-editor/CHANGELOG.md
+++ b/packages/block-editor/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 14.2.0 (2024-09-05)
+
## 14.1.0 (2024-08-21)
## 14.0.0 (2024-08-07)
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index 1f4fa7588ec6c9..d3064c78dbe658 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/block-editor",
- "version": "14.1.0",
+ "version": "14.2.0",
"description": "Generic block editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/block-editor/src/components/block-breadcrumb/index.js b/packages/block-editor/src/components/block-breadcrumb/index.js
index 8a70616d358d9e..440d9611d278be 100644
--- a/packages/block-editor/src/components/block-breadcrumb/index.js
+++ b/packages/block-editor/src/components/block-breadcrumb/index.js
@@ -66,10 +66,8 @@ function BlockBreadcrumb( { rootLabelText } ) {
>
{ hasSelection && (
{
// Find the block editor wrapper for the selected block
const blockEditor = blockRef.current?.closest(
@@ -96,10 +94,8 @@ function BlockBreadcrumb( { rootLabelText } ) {
{ parents.map( ( parentClientId ) => (
selectBlock( parentClientId ) }
>
diff --git a/packages/block-editor/src/components/block-draggable/content.scss b/packages/block-editor/src/components/block-draggable/content.scss
index f1318daebd5a04..102230168e2133 100644
--- a/packages/block-editor/src/components/block-draggable/content.scss
+++ b/packages/block-editor/src/components/block-draggable/content.scss
@@ -3,7 +3,7 @@
.block-editor-block-list__layout .is-dragging {
background-color: currentColor !important;
opacity: 0.05 !important;
- border-radius: $radius-block-ui !important;
+ border-radius: $radius-small !important;
// Disabling pointer events during the drag event is necessary,
// lest the block might affect your drag operation.
diff --git a/packages/block-editor/src/components/block-draggable/style.scss b/packages/block-editor/src/components/block-draggable/style.scss
index afbf77319f7200..349afa2c3563c0 100644
--- a/packages/block-editor/src/components/block-draggable/style.scss
+++ b/packages/block-editor/src/components/block-draggable/style.scss
@@ -7,7 +7,7 @@
.block-editor-block-draggable-chip {
background-color: $gray-900;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
box-shadow: 0 6px 8px rgba($black, 0.3);
color: $white;
cursor: grabbing;
diff --git a/packages/block-editor/src/components/block-edit/multiple-usage-warning.js b/packages/block-editor/src/components/block-edit/multiple-usage-warning.js
index bff12342904537..ac83b0e5c11315 100644
--- a/packages/block-editor/src/components/block-edit/multiple-usage-warning.js
+++ b/packages/block-editor/src/components/block-edit/multiple-usage-warning.js
@@ -24,8 +24,7 @@ export function MultipleUsageWarning( {
selectBlock( originalBlockClientId ) }
@@ -33,8 +32,7 @@ export function MultipleUsageWarning( {
{ __( 'Find original' ) }
,
onReplace( [] ) }
diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js
index 75b5239f47d6cc..a18556f2fa5bd9 100644
--- a/packages/block-editor/src/components/block-inspector/index.js
+++ b/packages/block-editor/src/components/block-inspector/index.js
@@ -72,7 +72,6 @@ function BlockInspectorLockedBlocks( { topLevelLockedBlock } ) {
{ ...blockInformation }
className={ blockInformation.isSynced && 'is-synced' }
/>
-
{ hasBlockStyles && (
@@ -322,6 +321,7 @@ const BlockInspectorSingleBlock = ( { clientId, blockName } ) => {
/>
+
diff --git a/packages/block-editor/src/components/block-list/block-invalid-warning.js b/packages/block-editor/src/components/block-list/block-invalid-warning.js
index fb235cbce9ca21..a35ecaf6bae8eb 100644
--- a/packages/block-editor/src/components/block-list/block-invalid-warning.js
+++ b/packages/block-editor/src/components/block-list/block-invalid-warning.js
@@ -101,8 +101,7 @@ export default function BlockInvalidWarning( { clientId } ) {
.block-editor-block-list__zoom-out-separator,
+.block-editor-block-list__layout.is-root-container.has-global-padding > .block-editor-block-list__zoom-out-separator {
+ max-width: none;
+ // Additional -1px is required to avoid sub pixel rounding errors allowing background to show.
+ margin: 0 calc(-1 * var(--wp--style--root--padding-right) - 1px) 0 calc(-1 * var(--wp--style--root--padding-left) - 1px) !important;
+}
diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js
index 37dba80511d920..ea6128f1534642 100644
--- a/packages/block-editor/src/components/block-list/index.js
+++ b/packages/block-editor/src/components/block-list/index.js
@@ -39,6 +39,7 @@ import {
DEFAULT_BLOCK_EDIT_CONTEXT,
} from '../block-edit/context';
import { useTypingObserver } from '../observe-typing';
+import { ZoomOutSeparator } from './zoom-out-separator';
import { unlock } from '../../lock-unlock';
export const IntersectionObserver = createContext();
@@ -174,49 +175,55 @@ function Items( {
// function on every render.
const hasAppender = CustomAppender !== false;
const hasCustomAppender = !! CustomAppender;
- const { order, selectedBlocks, visibleBlocks, shouldRenderAppender } =
- useSelect(
- ( select ) => {
- const {
- getSettings,
- getBlockOrder,
- getSelectedBlockClientId,
- getSelectedBlockClientIds,
- __unstableGetVisibleBlocks,
- getTemplateLock,
- getBlockEditingMode,
- __unstableGetEditorMode,
- } = select( blockEditorStore );
-
- const _order = getBlockOrder( rootClientId );
+ const {
+ order,
+ isZoomOut,
+ selectedBlocks,
+ visibleBlocks,
+ shouldRenderAppender,
+ } = useSelect(
+ ( select ) => {
+ const {
+ getSettings,
+ getBlockOrder,
+ getSelectedBlockClientId,
+ getSelectedBlockClientIds,
+ __unstableGetVisibleBlocks,
+ getTemplateLock,
+ getBlockEditingMode,
+ __unstableGetEditorMode,
+ } = select( blockEditorStore );
- if ( getSettings().__unstableIsPreviewMode ) {
- return {
- order: _order,
- selectedBlocks: EMPTY_ARRAY,
- visibleBlocks: EMPTY_SET,
- };
- }
+ const _order = getBlockOrder( rootClientId );
- const selectedBlockClientId = getSelectedBlockClientId();
+ if ( getSettings().__unstableIsPreviewMode ) {
return {
order: _order,
- selectedBlocks: getSelectedBlockClientIds(),
- visibleBlocks: __unstableGetVisibleBlocks(),
- shouldRenderAppender:
- hasAppender &&
- __unstableGetEditorMode() !== 'zoom-out' &&
- ( hasCustomAppender
- ? ! getTemplateLock( rootClientId ) &&
- getBlockEditingMode( rootClientId ) !== 'disabled'
- : rootClientId === selectedBlockClientId ||
- ( ! rootClientId &&
- ! selectedBlockClientId &&
- ! _order.length ) ),
+ selectedBlocks: EMPTY_ARRAY,
+ visibleBlocks: EMPTY_SET,
};
- },
- [ rootClientId, hasAppender, hasCustomAppender ]
- );
+ }
+
+ const selectedBlockClientId = getSelectedBlockClientId();
+ return {
+ order: _order,
+ selectedBlocks: getSelectedBlockClientIds(),
+ visibleBlocks: __unstableGetVisibleBlocks(),
+ isZoomOut: __unstableGetEditorMode() === 'zoom-out',
+ shouldRenderAppender:
+ hasAppender &&
+ __unstableGetEditorMode() !== 'zoom-out' &&
+ ( hasCustomAppender
+ ? ! getTemplateLock( rootClientId ) &&
+ getBlockEditingMode( rootClientId ) !== 'disabled'
+ : rootClientId === selectedBlockClientId ||
+ ( ! rootClientId &&
+ ! selectedBlockClientId &&
+ ! _order.length ) ),
+ };
+ },
+ [ rootClientId, hasAppender, hasCustomAppender ]
+ );
return (
@@ -230,10 +237,24 @@ function Items( {
! selectedBlocks.includes( clientId )
}
>
+ { isZoomOut && (
+
+ ) }
+ { isZoomOut && (
+
+ ) }
) ) }
{ order.length < 1 && placeholder }
diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js
index 15fb83139237cc..c3a279a618b5da 100644
--- a/packages/block-editor/src/components/block-list/use-block-props/index.js
+++ b/packages/block-editor/src/components/block-list/use-block-props/index.js
@@ -26,6 +26,7 @@ import {
import { useFocusHandler } from './use-focus-handler';
import { useEventHandlers } from './use-selected-block-event-handlers';
import { useNavModeExit } from './use-nav-mode-exit';
+import { useZoomOutModeExit } from './use-zoom-out-mode-exit';
import { useBlockRefProvider } from './use-block-refs';
import { useIntersectionObserver } from './use-intersection-observer';
import { useScrollIntoView } from './use-scroll-into-view';
@@ -85,6 +86,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
name,
blockApiVersion,
blockTitle,
+ editorMode,
isSelected,
isSubtreeDisabled,
hasOverlay,
@@ -115,6 +117,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
useFocusHandler( clientId ),
useEventHandlers( { clientId, isSelected } ),
useNavModeExit( clientId ),
+ useZoomOutModeExit( { editorMode } ),
useIsHovered( { clientId } ),
useIntersectionObserver(),
useMovingAnimation( { triggerAnimationOnChange: index, clientId } ),
diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js b/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js
new file mode 100644
index 00000000000000..bb6edd066f06f1
--- /dev/null
+++ b/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js
@@ -0,0 +1,44 @@
+/**
+ * WordPress dependencies
+ */
+import { useDispatch } from '@wordpress/data';
+import { useRefEffect } from '@wordpress/compose';
+
+/**
+ * Internal dependencies
+ */
+import { store as blockEditorStore } from '../../../store';
+import { unlock } from '../../../lock-unlock';
+
+/**
+ * Allows Zoom Out mode to be exited by double clicking in the selected block.
+ *
+ * @param {string} clientId Block client ID.
+ */
+export function useZoomOutModeExit( { editorMode } ) {
+ const { __unstableSetEditorMode } = unlock(
+ useDispatch( blockEditorStore )
+ );
+
+ return useRefEffect(
+ ( node ) => {
+ if ( editorMode !== 'zoom-out' ) {
+ return;
+ }
+
+ function onDoubleClick( event ) {
+ if ( ! event.defaultPrevented ) {
+ event.preventDefault();
+ __unstableSetEditorMode( 'edit' );
+ }
+ }
+
+ node.addEventListener( 'dblclick', onDoubleClick );
+
+ return () => {
+ node.removeEventListener( 'dblclick', onDoubleClick );
+ };
+ },
+ [ editorMode, __unstableSetEditorMode ]
+ );
+}
diff --git a/packages/block-editor/src/components/block-list/zoom-out-separator.js b/packages/block-editor/src/components/block-list/zoom-out-separator.js
new file mode 100644
index 00000000000000..be5af549630607
--- /dev/null
+++ b/packages/block-editor/src/components/block-list/zoom-out-separator.js
@@ -0,0 +1,110 @@
+/**
+ * External dependencies
+ */
+import clsx from 'clsx';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ __unstableMotion as motion,
+ __unstableAnimatePresence as AnimatePresence,
+} from '@wordpress/components';
+import { useReducedMotion } from '@wordpress/compose';
+import { useSelect } from '@wordpress/data';
+import { useState } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { store as blockEditorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
+
+export function ZoomOutSeparator( {
+ clientId,
+ rootClientId = '',
+ position = 'top',
+} ) {
+ const [ isDraggedOver, setIsDraggedOver ] = useState( false );
+ const {
+ sectionRootClientId,
+ sectionClientIds,
+ blockInsertionPoint,
+ blockInsertionPointVisible,
+ } = useSelect( ( select ) => {
+ const {
+ getBlockInsertionPoint,
+ getBlockOrder,
+ isBlockInsertionPointVisible,
+ getSectionRootClientId,
+ } = unlock( select( blockEditorStore ) );
+
+ const root = getSectionRootClientId();
+ const sectionRootClientIds = getBlockOrder( root );
+ return {
+ sectionRootClientId: root,
+ sectionClientIds: sectionRootClientIds,
+ blockOrder: getBlockOrder( root ),
+ blockInsertionPoint: getBlockInsertionPoint(),
+ blockInsertionPointVisible: isBlockInsertionPointVisible(),
+ };
+ }, [] );
+
+ const isReducedMotion = useReducedMotion();
+
+ if ( ! clientId ) {
+ return;
+ }
+
+ let isVisible = false;
+
+ const isSectionBlock =
+ rootClientId === sectionRootClientId &&
+ sectionClientIds &&
+ sectionClientIds.includes( clientId );
+
+ if ( ! isSectionBlock ) {
+ return null;
+ }
+
+ if ( position === 'top' ) {
+ isVisible =
+ blockInsertionPointVisible &&
+ blockInsertionPoint.index === 0 &&
+ clientId === sectionClientIds[ blockInsertionPoint.index ];
+ }
+
+ if ( position === 'bottom' ) {
+ isVisible =
+ blockInsertionPointVisible &&
+ clientId === sectionClientIds[ blockInsertionPoint.index - 1 ];
+ }
+
+ return (
+
+ { isVisible && (
+ setIsDraggedOver( true ) }
+ onDragLeave={ () => setIsDraggedOver( false ) }
+ >
+ ) }
+
+ );
+}
diff --git a/packages/block-editor/src/components/block-lock/style.scss b/packages/block-editor/src/components/block-lock/style.scss
index 8dc6bfb2021f08..ad59030a8f4405 100644
--- a/packages/block-editor/src/components/block-lock/style.scss
+++ b/packages/block-editor/src/components/block-lock/style.scss
@@ -41,7 +41,7 @@
&:hover {
background-color: $gray-100;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
}
}
diff --git a/packages/block-editor/src/components/block-mover/button.js b/packages/block-editor/src/components/block-mover/button.js
index 98c0aff79eff38..76b46701758e8d 100644
--- a/packages/block-editor/src/components/block-mover/button.js
+++ b/packages/block-editor/src/components/block-mover/button.js
@@ -129,8 +129,7 @@ const BlockMoverButton = forwardRef(
return (
<>
{ ( draggableProps ) => (
{
- const compositeStore = useCompositeStore();
const containerClass = 'block-editor-block-pattern-setup__container';
if ( viewMode === VIEWMODES.carousel ) {
@@ -65,7 +54,6 @@ const SetupContent = ( {
return (
-
) }
-
+
);
}
diff --git a/packages/block-editor/src/components/block-pattern-setup/style.scss b/packages/block-editor/src/components/block-pattern-setup/style.scss
index 3474eed5be5176..10582a7a2ce496 100644
--- a/packages/block-editor/src/components/block-pattern-setup/style.scss
+++ b/packages/block-editor/src/components/block-pattern-setup/style.scss
@@ -4,7 +4,7 @@
justify-content: center;
align-items: flex-start;
width: 100%;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
&.view-mode-grid {
padding-top: $grid-unit-05;
@@ -60,7 +60,7 @@
.block-editor-block-preview__container {
min-height: 100px;
- border-radius: $radius-block-ui;
+ border-radius: $radius-medium;
border: $border-width solid $gray-300;
}
diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js
index 47deb777a84aab..2609cc2db97a13 100644
--- a/packages/block-editor/src/components/block-patterns-list/index.js
+++ b/packages/block-editor/src/components/block-patterns-list/index.js
@@ -9,9 +9,9 @@ import clsx from 'clsx';
import { cloneBlock } from '@wordpress/blocks';
import { useEffect, useState, forwardRef, useMemo } from '@wordpress/element';
import {
+ Composite,
VisuallyHidden,
Tooltip,
- privateApis as componentsPrivateApis,
__experimentalHStack as HStack,
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
@@ -21,16 +21,11 @@ import { Icon, symbol } from '@wordpress/icons';
/**
* Internal dependencies
*/
-import { unlock } from '../../lock-unlock';
import BlockPreview from '../block-preview';
import InserterDraggableBlocks from '../inserter-draggable-blocks';
import BlockPatternsPaging from '../block-patterns-paging';
import { INSERTER_PATTERN_TYPES } from '../inserter/block-patterns-tab/utils';
-const { CompositeV2: Composite, CompositeItemV2: CompositeItem } = unlock(
- componentsPrivateApis
-);
-
const WithToolTip = ( { showTooltip, title, children } ) => {
if ( showTooltip ) {
return { children } ;
@@ -105,7 +100,7 @@ function BlockPattern( {
}
title={ pattern.title }
>
-
) }
-
+
) }
diff --git a/packages/block-editor/src/components/block-patterns-list/style.scss b/packages/block-editor/src/components/block-patterns-list/style.scss
index 6b23c1e844dad9..84e95563737c3c 100644
--- a/packages/block-editor/src/components/block-patterns-list/style.scss
+++ b/packages/block-editor/src/components/block-patterns-list/style.scss
@@ -36,12 +36,12 @@
display: flex;
align-items: center;
overflow: hidden;
- border-radius: $radius-block-ui;
+ border-radius: $radius-medium;
&::after {
outline: $border-width solid rgba($black, 0.1);
outline-offset: -$border-width;
- border-radius: $radius-block-ui;
+ border-radius: $radius-medium;
}
}
diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js
index c8ee422091ad1f..98e7f7b2d21420 100644
--- a/packages/block-editor/src/components/block-switcher/index.js
+++ b/packages/block-editor/src/components/block-switcher/index.js
@@ -198,6 +198,7 @@ const BlockIndicator = ( { icon, showTitle, blockTitle } ) => (
export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
const {
+ hasContentOnlyLocking,
canRemove,
hasBlockStyles,
icon,
@@ -206,8 +207,12 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
isTemplate,
} = useSelect(
( select ) => {
- const { getBlocksByClientId, getBlockAttributes, canRemoveBlocks } =
- select( blockEditorStore );
+ const {
+ getTemplateLock,
+ getBlocksByClientId,
+ getBlockAttributes,
+ canRemoveBlocks,
+ } = select( blockEditorStore );
const { getBlockStyles, getBlockType, getActiveBlockVariation } =
select( blocksStore );
const _blocks = getBlocksByClientId( clientIds );
@@ -219,6 +224,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
const blockType = getBlockType( firstBlockName );
let _icon;
+ let _hasTemplateLock;
if ( _isSingleBlockSelected ) {
const match = getActiveBlockVariation(
firstBlockName,
@@ -226,9 +232,14 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
);
// Take into account active block variations.
_icon = match?.icon || blockType.icon;
+ _hasTemplateLock =
+ getTemplateLock( clientIds[ 0 ] ) === 'contentOnly';
} else {
const isSelectionOfSameType =
new Set( _blocks.map( ( { name } ) => name ) ).size === 1;
+ _hasTemplateLock = clientIds.some(
+ ( id ) => getTemplateLock( id ) === 'contentOnly'
+ );
// When selection consists of blocks of multiple types, display an
// appropriate icon to communicate the non-uniformity.
_icon = isSelectionOfSameType ? blockType.icon : copy;
@@ -244,6 +255,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
_isSingleBlockSelected && isReusableBlock( _blocks[ 0 ] ),
isTemplate:
_isSingleBlockSelected && isTemplatePart( _blocks[ 0 ] ),
+ hasContentOnlyLocking: _hasTemplateLock,
};
},
[ clientIds ]
@@ -252,6 +264,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
clientId: clientIds?.[ 0 ],
maximumLength: 35,
} );
+
if ( invalidBlocks ) {
return null;
}
@@ -261,7 +274,10 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
? blockTitle
: __( 'Multiple blocks selected' );
- const hideDropdown = disabled || ( ! hasBlockStyles && ! canRemove );
+ const hideDropdown =
+ disabled ||
+ ( ! hasBlockStyles && ! canRemove ) ||
+ hasContentOnlyLocking;
if ( hideDropdown ) {
return (
diff --git a/packages/block-editor/src/components/block-switcher/pattern-transformations-menu.js b/packages/block-editor/src/components/block-switcher/pattern-transformations-menu.js
index 46fd83c92d91f6..7d66e19a214a28 100644
--- a/packages/block-editor/src/components/block-switcher/pattern-transformations-menu.js
+++ b/packages/block-editor/src/components/block-switcher/pattern-transformations-menu.js
@@ -7,11 +7,11 @@ import { useInstanceId, useViewportMatch } from '@wordpress/compose';
import { chevronRight } from '@wordpress/icons';
import {
+ Composite,
MenuGroup,
MenuItem,
Popover,
VisuallyHidden,
- privateApis as componentsPrivateApis,
} from '@wordpress/components';
/**
@@ -19,13 +19,6 @@ import {
*/
import BlockPreview from '../block-preview';
import useTransformedPatterns from './use-transformed-patterns';
-import { unlock } from '../../lock-unlock';
-
-const {
- CompositeV2: Composite,
- CompositeItemV2: CompositeItem,
- useCompositeStoreV2: useCompositeStore,
-} = unlock( componentsPrivateApis );
function PatternTransformationsMenu( {
blocks,
@@ -82,10 +75,8 @@ function PreviewPatternsPopover( { patterns, onSelect } ) {
}
function BlockPatternsList( { patterns, onSelect } ) {
- const composite = useCompositeStore();
return (
-
{ pattern.title }
-
+
{ !! pattern.description && (
{ pattern.description }
diff --git a/packages/block-editor/src/components/block-switcher/style.scss b/packages/block-editor/src/components/block-switcher/style.scss
index 287afaed4055c6..823a656668a621 100644
--- a/packages/block-editor/src/components/block-switcher/style.scss
+++ b/packages/block-editor/src/components/block-switcher/style.scss
@@ -99,7 +99,7 @@
width: 300px;
border: $border-width solid $gray-900;
background: $white;
- border-radius: $radius-block-ui;
+ border-radius: $radius-medium;
outline: none;
box-shadow: none;
overflow: auto;
@@ -161,7 +161,7 @@
.block-editor-block-switcher__preview-patterns-container-list__item {
height: 100%;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
transition: all 0.05s ease-in-out;
position: relative;
border: $border-width solid transparent;
diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js
index 13dfd6b33e56a6..6c4789cb2924f2 100644
--- a/packages/block-editor/src/components/block-toolbar/index.js
+++ b/packages/block-editor/src/components/block-toolbar/index.js
@@ -65,6 +65,8 @@ export function PrivateBlockToolbar( {
shouldShowVisualToolbar,
showParentSelector,
isUsingBindings,
+ hasParentPattern,
+ hasContentOnlyLocking,
} = useSelect( ( select ) => {
const {
getBlockName,
@@ -74,6 +76,8 @@ export function PrivateBlockToolbar( {
isBlockValid,
getBlockEditingMode,
getBlockAttributes,
+ getBlockParentsByBlockName,
+ getTemplateLock,
} = select( blockEditorStore );
const selectedBlockClientIds = getSelectedBlockClientIds();
const selectedBlockClientId = selectedBlockClientIds[ 0 ];
@@ -94,6 +98,17 @@ export function PrivateBlockToolbar( {
( clientId ) =>
!! getBlockAttributes( clientId )?.metadata?.bindings
);
+
+ const _hasParentPattern = selectedBlockClientIds.every(
+ ( clientId ) =>
+ getBlockParentsByBlockName( clientId, 'core/block', true )
+ .length > 0
+ );
+
+ // If one or more selected blocks are locked, do not show the BlockGroupToolbar.
+ const _hasTemplateLock = selectedBlockClientIds.some(
+ ( id ) => getTemplateLock( id ) === 'contentOnly'
+ );
return {
blockClientId: selectedBlockClientId,
blockClientIds: selectedBlockClientIds,
@@ -113,6 +128,8 @@ export function PrivateBlockToolbar( {
selectedBlockClientIds.length === 1 &&
_isDefaultEditingMode,
isUsingBindings: _isUsingBindings,
+ hasParentPattern: _hasParentPattern,
+ hasContentOnlyLocking: _hasTemplateLock,
};
}, [] );
@@ -167,7 +184,7 @@ export function PrivateBlockToolbar( {
isDefaultEditingMode && }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
( isDefaultEditingMode ||
- isContentOnlyEditingMode ||
+ ( isContentOnlyEditingMode && ! hasParentPattern ) ||
isSynced ) && (
) }
- { shouldShowVisualToolbar && isMultiToolbar && (
-
- ) }
+ { ! hasContentOnlyLocking &&
+ shouldShowVisualToolbar &&
+ isMultiToolbar && }
{ shouldShowVisualToolbar && (
<>
- { ! isTyping && (
+ { ! isTyping && ! isZoomOutMode && (
diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss
index a37271e609b179..9f1325d7f95a1a 100644
--- a/packages/block-editor/src/components/block-tools/style.scss
+++ b/packages/block-editor/src/components/block-tools/style.scss
@@ -63,7 +63,6 @@
.block-editor-inserter__toggle.components-button.has-icon {
// Basic look
background: $gray-900;
- border-radius: $radius-block-ui;
color: $white;
padding: 0;
@@ -95,7 +94,7 @@
z-index: z-index(".block-editor-block-list__block-selection-button");
// Dark block UI appearance.
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
background-color: $gray-900;
font-size: $default-font-size;
@@ -188,7 +187,7 @@
.block-editor-block-contextual-toolbar {
border: $border-width solid $gray-900;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
overflow: visible; // allow the parent selector to be visible
position: static;
width: auto;
diff --git a/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js
index d15ee34714f5de..79f8be3f9cfe97 100644
--- a/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js
+++ b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js
@@ -10,7 +10,7 @@ import { useEffect, useState } from '@wordpress/element';
import BlockPopoverInbetween from '../block-popover/inbetween';
import ZoomOutModeInserterButton from './zoom-out-mode-inserter-button';
import { store as blockEditorStore } from '../../store';
-import { sectionRootClientIdKey } from '../../store/private-keys';
+import { unlock } from '../../lock-unlock';
function ZoomOutModeInserters() {
const [ isReady, setIsReady ] = useState( false );
@@ -32,8 +32,11 @@ function ZoomOutModeInserters() {
getSelectedBlockClientId,
getHoveredBlockClientId,
isBlockInsertionPointVisible,
- } = select( blockEditorStore );
- const { [ sectionRootClientIdKey ]: root } = getSettings();
+ getSectionRootClientId,
+ } = unlock( select( blockEditorStore ) );
+
+ const root = getSectionRootClientId();
+
return {
hasSelection: !! getSelectionStart().clientId,
blockInsertionPoint: getBlockInsertionPoint(),
@@ -85,18 +88,6 @@ function ZoomOutModeInserters() {
previousClientId={ previousClientId }
nextClientId={ nextClientId }
>
- { shouldRenderInsertionPoint && (
-
- ) }
{ ! shouldRenderInsertionPoint && (
{
- let label;
- if ( hasSingleBlockType ) {
- label = sprintf(
- // translators: %s: the name of the block when there is only one
- _x( 'Add %s', 'directly add the only allowed block' ),
- blockTitle
- );
- } else {
- label = _x(
- 'Add block',
- 'Generic label for block inserter button'
- );
- }
const isToggleButton = ! hasSingleBlockType;
+ const label = hasSingleBlockType
+ ? sprintf(
+ // translators: %s: the name of the block when there is only one
+ _x(
+ 'Add %s',
+ 'directly add the only allowed block'
+ ),
+ blockTitle
+ )
+ : _x(
+ 'Add block',
+ 'Generic label for block inserter button'
+ );
- let inserterButton = (
+ return (
- { ! hasSingleBlockType && (
- { label }
- ) }
);
-
- if ( isToggleButton || hasSingleBlockType ) {
- inserterButton = (
- { inserterButton }
- );
- }
- return inserterButton;
} }
isAppender
/>
diff --git a/packages/block-editor/src/components/colors-gradients/style.scss b/packages/block-editor/src/components/colors-gradients/style.scss
index b3539637a9904c..fc1b1a4d469033 100644
--- a/packages/block-editor/src/components/colors-gradients/style.scss
+++ b/packages/block-editor/src/components/colors-gradients/style.scss
@@ -73,15 +73,15 @@ $swatch-gap: 12px;
// Identify the first visible instance as placeholder items will not have this class.
&:nth-child(1 of &) {
margin-top: $grid-unit-30;
- border-top-left-radius: $radius-block-ui;
- border-top-right-radius: $radius-block-ui;
+ border-top-left-radius: $radius-small;
+ border-top-right-radius: $radius-small;
border-top: 1px solid $gray-300;
}
// Identify the last visible instance as placeholder items will not have this class.
&:nth-last-child(1 of &) {
- border-bottom-left-radius: $radius-block-ui;
- border-bottom-right-radius: $radius-block-ui;
+ border-bottom-left-radius: $radius-small;
+ border-bottom-right-radius: $radius-small;
}
> div,
diff --git a/packages/block-editor/src/components/default-block-appender/content.scss b/packages/block-editor/src/components/default-block-appender/content.scss
index 77725d2508ec5e..51e0b4381a15d5 100644
--- a/packages/block-editor/src/components/default-block-appender/content.scss
+++ b/packages/block-editor/src/components/default-block-appender/content.scss
@@ -48,7 +48,6 @@
.block-editor-inserter__toggle.components-button.has-icon {
// Basic look
background: $gray-900;
- border-radius: $radius-block-ui;
color: $white;
padding: 0;
diff --git a/packages/block-editor/src/components/global-styles/shadow-panel-components.js b/packages/block-editor/src/components/global-styles/shadow-panel-components.js
index e3a6c71adf8fc6..1ed367e3c3ad0e 100644
--- a/packages/block-editor/src/components/global-styles/shadow-panel-components.js
+++ b/packages/block-editor/src/components/global-styles/shadow-panel-components.js
@@ -10,7 +10,7 @@ import {
Button,
FlexItem,
Dropdown,
- privateApis as componentsPrivateApis,
+ Composite,
} from '@wordpress/components';
import { useMemo } from '@wordpress/element';
import { shadow as shadowIcon, Icon, check } from '@wordpress/icons';
@@ -20,11 +20,6 @@ import { shadow as shadowIcon, Icon, check } from '@wordpress/icons';
*/
import clsx from 'clsx';
-/**
- * Internal dependencies
- */
-import { unlock } from '../../lock-unlock';
-
/**
* Shared reference to an empty array for cases where it is important to avoid
* returning a new array reference on every invocation.
@@ -32,11 +27,6 @@ import { unlock } from '../../lock-unlock';
* @type {Array}
*/
const EMPTY_ARRAY = [];
-const {
- CompositeItemV2: CompositeItem,
- CompositeV2: Composite,
- useCompositeStoreV2: useCompositeStore,
-} = unlock( componentsPrivateApis );
export function ShadowPopoverContainer( { shadow, onShadowChange, settings } ) {
const shadows = useShadowPresets( settings );
@@ -66,10 +56,8 @@ export function ShadowPopoverContainer( { shadow, onShadowChange, settings } ) {
}
export function ShadowPresets( { presets, activeShadow, onSelect } ) {
- const compositeStore = useCompositeStore();
return ! presets ? null : (
{
nearestFontWeight: '400',
},
},
+ {
+ message:
+ 'should return nearest fontStyle and fontWeight for normal/400 when fontFamilyFaces contain numerical fontWeight value',
+ fontFamilyFaces: [
+ {
+ fontFamily: 'IBM Plex Mono',
+ fontStyle: 'normal',
+ fontWeight: 400,
+ src: [
+ 'file:./assets/fonts/ibm-plex-mono/IBMPlexMono-Regular.woff2',
+ ],
+ },
+ {
+ fontFamily: 'IBM Plex Mono',
+ fontStyle: 'italic',
+ fontWeight: '400',
+ src: [
+ 'file:./assets/fonts/ibm-plex-mono/IBMPlexMono-Italic.woff2',
+ ],
+ },
+ {
+ fontFamily: 'IBM Plex Mono',
+ fontStyle: 'normal',
+ fontWeight: '700',
+ src: [
+ 'file:./assets/fonts/ibm-plex-mono/IBMPlexMono-Bold.woff2',
+ ],
+ },
+ ],
+ fontStyle: 'normal',
+ fontWeight: '400',
+ expected: {
+ nearestFontStyle: 'normal',
+ nearestFontWeight: '400',
+ },
+ },
+ {
+ message:
+ 'should return nearest fontStyle and fontWeight for normal/400 when fontFamilyFaces contain undefined fontWeight value',
+ fontFamilyFaces: [
+ {
+ fontFamily: 'IBM Plex Mono',
+ fontStyle: 'normal',
+ src: [
+ 'file:./assets/fonts/ibm-plex-mono/IBMPlexMono-Regular.woff2',
+ ],
+ },
+ ],
+ fontStyle: 'normal',
+ fontWeight: '400',
+ expected: {
+ nearestFontStyle: 'normal',
+ nearestFontWeight: '700',
+ },
+ },
].forEach(
( {
message,
diff --git a/packages/block-editor/src/components/global-styles/typography-utils.js b/packages/block-editor/src/components/global-styles/typography-utils.js
index 32fbd50db2dbff..4b7c90ae4f222c 100644
--- a/packages/block-editor/src/components/global-styles/typography-utils.js
+++ b/packages/block-editor/src/components/global-styles/typography-utils.js
@@ -174,6 +174,10 @@ export function findNearestFontWeight(
availableFontWeights,
newFontWeightValue
) {
+ newFontWeightValue =
+ 'number' === typeof newFontWeightValue
+ ? newFontWeightValue.toString()
+ : newFontWeightValue;
if ( ! newFontWeightValue || typeof newFontWeightValue !== 'string' ) {
return '';
}
@@ -260,7 +264,7 @@ export function findNearestStyleAndWeight(
( { value: fs } ) => fs === fontStyle
);
const hasFontWeight = fontWeights?.some(
- ( { value: fw } ) => fw === fontWeight
+ ( { value: fw } ) => fw?.toString() === fontWeight?.toString()
);
if ( ! hasFontStyle ) {
diff --git a/packages/block-editor/src/components/grid/style.scss b/packages/block-editor/src/components/grid/style.scss
index 6790d683ca7d03..3a04eb006e7910 100644
--- a/packages/block-editor/src/components/grid/style.scss
+++ b/packages/block-editor/src/components/grid/style.scss
@@ -117,7 +117,7 @@
content: "";
position: absolute;
display: block;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
height: $grid-unit-40;
// Position the focus rectangle.
diff --git a/packages/block-editor/src/components/iframe/get-compatibility-styles.js b/packages/block-editor/src/components/iframe/get-compatibility-styles.js
index ab80e0e8eb43ea..0eae82b11e01f8 100644
--- a/packages/block-editor/src/components/iframe/get-compatibility-styles.js
+++ b/packages/block-editor/src/components/iframe/get-compatibility-styles.js
@@ -40,7 +40,12 @@ export function getCompatibilityStyles() {
// Don't try to add the reset styles, which were removed as a dependency
// from `edit-blocks` for the iframe since we don't need to reset admin
// styles.
- if ( ownerNode.id === 'wp-reset-editor-styles-css' ) {
+ if (
+ [
+ 'wp-reset-editor-styles-css',
+ 'wp-reset-editor-styles-rtl-css',
+ ].includes( ownerNode.id )
+ ) {
return accumulator;
}
diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js
index 9702044aeca6b0..0d35b459190fef 100644
--- a/packages/block-editor/src/components/inner-blocks/index.js
+++ b/packages/block-editor/src/components/inner-blocks/index.js
@@ -30,7 +30,6 @@ import useBlockSync from '../provider/use-block-sync';
import { store as blockEditorStore } from '../../store';
import useBlockDropZone from '../use-block-drop-zone';
import { unlock } from '../../lock-unlock';
-import { sectionRootClientIdKey } from '../../store/private-keys';
const EMPTY_OBJECT = {};
@@ -204,7 +203,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) {
getBlockEditingMode,
getBlockSettings,
isDragging,
- getSettings,
+ getSectionRootClientId,
} = unlock( select( blockEditorStore ) );
let _isDropZoneDisabled;
@@ -226,8 +225,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) {
// In zoom out mode, we want to disable the drop zone for the sections.
// The inner blocks belonging to the section drop zone is
// already disabled by the blocks themselves being disabled.
- const { [ sectionRootClientIdKey ]: sectionRootClientId } =
- getSettings();
+ const sectionRootClientId = getSectionRootClientId();
_isDropZoneDisabled = clientId !== sectionRootClientId;
}
diff --git a/packages/block-editor/src/components/inserter-list-item/style.scss b/packages/block-editor/src/components/inserter-list-item/style.scss
index f91e4365db30ce..435f60ed9e2f14 100644
--- a/packages/block-editor/src/components/inserter-list-item/style.scss
+++ b/packages/block-editor/src/components/inserter-list-item/style.scss
@@ -43,7 +43,6 @@
cursor: pointer;
background: transparent;
word-break: break-word;
- border-radius: $radius-block-ui;
transition: all 0.05s ease-in-out;
@include reduce-motion("transition");
position: relative;
@@ -72,7 +71,7 @@
bottom: 0;
left: 0;
right: 0;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
opacity: 0.04;
background: var(--wp-admin-theme-color);
// This fixes drag-and-drop in Firefox.
@@ -97,7 +96,6 @@
.block-editor-block-types-list__item-icon {
padding: 12px 20px;
- border-radius: $radius-block-ui;
color: $gray-900;
transition: all 0.05s ease-in-out;
@include reduce-motion("transition");
diff --git a/packages/block-editor/src/components/inserter-listbox/index.js b/packages/block-editor/src/components/inserter-listbox/index.js
index 6af26a1d746bfb..bc833b0e4c36b1 100644
--- a/packages/block-editor/src/components/inserter-listbox/index.js
+++ b/packages/block-editor/src/components/inserter-listbox/index.js
@@ -1,28 +1,19 @@
/**
* WordPress dependencies
*/
-import { privateApis as componentsPrivateApis } from '@wordpress/components';
+import { Composite } from '@wordpress/components';
/**
* Internal dependencies
*/
-import { unlock } from '../../lock-unlock';
export { default as InserterListboxGroup } from './group';
export { default as InserterListboxRow } from './row';
export { default as InserterListboxItem } from './item';
-const { CompositeV2: Composite, useCompositeStoreV2: useCompositeStore } =
- unlock( componentsPrivateApis );
-
function InserterListbox( { children } ) {
- const store = useCompositeStore( {
- focusShift: true,
- focusWrap: 'horizontal',
- } );
-
return (
- > }>
+ > }>
{ children }
);
diff --git a/packages/block-editor/src/components/inserter-listbox/item.js b/packages/block-editor/src/components/inserter-listbox/item.js
index 825c3fdfc353c6..69c316bdbbccef 100644
--- a/packages/block-editor/src/components/inserter-listbox/item.js
+++ b/packages/block-editor/src/components/inserter-listbox/item.js
@@ -1,28 +1,18 @@
/**
* WordPress dependencies
*/
-import {
- Button,
- privateApis as componentsPrivateApis,
-} from '@wordpress/components';
+import { Button, Composite } from '@wordpress/components';
import { forwardRef } from '@wordpress/element';
-/**
- * Internal dependencies
- */
-import { unlock } from '../../lock-unlock';
-
-const { CompositeItemV2: CompositeItem } = unlock( componentsPrivateApis );
-
function InserterListboxItem(
{ isFirst, as: Component, children, ...props },
ref
) {
return (
- ;
+ return ;
}
export default forwardRef( InserterListboxRow );
diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js
index 3703381b23a140..61716f616dafa9 100644
--- a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js
+++ b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js
@@ -17,6 +17,7 @@ import {
__experimentalText as Text,
FlexBlock,
} from '@wordpress/components';
+import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
@@ -32,6 +33,7 @@ import {
myPatternsCategory,
INSERTER_PATTERN_TYPES,
} from './utils';
+import { store as blockEditorStore } from '../../../store';
const noop = () => {};
@@ -42,6 +44,11 @@ export function PatternCategoryPreviews( {
category,
showTitlesAsTooltip,
} ) {
+ const isZoomOutMode = useSelect(
+ ( select ) =>
+ select( blockEditorStore ).__unstableGetEditorMode() === 'zoom-out',
+ []
+ );
const [ allPatterns, , onClickPattern ] = usePatternsState(
onInsert,
rootClientId,
@@ -163,22 +170,32 @@ export function PatternCategoryPreviews( {
) }
-
{ currentCategoryPatterns.length > 0 && (
-
+ <>
+ { isZoomOutMode && (
+
+ { __( 'Drag and drop patterns into the canvas.' ) }
+
+ ) }
+
+ >
) }
>
);
diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js
index d3e7dba0df6838..1af81d0231a1a8 100644
--- a/packages/block-editor/src/components/inserter/index.js
+++ b/packages/block-editor/src/components/inserter/index.js
@@ -9,7 +9,7 @@ import clsx from 'clsx';
import { speak } from '@wordpress/a11y';
import { __, _x, sprintf } from '@wordpress/i18n';
import { Dropdown, Button } from '@wordpress/components';
-import { forwardRef, Component } from '@wordpress/element';
+import { Component } from '@wordpress/element';
import { withDispatch, withSelect } from '@wordpress/data';
import { compose, ifCondition } from '@wordpress/compose';
import { createBlock, store as blocksStore } from '@wordpress/blocks';
@@ -76,7 +76,7 @@ const defaultRenderToggle = ( {
);
};
-class PrivateInserter extends Component {
+class Inserter extends Component {
constructor() {
super( ...arguments );
@@ -222,7 +222,7 @@ class PrivateInserter extends Component {
}
}
-export const ComposedPrivateInserter = compose( [
+export default compose( [
withSelect(
( select, { clientId, rootClientId, shouldDirectInsert = true } ) => {
const {
@@ -418,10 +418,4 @@ export const ComposedPrivateInserter = compose( [
( { hasItems, isAppender, rootClientId, clientId } ) =>
hasItems || ( ! isAppender && ! rootClientId && ! clientId )
),
-] )( PrivateInserter );
-
-const Inserter = forwardRef( ( props, ref ) => {
- return ;
-} );
-
-export default Inserter;
+] )( Inserter );
diff --git a/packages/block-editor/src/components/inserter/media-tab/media-list.js b/packages/block-editor/src/components/inserter/media-tab/media-list.js
index bfc858bc8c4de7..73f38cfc306fbc 100644
--- a/packages/block-editor/src/components/inserter/media-tab/media-list.js
+++ b/packages/block-editor/src/components/inserter/media-tab/media-list.js
@@ -1,17 +1,13 @@
/**
* WordPress dependencies
*/
-import { privateApis as componentsPrivateApis } from '@wordpress/components';
+import { Composite } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { MediaPreview } from './media-preview';
-import { unlock } from '../../../lock-unlock';
-
-const { CompositeV2: Composite, useCompositeStoreV2: useCompositeStore } =
- unlock( componentsPrivateApis );
function MediaList( {
mediaList,
@@ -19,10 +15,8 @@ function MediaList( {
onClick,
label = __( 'Media List' ),
} ) {
- const compositeStore = useCompositeStore();
return (
-
) }
-
+
{ ! isInserting && (
{};
function InserterMenu(
@@ -102,12 +101,12 @@ function InserterMenu(
window.requestAnimationFrame( () => {
if (
! shouldFocusBlock &&
- ! blockTypesTabRef?.current.contains(
+ ! blockTypesTabRef.current?.contains(
ref.current.ownerDocument.activeElement
)
) {
// There has been a focus loss, so focus the first button in the block types tab
- blockTypesTabRef?.current.querySelector( 'button' ).focus();
+ blockTypesTabRef.current?.querySelector( 'button' ).focus();
}
} );
},
@@ -147,11 +146,6 @@ function InserterMenu(
const showMediaPanel = selectedTab === 'media' && !! selectedMediaCategory;
- const showZoomOut =
- showPatternPanel && !! window.__experimentalEnableZoomedOutPatternsTab;
-
- useZoomOut( showZoomOut );
-
const inserterSearch = useMemo( () => {
if ( selectedTab === 'media' ) {
return null;
diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js
index 0852c562828a4b..e28ae1f3896c45 100644
--- a/packages/block-editor/src/components/inserter/quick-inserter.js
+++ b/packages/block-editor/src/components/inserter/quick-inserter.js
@@ -9,7 +9,7 @@ import clsx from 'clsx';
import { useState, useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Button, SearchControl } from '@wordpress/components';
-import { useDispatch, useSelect } from '@wordpress/data';
+import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
@@ -82,8 +82,6 @@ export default function QuickInserter( {
}
}, [ setInserterIsOpened ] );
- const { showInsertionPoint } = useDispatch( blockEditorStore );
-
// When clicking Browse All select the appropriate block so as
// the insertion point can work as expected.
const onBrowseAll = () => {
@@ -93,7 +91,6 @@ export default function QuickInserter( {
filterValue,
onSelect,
} );
- showInsertionPoint( rootClientId, insertionIndex );
};
let maxBlockPatterns = 0;
diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss
index 21f8abfa59c9e8..289acecb5c1c33 100644
--- a/packages/block-editor/src/components/inserter/style.scss
+++ b/packages/block-editor/src/components/inserter/style.scss
@@ -44,12 +44,12 @@ $block-inserter-tabs-height: 44px;
&:first-child {
border-top: $border-width solid $gray-400;
- border-radius: $radius-block-ui $radius-block-ui 0 0;
+ border-radius: $radius-medium $radius-medium 0 0;
}
&:last-child {
border-bottom: $border-width solid $gray-400;
- border-radius: 0 0 $radius-block-ui $radius-block-ui;
+ border-radius: 0 0 $radius-medium $radius-medium;
}
&.components-button {
@@ -276,7 +276,7 @@ $block-inserter-tabs-height: 44px;
bottom: 0;
left: 0;
right: 0;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
opacity: 0.04;
background: var(--wp-admin-theme-color);
height: 100%;
@@ -359,7 +359,7 @@ $block-inserter-tabs-height: 44px;
min-height: $grid-unit-60 * 3;
color: $gray-700;
background: $gray-100;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
}
.block-editor-inserter__tips {
@@ -577,7 +577,6 @@ $block-inserter-tabs-height: 44px;
> button {
background: $white;
- border-radius: $radius-block-ui;
display: none;
// These styles are important so as focus isn't lost
@@ -611,7 +610,6 @@ $block-inserter-tabs-height: 44px;
max-width: 100%;
outline: $border-width solid rgba($black, 0.1);
outline-offset: -$border-width;
- border-radius: $radius-block-ui;
}
.block-editor-inserter__media-list__item-preview-spinner {
@@ -711,3 +709,7 @@ $block-inserter-tabs-height: 44px;
}
}
}
+
+.block-editor-tabbed-sidebar__tabpanel .block-editor-inserter__help-text {
+ padding: 0 $grid-unit-30 $grid-unit-20;
+}
diff --git a/packages/block-editor/src/components/inspector-controls-tabs/settings-tab.js b/packages/block-editor/src/components/inspector-controls-tabs/settings-tab.js
index ec34035b754a91..ea2f45114bf9cf 100644
--- a/packages/block-editor/src/components/inspector-controls-tabs/settings-tab.js
+++ b/packages/block-editor/src/components/inspector-controls-tabs/settings-tab.js
@@ -9,6 +9,7 @@ const SettingsTab = ( { showAdvancedControls = false } ) => (
<>
+
{ showAdvancedControls && (
diff --git a/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js b/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js
index ff68be82a829f1..6a80d47f024816 100644
--- a/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js
+++ b/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js
@@ -32,6 +32,7 @@ function getShowTabs( blockName, tabSettings = {} ) {
export default function useInspectorControlsTabs( blockName ) {
const tabs = [];
const {
+ bindings: bindingsGroup,
border: borderGroup,
color: colorGroup,
default: defaultGroup,
@@ -64,8 +65,10 @@ export default function useInspectorControlsTabs( blockName ) {
// (i.e. both list view and styles), check only the default and position
// InspectorControls slots. If we have multiple tabs, we'll need to check
// the advanced controls slot as well to ensure they are rendered.
- const advancedFills =
- useSlotFills( InspectorAdvancedControls.slotName ) || [];
+ const advancedFills = [
+ ...( useSlotFills( InspectorAdvancedControls.slotName ) || [] ),
+ ...( useSlotFills( bindingsGroup.Slot.__unstableName ) || [] ),
+ ];
const settingsFills = [
...( useSlotFills( defaultGroup.Slot.__unstableName ) || [] ),
diff --git a/packages/block-editor/src/components/inspector-controls/groups.js b/packages/block-editor/src/components/inspector-controls/groups.js
index 9ca1a72b9918a6..34ec49a5e1cb41 100644
--- a/packages/block-editor/src/components/inspector-controls/groups.js
+++ b/packages/block-editor/src/components/inspector-controls/groups.js
@@ -5,6 +5,7 @@ import { createSlotFill } from '@wordpress/components';
const InspectorControlsDefault = createSlotFill( 'InspectorControls' );
const InspectorControlsAdvanced = createSlotFill( 'InspectorAdvancedControls' );
+const InspectorControlsBindings = createSlotFill( 'InspectorControlsBindings' );
const InspectorControlsBackground = createSlotFill(
'InspectorControlsBackground'
);
@@ -26,6 +27,7 @@ const groups = {
default: InspectorControlsDefault,
advanced: InspectorControlsAdvanced,
background: InspectorControlsBackground,
+ bindings: InspectorControlsBindings,
border: InspectorControlsBorder,
color: InspectorControlsColor,
dimensions: InspectorControlsDimensions,
diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss
index 1390a285fe163e..b0a5f6af2651b3 100644
--- a/packages/block-editor/src/components/link-control/style.scss
+++ b/packages/block-editor/src/components/link-control/style.scss
@@ -78,7 +78,6 @@ $block-editor-link-control-number-of-actions: 1;
@include input-control;
display: block;
border: $border-width solid $gray-600;
- border-radius: $radius-block-ui;
height: $button-size-next-default-40px; // components do not properly support unstable-large yet.
margin: 0;
padding: $grid-unit-10 $button-size-next-default-40px $grid-unit-10 $grid-unit-20;
@@ -230,7 +229,7 @@ $block-editor-link-control-number-of-actions: 1;
background-color: $gray-100;
width: $grid-unit-40;
height: $grid-unit-40;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
}
.block-editor-link-control__search-item-icon {
@@ -252,7 +251,6 @@ $block-editor-link-control-number-of-actions: 1;
}
.block-editor-link-control__search-item-title {
- border-radius: $radius-block-ui;
line-height: 1.1;
diff --git a/packages/block-editor/src/components/list-view/style.scss b/packages/block-editor/src/components/list-view/style.scss
index 406053af89317c..3d25597d2af904 100644
--- a/packages/block-editor/src/components/list-view/style.scss
+++ b/packages/block-editor/src/components/list-view/style.scss
@@ -101,16 +101,16 @@
// Border radius for corners of the selected item.
&.is-first-selected td:first-child {
- border-top-left-radius: $radius-block-ui;
+ border-top-left-radius: $radius-small;
}
&.is-first-selected td:last-child {
- border-top-right-radius: $radius-block-ui;
+ border-top-right-radius: $radius-small;
}
&.is-last-selected td:first-child {
- border-bottom-left-radius: $radius-block-ui;
+ border-bottom-left-radius: $radius-small;
}
&.is-last-selected td:last-child {
- border-bottom-right-radius: $radius-block-ui;
+ border-bottom-right-radius: $radius-small;
}
&.is-branch-selected:not(.is-selected):not(.is-synced-branch) {
background: rgba(var(--wp-admin-theme-color--rgb), 0.04);
@@ -119,23 +119,23 @@
background: rgba(var(--wp-block-synced-color--rgb), 0.04);
}
&.is-branch-selected.is-first-selected td:first-child {
- border-top-left-radius: $radius-block-ui;
+ border-top-left-radius: $radius-small;
}
&.is-branch-selected.is-first-selected td:last-child {
- border-top-right-radius: $radius-block-ui;
+ border-top-right-radius: $radius-small;
}
&[data-expanded="false"] {
&.is-branch-selected.is-first-selected td:first-child {
- border-top-left-radius: $radius-block-ui;
+ border-top-left-radius: $radius-small;
}
&.is-branch-selected.is-first-selected td:last-child {
- border-top-right-radius: $radius-block-ui;
+ border-top-right-radius: $radius-small;
}
&.is-branch-selected.is-last-selected td:first-child {
- border-bottom-left-radius: $radius-block-ui;
+ border-bottom-left-radius: $radius-small;
}
&.is-branch-selected.is-last-selected td:last-child {
- border-bottom-right-radius: $radius-block-ui;
+ border-bottom-right-radius: $radius-small;
}
}
&.is-branch-selected:not(.is-selected) td {
@@ -214,7 +214,6 @@
height: $grid-unit-40;
padding: ($grid-unit-15 * 0.5) $grid-unit-05 ($grid-unit-15 * 0.5) 0;
text-align: left;
- border-radius: $radius-block-ui;
position: relative;
white-space: nowrap;
@@ -406,7 +405,7 @@
right: 0;
transform: translateY(-50%);
background: rgba($black, 0.1);
- border-radius: $radius-block-ui;
+ border-radius: $radius-x-small;
padding: 2px 6px;
max-width: 100%;
box-sizing: border-box;
@@ -429,10 +428,10 @@
background-size: cover;
width: 18px;
height: 18px;
- border-radius: $radius-block-ui;
+ border-radius: $radius-x-small;
&:not(:only-child) {
- box-shadow: 0 0 0 $radius-block-ui $white;
+ box-shadow: 0 0 0 $radius-small $white;
}
&:not(:first-child) {
@@ -442,7 +441,7 @@
&.is-selected .block-editor-list-view-block-select-button__image {
&:not(:only-child) {
- box-shadow: 0 0 0 $radius-block-ui var(--wp-admin-theme-color);
+ box-shadow: 0 0 0 $radius-small var(--wp-admin-theme-color);
}
}
}
diff --git a/packages/block-editor/src/components/media-placeholder/README.md b/packages/block-editor/src/components/media-placeholder/README.md
index 6cb00206d93d72..68fcf150c4edc4 100644
--- a/packages/block-editor/src/components/media-placeholder/README.md
+++ b/packages/block-editor/src/components/media-placeholder/README.md
@@ -198,9 +198,9 @@ Callback called when urls can be configured. No media insertion from url will be
### handleUpload
-When set to false the handling of the upload is left to the calling component.
+When the value is set to `false` or returned as `false`, the handling of the upload is left to the consumer component. The function signature accepts an array containing the files to be uploaded.
-- Type: `Boolean`
+- Type: `Boolean|Function`
- Required: No
- Default: `true`
- Platform: Web
diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js
index a5b4eb0bf19b3f..4d41289f324c0f 100644
--- a/packages/block-editor/src/components/media-placeholder/index.js
+++ b/packages/block-editor/src/components/media-placeholder/index.js
@@ -172,7 +172,10 @@ export function MediaPlaceholder( {
};
const onFilesUpload = ( files ) => {
- if ( ! handleUpload ) {
+ if (
+ ! handleUpload ||
+ ( typeof handleUpload === 'function' && ! handleUpload( files ) )
+ ) {
return onSelect( files );
}
onFilesPreUpload( files );
@@ -320,15 +323,15 @@ export function MediaPlaceholder( {
if ( isAudio ) {
instructions = __(
- 'Upload an audio file, pick one from your media library, or add one with a URL.'
+ 'Upload or drag an audio file here, or pick one from your library.'
);
} else if ( isImage ) {
instructions = __(
- 'Upload an image file, pick one from your media library, or add one with a URL.'
+ 'Upload or drag an image file here, or pick one from your library.'
);
} else if ( isVideo ) {
instructions = __(
- 'Upload a video file, pick one from your media library, or add one with a URL.'
+ 'Upload or drag a video file here, or pick one from your library.'
);
}
}
diff --git a/packages/block-editor/src/components/responsive-block-control/README.md b/packages/block-editor/src/components/responsive-block-control/README.md
index 379e245f03ff37..16183ccd6314f5 100644
--- a/packages/block-editor/src/components/responsive-block-control/README.md
+++ b/packages/block-editor/src/components/responsive-block-control/README.md
@@ -27,9 +27,6 @@ import {
InspectorControls,
__experimentalResponsiveBlockControl as ResponsiveBlockControl,
} from '@wordpress/block-editor';
-import {
- DimensionControl,
-} from '@wordpress/components';
registerBlockType( 'my-plugin/my-block', {
// ...
@@ -37,34 +34,13 @@ registerBlockType( 'my-plugin/my-block', {
edit( { attributes, setAttributes } ) {
const [ isResponsive, setIsResponsive ] = useState( false );
-
- // Used for example purposes only
- const sizeOptions = [
- {
- label: 'Small',
- value: 'small',
- },
- {
- label: 'Medium',
- value: 'medium',
- },
- {
- label: 'Large',
- value: 'large',
- },
- ];
-
const { paddingSize } = attributes;
-
// Your custom control can be anything you'd like to use.
- // You are not restricted to `DimensionControl`s, but this
- // makes life easier if dealing with standard CSS values.
- // see `packages/components/src/dimension-control/README.md`
const paddingControl = ( labelComponent, viewport ) => {
return (
-
{
// id: 'small',
// label: 'All'
// }
- return ;
+ return ;
};
```
diff --git a/packages/block-editor/src/components/rich-text/style.scss b/packages/block-editor/src/components/rich-text/style.scss
index a3bb78b2521db8..ca274d378d4088 100644
--- a/packages/block-editor/src/components/rich-text/style.scss
+++ b/packages/block-editor/src/components/rich-text/style.scss
@@ -12,7 +12,7 @@
}
.components-toolbar {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
}
.components-toolbar__control,
diff --git a/packages/block-editor/src/components/spacing-sizes-control/style.scss b/packages/block-editor/src/components/spacing-sizes-control/style.scss
index cbcc8e30c58253..a387e5369d01ed 100644
--- a/packages/block-editor/src/components/spacing-sizes-control/style.scss
+++ b/packages/block-editor/src/components/spacing-sizes-control/style.scss
@@ -19,6 +19,7 @@
.spacing-sizes-control__range-control,
.spacing-sizes-control__custom-value-range {
flex: 1;
+ margin-bottom: 0; // Needed for some instances of the range control, such as the Spacer block.
}
.components-range-control__mark {
diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js
index fb25368239a011..ff4d52aaa493bc 100644
--- a/packages/block-editor/src/components/use-block-drop-zone/index.js
+++ b/packages/block-editor/src/components/use-block-drop-zone/index.js
@@ -24,7 +24,6 @@ import {
} from '../../utils/math';
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';
-import { sectionRootClientIdKey } from '../../store/private-keys';
const THRESHOLD_DISTANCE = 30;
const MINIMUM_HEIGHT_FOR_THRESHOLD = 120;
@@ -275,6 +274,23 @@ export function isDropTargetValid(
return areBlocksAllowed && targetMatchesDraggedBlockParents;
}
+/**
+ * Checks if the given element is an insertion point.
+ *
+ * @param {EventTarget|null} targetToCheck - The element to check.
+ * @param {Document} ownerDocument - The owner document of the element.
+ * @return {boolean} True if the element is a insertion point, false otherwise.
+ */
+function isInsertionPoint( targetToCheck, ownerDocument ) {
+ const { defaultView } = ownerDocument;
+
+ return !! (
+ defaultView &&
+ targetToCheck instanceof defaultView.HTMLElement &&
+ targetToCheck.dataset.isInsertionPoint
+ );
+}
+
/**
* @typedef {Object} WPBlockDropZoneConfig
* @property {?HTMLElement} dropZoneElement Optional element to be used as the drop zone.
@@ -314,8 +330,8 @@ export default function useBlockDropZone( {
getAllowedBlocks,
isDragging,
isGroupable,
- getSettings,
isZoomOutMode,
+ getSectionRootClientId,
} = unlock( useSelect( blockEditorStore ) );
const {
showInsertionPoint,
@@ -361,8 +377,7 @@ export default function useBlockDropZone( {
return;
}
- const { [ sectionRootClientIdKey ]: sectionRootClientId } =
- getSettings();
+ const sectionRootClientId = getSectionRootClientId();
// In Zoom Out mode, if the target is not the section root provided by settings then
// do not allow dropping as the drop target is not within the root (that which is
@@ -424,6 +439,10 @@ export default function useBlockDropZone( {
const [ targetIndex, operation, nearestSide ] =
dropTargetPosition;
+ if ( isZoomOutMode() && operation !== 'insert' ) {
+ return;
+ }
+
if ( operation === 'group' ) {
const targetBlock = blocks[ targetIndex ];
const areAllImages = [
@@ -494,6 +513,8 @@ export default function useBlockDropZone( {
getBlockNamesByClientId,
getDraggedBlockClientIds,
getBlockType,
+ getSectionRootClientId,
+ isZoomOutMode,
getBlocks,
getBlockListSettings,
dropZoneElement,
@@ -506,8 +527,6 @@ export default function useBlockDropZone( {
isGroupable,
getBlockVariations,
getGroupingBlockName,
- getSettings,
- isZoomOutMode,
]
),
200
@@ -523,7 +542,18 @@ export default function useBlockDropZone( {
// https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
throttled( event, event.currentTarget.ownerDocument );
},
- onDragLeave() {
+ onDragLeave( event ) {
+ const { ownerDocument } = event.currentTarget;
+
+ // If the drag event is leaving the drop zone and entering an insertion point,
+ // do not hide the insertion point as it is conceptually within the dropzone.
+ if (
+ isInsertionPoint( event.relatedTarget, ownerDocument ) ||
+ isInsertionPoint( event.target, ownerDocument )
+ ) {
+ return;
+ }
+
throttled.cancel();
hideInsertionPoint();
},
diff --git a/packages/block-editor/src/components/warning/content.scss b/packages/block-editor/src/components/warning/content.scss
index 0c1185a0782e41..9380a224b2ff95 100644
--- a/packages/block-editor/src/components/warning/content.scss
+++ b/packages/block-editor/src/components/warning/content.scss
@@ -7,7 +7,7 @@
// Block UI appearance.
border: $border-width solid $gray-900;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
background-color: $white;
.block-editor-warning__message {
diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js
index c1422acaac0e5a..3b59de2238b968 100644
--- a/packages/block-editor/src/hooks/block-bindings.js
+++ b/packages/block-editor/src/hooks/block-bindings.js
@@ -12,7 +12,7 @@ import {
__experimentalVStack as VStack,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
-import { useRegistry } from '@wordpress/data';
+import { useRegistry, useSelect } from '@wordpress/data';
import { useContext, Fragment } from '@wordpress/element';
import { useViewportMatch } from '@wordpress/compose';
@@ -27,6 +27,7 @@ import { unlock } from '../lock-unlock';
import InspectorControls from '../components/inspector-controls';
import BlockContext from '../components/block-context';
import { useBlockBindingsUtils } from '../utils/block-bindings';
+import { store as blockEditorStore } from '../store';
const { DropdownMenuV2 } = unlock( componentsPrivateApis );
@@ -196,6 +197,13 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
}
} );
+ const { canUpdateBlockBindings } = useSelect( ( select ) => {
+ return {
+ canUpdateBlockBindings:
+ select( blockEditorStore ).getSettings().canUpdateBlockBindings,
+ };
+ }, [] );
+
if ( ! bindableAttributes || bindableAttributes.length === 0 ) {
return null;
}
@@ -231,17 +239,16 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
}
} );
- // Lock the UI when the experiment is not enabled or there are no fields to connect to.
+ // Lock the UI when the user can't update bindings or there are no fields to connect to.
const readOnly =
- ! window.__experimentalBlockBindingsUI ||
- ! Object.keys( fieldsList ).length;
+ ! canUpdateBlockBindings || ! Object.keys( fieldsList ).length;
if ( readOnly && Object.keys( filteredBindings ).length === 0 ) {
return null;
}
return (
-
+
{
@@ -263,9 +270,13 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
/>
) }
-
- { __( 'Attributes connected to various sources.' ) }
-
+
+
+ { __(
+ 'Attributes connected to custom fields or other dynamic data.'
+ ) }
+
+
);
diff --git a/packages/block-editor/src/hooks/layout.scss b/packages/block-editor/src/hooks/layout.scss
index 3eedd1f629a633..0ce3bc0577640f 100644
--- a/packages/block-editor/src/hooks/layout.scss
+++ b/packages/block-editor/src/hooks/layout.scss
@@ -1,23 +1,13 @@
-.block-editor-hooks__layout-controls-units {
- display: flex;
- flex-direction: column;
- gap: $grid-unit-20;
-}
-
-.block-editor-block-inspector .block-editor-hooks__layout-controls-unit-input {
- margin-bottom: 0;
-}
-
-.block-editor-hooks__layout-controls-reset {
- display: flex;
- justify-content: flex-end;
- margin-bottom: $grid-unit-30;
+.block-editor-hooks__layout-constrained {
+ .components-base-control {
+ margin-bottom: 0; // Cancel out margins added by block inspector
+ }
}
-.block-editor-hooks__layout-controls-helptext {
+.block-editor-hooks__layout-constrained-helptext {
color: $gray-700;
font-size: $helptext-font-size;
- margin-bottom: $grid-unit-20;
+ margin-bottom: 0; // Cancel out margins added by common.css
}
.block-editor-hooks__flex-layout-justification-controls,
diff --git a/packages/block-editor/src/layouts/constrained.js b/packages/block-editor/src/layouts/constrained.js
index 21aca422a315ff..7bf6badf008709 100644
--- a/packages/block-editor/src/layouts/constrained.js
+++ b/packages/block-editor/src/layouts/constrained.js
@@ -7,6 +7,7 @@ import {
__experimentalToggleGroupControl as ToggleGroupControl,
__experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon,
__experimentalInputControlPrefixWrapper as InputControlPrefixWrapper,
+ __experimentalVStack as VStack,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import {
@@ -70,12 +71,14 @@ export default {
availableUnits: availableUnits || [ '%', 'px', 'em', 'rem', 'vw' ],
} );
return (
- <>
+
{ allowCustomContentAndWideSize && (
-
+ <>
}
/>
-
+
{ __(
'Customize the width for all elements that are assigned to the center or wide columns.'
) }
-
+ >
) }
{ allowJustification && (
) }
- >
+
);
},
toolBarControls: function DefaultLayoutToolbarControls( {
diff --git a/packages/block-editor/src/layouts/grid.js b/packages/block-editor/src/layouts/grid.js
index 7ab5c7ebbc8c3d..d1f6fdd785089b 100644
--- a/packages/block-editor/src/layouts/grid.js
+++ b/packages/block-editor/src/layouts/grid.js
@@ -74,7 +74,8 @@ export default {
// In the experiment we want to also show column control in Auto mode, and
// the minimum width control in Manual mode.
const showColumnsControl =
- window.__experimentalEnableGridInteractivity || layout?.columnCount;
+ window.__experimentalEnableGridInteractivity ||
+ !! layout?.columnCount;
const showMinWidthControl =
window.__experimentalEnableGridInteractivity ||
! layout?.columnCount;
@@ -317,7 +318,7 @@ function GridLayoutColumnsAndRowsControl( {
const defaultNewColumnCount =
isManualPlacement ? 1 : undefined;
const newColumnCount =
- value === ''
+ value === '' || value === '0'
? defaultNewColumnCount
: parseInt( value, 10 );
onChange( {
@@ -327,7 +328,7 @@ function GridLayoutColumnsAndRowsControl( {
} else {
// Don't allow unsetting the column count.
const newColumnCount =
- value === ''
+ value === '' || value === '0'
? 1
: parseInt( value, 10 );
onChange( {
@@ -337,7 +338,7 @@ function GridLayoutColumnsAndRowsControl( {
}
} }
value={ columnCount }
- min={ 0 }
+ min={ 1 }
label={ __( 'Columns' ) }
hideLabelFromVision={
! window.__experimentalEnableGridInteractivity ||
@@ -355,7 +356,7 @@ function GridLayoutColumnsAndRowsControl( {
onChange={ ( value ) => {
// Don't allow unsetting the row count.
const newRowCount =
- value === ''
+ value === '' || value === '0'
? 1
: parseInt( value, 10 );
onChange( {
@@ -364,21 +365,24 @@ function GridLayoutColumnsAndRowsControl( {
} );
} }
value={ rowCount }
- min={ 0 }
+ min={ 1 }
label={ __( 'Rows' ) }
/>
) : (
onChange( {
...layout,
- columnCount: value,
+ columnCount:
+ value === '' || value === '0'
+ ? 1
+ : value,
} )
}
- min={ 0 }
+ min={ 1 }
max={ 16 }
withInputField={ false }
label={ __( 'Columns' ) }
diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js
index 404c94940058ad..12f477a95a196b 100644
--- a/packages/block-editor/src/private-apis.js
+++ b/packages/block-editor/src/private-apis.js
@@ -6,7 +6,6 @@ import { ExperimentalBlockEditorProvider } from './components/provider';
import { lock } from './lock-unlock';
import { getRichTextValues } from './components/rich-text/get-rich-text-values';
import ResizableBoxPopover from './components/resizable-box-popover';
-import { ComposedPrivateInserter as PrivateInserter } from './components/inserter';
import { default as PrivateQuickInserter } from './components/inserter/quick-inserter';
import {
extractWords,
@@ -60,7 +59,6 @@ lock( privateApis, {
ExperimentalBlockEditorProvider,
getDuotoneFilter,
getRichTextValues,
- PrivateInserter,
PrivateQuickInserter,
extractWords,
getNormalizedSearchTerms,
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
index ebd310b3de9d3f..e91f997ca67837 100644
--- a/packages/block-editor/src/store/actions.js
+++ b/packages/block-editor/src/store/actions.js
@@ -34,9 +34,6 @@ import {
__experimentalUpdateSettings,
privateRemoveBlocks,
} from './private-actions';
-import { STORE_NAME } from './constants';
-
-import { sectionRootClientIdKey } from './private-keys';
/** @typedef {import('../components/use-on-block-drop/types').WPDropOperation} WPDropOperation */
@@ -1671,13 +1668,12 @@ export const setNavigationMode =
*/
export const __unstableSetEditorMode =
( mode ) =>
- ( { dispatch, select, registry } ) => {
+ ( { dispatch, select } ) => {
// When switching to zoom-out mode, we need to select the parent section
if ( mode === 'zoom-out' ) {
const firstSelectedClientId = select.getBlockSelectionStart();
- const { [ sectionRootClientIdKey ]: sectionRootClientId } = registry
- .select( STORE_NAME )
- .getSettings();
+
+ const sectionRootClientId = select.getSectionRootClientId();
if ( firstSelectedClientId ) {
let sectionClientId;
diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js
index 9cc793362296c4..b72ebd18183377 100644
--- a/packages/block-editor/src/store/private-selectors.js
+++ b/packages/block-editor/src/store/private-selectors.js
@@ -28,6 +28,7 @@ import { unlock } from '../lock-unlock';
import {
selectBlockPatternsKey,
reusableBlocksSelectKey,
+ sectionRootClientIdKey,
} from './private-keys';
export { getBlockSettings } from './get-block-settings';
@@ -421,9 +422,11 @@ const EMPTY_ARRAY = [];
export const getReusableBlocks = createRegistrySelector(
( select ) => ( state ) => {
const reusableBlocksSelect = state.settings[ reusableBlocksSelectKey ];
- return reusableBlocksSelect
- ? reusableBlocksSelect( select )
- : state.settings.__experimentalReusableBlocks ?? EMPTY_ARRAY;
+ return (
+ ( reusableBlocksSelect
+ ? reusableBlocksSelect( select )
+ : state.settings.__experimentalReusableBlocks ) ?? EMPTY_ARRAY
+ );
}
);
@@ -543,3 +546,7 @@ export const getBlockStyles = createSelector(
export function isZoomOutMode( state ) {
return state.editorMode === 'zoom-out';
}
+
+export function getSectionRootClientId( state ) {
+ return state.settings?.[ sectionRootClientIdKey ];
+}
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
index 91d853a50a6a42..cd4569c45e5801 100644
--- a/packages/block-editor/src/store/reducer.js
+++ b/packages/block-editor/src/store/reducer.js
@@ -1624,8 +1624,6 @@ export function insertionPoint( state = null, action ) {
}
case 'HIDE_INSERTION_POINT':
- case 'CLEAR_SELECTED_BLOCK':
- case 'SELECT_BLOCK':
return null;
}
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index 2a840392fbffed..30fdb76bdbe787 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -37,10 +37,9 @@ import {
getContentLockingParent,
getTemporarilyEditingAsBlocks,
getTemporarilyEditingFocusModeToRevert,
+ getSectionRootClientId,
} from './private-selectors';
-import { sectionRootClientIdKey } from './private-keys';
-
/**
* A block selection object.
*
@@ -2061,9 +2060,7 @@ export const getInserterItems = createRegistrySelector( ( select ) =>
let sectionRootClientId;
try {
sectionRootClientId =
- getSettings( state )[
- sectionRootClientIdKey
- ];
+ getSectionRootClientId( state );
} catch ( e ) {}
if (
sectionRootClientId &&
@@ -2841,8 +2838,7 @@ export function __unstableHasActiveBlockOverlayActive( state, clientId ) {
// In zoom-out mode, the block overlay is always active for section level blocks.
if ( editorMode === 'zoom-out' ) {
- const { [ sectionRootClientIdKey ]: sectionRootClientId } =
- getSettings( state );
+ const sectionRootClientId = getSectionRootClientId( state );
if ( sectionRootClientId ) {
const sectionClientIds = getBlockOrder(
state,
@@ -2935,8 +2931,7 @@ export const getBlockEditingMode = createRegistrySelector(
// sections.
const editorMode = __unstableGetEditorMode( state );
if ( editorMode === 'zoom-out' ) {
- const { [ sectionRootClientIdKey ]: sectionRootClientId } =
- getSettings( state );
+ const sectionRootClientId = getSectionRootClientId( state );
if ( clientId === '' /* ROOT_CONTAINER_CLIENT_ID */ ) {
return sectionRootClientId ? 'disabled' : 'contentOnly';
diff --git a/packages/block-editor/src/utils/dom.js b/packages/block-editor/src/utils/dom.js
index bfdce60b9f0060..9c2e813ef742b6 100644
--- a/packages/block-editor/src/utils/dom.js
+++ b/packages/block-editor/src/utils/dom.js
@@ -96,11 +96,26 @@ function isElementVisible( element ) {
return false;
}
- return element.checkVisibility( {
- opacityProperty: true,
- contentVisibilityAuto: true,
- visibilityProperty: true,
- } );
+ // Older browsers, e.g. Safari < 17.4 may not support the `checkVisibility` method.
+ if ( element.checkVisibility ) {
+ return element.checkVisibility?.( {
+ opacityProperty: true,
+ contentVisibilityAuto: true,
+ visibilityProperty: true,
+ } );
+ }
+
+ const style = viewport.getComputedStyle( element );
+
+ if (
+ style.display === 'none' ||
+ style.visibility === 'hidden' ||
+ style.opacity === '0'
+ ) {
+ return false;
+ }
+
+ return true;
}
/**
diff --git a/packages/block-editor/src/utils/get-font-styles-and-weights.js b/packages/block-editor/src/utils/get-font-styles-and-weights.js
index 88b40a5aacfeb5..dcb56fc86b2673 100644
--- a/packages/block-editor/src/utils/get-font-styles-and-weights.js
+++ b/packages/block-editor/src/utils/get-font-styles-and-weights.js
@@ -79,7 +79,10 @@ export function getFontStylesAndWeights( fontFamilyFaces ) {
fontFamilyFaces?.forEach( ( face ) => {
// Check for variable font by looking for a space in the font weight value. e.g. "100 900"
- if ( /\s/.test( face.fontWeight.trim() ) ) {
+ if (
+ 'string' === typeof face.fontWeight &&
+ /\s/.test( face.fontWeight.trim() )
+ ) {
isVariableFont = true;
// Find font weight start and end values.
@@ -105,11 +108,15 @@ export function getFontStylesAndWeights( fontFamilyFaces ) {
}
// Format font style and weight values.
- const fontWeight = formatFontWeight( face.fontWeight );
+ const fontWeight = formatFontWeight(
+ 'number' === typeof face.fontWeight
+ ? face.fontWeight.toString()
+ : face.fontWeight
+ );
const fontStyle = formatFontStyle( face.fontStyle );
// Create font style and font weight lists without duplicates.
- if ( fontStyle ) {
+ if ( fontStyle && Object.keys( fontStyle ).length ) {
if (
! fontStyles.some(
( style ) => style.value === fontStyle.value
@@ -118,7 +125,8 @@ export function getFontStylesAndWeights( fontFamilyFaces ) {
fontStyles.push( fontStyle );
}
}
- if ( fontWeight ) {
+
+ if ( fontWeight && Object.keys( fontWeight ).length ) {
if (
! fontWeights.some(
( weight ) => weight.value === fontWeight.value
diff --git a/packages/block-editor/src/utils/test/get-font-styles-and-weights.js b/packages/block-editor/src/utils/test/get-font-styles-and-weights.js
index ffc1a635aa1fb1..7c50409bae1acc 100644
--- a/packages/block-editor/src/utils/test/get-font-styles-and-weights.js
+++ b/packages/block-editor/src/utils/test/get-font-styles-and-weights.js
@@ -343,6 +343,17 @@ describe( 'getFontStylesAndWeights', () => {
fontWeight: '400',
src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b52SlTPu5rIkWIZjVKKtYtfxYqZ4RJBFzFfYUjkSDdlqZgy7LYxnL31AHfAAy5.woff2',
},
+ {
+ fontFamily: 'Piazzolla',
+ fontStyle: 'normal',
+ fontWeight: 600,
+ src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b52SlTPu5rIkWIZjVKKtYtfxYqZ4RJBFzFfYUjkSDdlqZgy7JxwXL31AHfAAy5.woff2',
+ },
+ {
+ fontFamily: 'Piazzolla',
+ fontStyle: 'normal',
+ src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b52SlTPu5rIkWIZjVKKtYtfxYqZ4RJBFzFfYUjkSDdlqZgy7JxwXL31AHfAAy5.woff2',
+ },
{
fontFamily: 'Piazzolla',
fontStyle: 'normal',
@@ -378,6 +389,10 @@ describe( 'getFontStylesAndWeights', () => {
name: 'Regular',
value: '400',
},
+ {
+ name: 'Semi Bold',
+ value: '600',
+ },
{
name: 'Black',
value: '900',
@@ -396,6 +411,14 @@ describe( 'getFontStylesAndWeights', () => {
fontWeight: '400',
},
},
+ {
+ key: 'normal-600',
+ name: 'Semi Bold',
+ style: {
+ fontStyle: 'normal',
+ fontWeight: '600',
+ },
+ },
{
key: 'normal-900',
name: 'Black',
@@ -420,6 +443,14 @@ describe( 'getFontStylesAndWeights', () => {
fontWeight: '400',
},
},
+ {
+ key: 'italic-600',
+ name: 'Semi Bold Italic',
+ style: {
+ fontStyle: 'italic',
+ fontWeight: '600',
+ },
+ },
{
key: 'italic-900',
name: 'Black Italic',
@@ -510,4 +541,121 @@ describe( 'getFontStylesAndWeights', () => {
isVariableFont: false,
} );
} );
+
+ it( 'should return available styles and weights for a font without fontWeight', () => {
+ const fontFamilyFaces = [
+ {
+ fontFamily: 'AR One Sans',
+ fontStyle: 'normal',
+ src: 'http://www.wordpress.org/wp-content/uploads/fonts/AROneSans-VariableFont_ARRRwght.ttf',
+ },
+ ];
+ expect( getFontStylesAndWeights( fontFamilyFaces ) ).toEqual( {
+ fontStyles: [
+ {
+ name: 'Regular',
+ value: 'normal',
+ },
+ {
+ name: 'Italic',
+ value: 'italic',
+ },
+ ],
+ fontWeights: [
+ {
+ name: 'Bold',
+ value: '700',
+ },
+ ],
+ combinedStyleAndWeightOptions: [
+ {
+ key: 'normal-700',
+ name: 'Bold',
+ style: {
+ fontStyle: 'normal',
+ fontWeight: '700',
+ },
+ },
+ {
+ key: 'italic-700',
+ name: 'Bold Italic',
+ style: {
+ fontStyle: 'italic',
+ fontWeight: '700',
+ },
+ },
+ ],
+ isSystemFont: false,
+ isVariableFont: false,
+ } );
+ } );
+
+ it( 'should return available styles and weights for a font with numeric fontWeight', () => {
+ const fontFamilyFaces = [
+ {
+ fontFamily: 'AR One Sans',
+ fontStyle: 'normal',
+ fontWeight: 400,
+ src: 'http://www.wordpress.org/wp-content/uploads/fonts/AROneSans-VariableFont_ARRRwght.ttf',
+ },
+ ];
+ expect( getFontStylesAndWeights( fontFamilyFaces ) ).toEqual( {
+ fontStyles: [
+ {
+ name: 'Regular',
+ value: 'normal',
+ },
+ {
+ name: 'Italic',
+ value: 'italic',
+ },
+ ],
+ fontWeights: [
+ {
+ name: 'Regular',
+ value: '400',
+ },
+ {
+ name: 'Bold',
+ value: '700',
+ },
+ ],
+ combinedStyleAndWeightOptions: [
+ {
+ key: 'normal-400',
+ name: 'Regular',
+ style: {
+ fontStyle: 'normal',
+ fontWeight: '400',
+ },
+ },
+ {
+ key: 'normal-700',
+ name: 'Bold',
+ style: {
+ fontStyle: 'normal',
+ fontWeight: '700',
+ },
+ },
+ {
+ key: 'italic-400',
+ name: 'Regular Italic',
+ style: {
+ fontStyle: 'italic',
+ fontWeight: '400',
+ },
+ },
+ {
+ key: 'italic-700',
+ name: 'Bold Italic',
+ style: {
+ fontStyle: 'italic',
+ fontWeight: '700',
+ },
+ },
+ ],
+ isSystemFont: false,
+ isVariableFont: false,
+ } );
+ } );
} );
diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md
index 319d2b04a31687..4ee3ebc7564b53 100644
--- a/packages/block-library/CHANGELOG.md
+++ b/packages/block-library/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 9.7.0 (2024-09-05)
+
## 9.6.0 (2024-08-21)
## 9.5.0 (2024-08-07)
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index 5b522f01ea7786..1353ef24c77d89 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/block-library",
- "version": "9.6.1",
+ "version": "9.7.0",
"description": "Block library for the WordPress editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
@@ -30,6 +30,13 @@
"src/**/*.scss",
"{src,build,build-module}/*/init.js"
],
+ "wpScriptModuleExports": {
+ "./file/view": "./build-module/file/view.js",
+ "./image/view": "./build-module/image/view.js",
+ "./navigation/view": "./build-module/navigation/view.js",
+ "./query/view": "./build-module/query/view.js",
+ "./search/view": "./build-module/search/view.js"
+ },
"dependencies": {
"@babel/runtime": "^7.16.0",
"@wordpress/a11y": "file:../a11y",
diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js
index 53422e1c4cb8c0..5c90361e6bb435 100644
--- a/packages/block-library/src/block/edit.js
+++ b/packages/block-library/src/block/edit.js
@@ -29,6 +29,7 @@ import {
privateApis as blockEditorPrivateApis,
store as blockEditorStore,
BlockControls,
+ InnerBlocks,
} from '@wordpress/block-editor';
import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
import { store as blocksStore } from '@wordpress/blocks';
@@ -193,19 +194,15 @@ function ReusableBlockEdit( {
hasPatternOverridesSource,
} = useSelect(
( select ) => {
- const {
- getBlocks,
- getSettings,
- getBlockEditingMode: _getBlockEditingMode,
- } = select( blockEditorStore );
+ const { getBlocks, getSettings, getBlockEditingMode } =
+ select( blockEditorStore );
const { getBlockBindingsSource } = unlock( select( blocksStore ) );
// For editing link to the site editor if the theme and user permissions support it.
return {
innerBlocks: getBlocks( patternClientId ),
- getBlockEditingMode: _getBlockEditingMode,
onNavigateToEntityRecord:
getSettings().onNavigateToEntityRecord,
- editingMode: _getBlockEditingMode( patternClientId ),
+ editingMode: getBlockEditingMode( patternClientId ),
hasPatternOverridesSource: !! getBlockBindingsSource(
'core/pattern-overrides'
),
@@ -247,14 +244,15 @@ function ReusableBlockEdit( {
),
} );
- // Use `blocks` variable until `innerBlocks` is populated, which has the proper clientIds.
const innerBlocksProps = useInnerBlocksProps( blockProps, {
templateLock: 'all',
layout,
- value: innerBlocks.length > 0 ? innerBlocks : blocks,
+ value: blocks,
onInput: NOOP,
onChange: NOOP,
- renderAppender: blocks?.length ? undefined : blocks.ButtonBlockAppender,
+ renderAppender: blocks?.length
+ ? undefined
+ : InnerBlocks.ButtonBlockAppender,
} );
const handleEditOriginal = () => {
@@ -292,7 +290,7 @@ function ReusableBlockEdit( {
return (
<>
- { hasResolved && (
+ { hasResolved && ! isMissing && (
%2$s';
$items_markup = wp_list_categories( $args );
$type = 'list';
+
+ if ( ! empty( $block->context['enhancedPagination'] ) ) {
+ $p = new WP_HTML_Tag_Processor( $items_markup );
+ while ( $p->next_tag( 'a' ) ) {
+ $p->set_attribute( 'data-wp-on--click', 'core/query::actions.navigate' );
+ }
+ $items_markup = $p->get_updated_html();
+ }
}
$wrapper_attributes = get_block_wrapper_attributes( array( 'class' => "wp-block-categories-{$type}" ) );
diff --git a/packages/block-library/src/comment-reply-link/block.json b/packages/block-library/src/comment-reply-link/block.json
index c10129412145c8..68aa93c3c1526a 100644
--- a/packages/block-library/src/comment-reply-link/block.json
+++ b/packages/block-library/src/comment-reply-link/block.json
@@ -25,7 +25,11 @@
},
"spacing": {
"margin": true,
- "padding": true
+ "padding": true,
+ "__experimentalDefaultControls": {
+ "margin": false,
+ "padding": false
+ }
},
"typography": {
"fontSize": true,
@@ -40,6 +44,13 @@
"fontSize": true
}
},
+ "__experimentalBorder": {
+ "radius": true,
+ "color": true,
+ "width": true,
+ "style": true
+ },
"html": false
- }
+ },
+ "style": "wp-block-comment-reply-link"
}
diff --git a/packages/block-library/src/comment-reply-link/style.scss b/packages/block-library/src/comment-reply-link/style.scss
new file mode 100644
index 00000000000000..7eca6cee1aa979
--- /dev/null
+++ b/packages/block-library/src/comment-reply-link/style.scss
@@ -0,0 +1,4 @@
+.wp-block-comment-reply-link {
+ // This block has customizable padding, border-box makes that more predictable.
+ box-sizing: border-box;
+}
diff --git a/packages/block-library/src/comments/edit/comments-legacy.js b/packages/block-library/src/comments/edit/comments-legacy.js
index b5a12e34343dac..dbe89a4e3a83e1 100644
--- a/packages/block-library/src/comments/edit/comments-legacy.js
+++ b/packages/block-library/src/comments/edit/comments-legacy.js
@@ -29,8 +29,7 @@ export default function CommentsLegacy( {
const actions = [
void setAttributes( { legacy: false } ) }
variant="primary"
diff --git a/packages/block-library/src/cover/deprecated.js b/packages/block-library/src/cover/deprecated.js
index c5f430eead685f..5e5fc64d12c16f 100644
--- a/packages/block-library/src/cover/deprecated.js
+++ b/packages/block-library/src/cover/deprecated.js
@@ -400,8 +400,9 @@ const v12 = {
supports: v12BlockSupports,
isEligible( attributes ) {
return (
- attributes.customOverlayColor !== undefined ||
- attributes.overlayColor !== undefined
+ ( attributes.customOverlayColor !== undefined ||
+ attributes.overlayColor !== undefined ) &&
+ attributes.isUserOverlayColor === undefined
);
},
migrate( attributes ) {
diff --git a/packages/block-library/src/cover/edit/index.js b/packages/block-library/src/cover/edit/index.js
index 3f68c08b632ace..ec62bd58a2c33a 100644
--- a/packages/block-library/src/cover/edit/index.js
+++ b/packages/block-library/src/cover/edit/index.js
@@ -143,7 +143,10 @@ function CoverEdit( {
averageBackgroundColor
);
__unstableMarkNextChangeAsNotPersistent();
- setAttributes( { isDark: newIsDark } );
+ setAttributes( {
+ isDark: newIsDark,
+ isUserOverlayColor: isUserOverlayColor || false,
+ } );
} )();
// Disable reason: Update the block only when the featured image changes.
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -201,6 +204,7 @@ function CoverEdit( {
useFeaturedImage: undefined,
dimRatio: newDimRatio,
isDark: newIsDark,
+ isUserOverlayColor: isUserOverlayColor || false,
} );
};
diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js
index 6978e144794047..da3dac3e3d62b5 100644
--- a/packages/block-library/src/embed/edit.js
+++ b/packages/block-library/src/embed/edit.js
@@ -14,7 +14,6 @@ import { embedContentIcon } from './icons';
import EmbedLoading from './embed-loading';
import EmbedPlaceholder from './embed-placeholder';
import EmbedPreview from './embed-preview';
-import { Caption } from '../utils/caption';
/**
* External dependencies
@@ -277,14 +276,8 @@ const EmbedEdit = ( props ) => {
icon={ icon }
label={ label }
insertBlocksAfter={ insertBlocksAfter }
- />
-
>
diff --git a/packages/block-library/src/embed/embed-placeholder.js b/packages/block-library/src/embed/embed-placeholder.js
index 9a26060a294120..1626aca5c9c9cb 100644
--- a/packages/block-library/src/embed/embed-placeholder.js
+++ b/packages/block-library/src/embed/embed-placeholder.js
@@ -66,16 +66,14 @@ const EmbedPlaceholder = ( {
justify="flex-start"
>
{ _x( 'Try again', 'button label' ) }
{ ' ' }
diff --git a/packages/block-library/src/embed/embed-preview.js b/packages/block-library/src/embed/embed-preview.js
index d53f1148cee13c..6ef10df08a8c24 100644
--- a/packages/block-library/src/embed/embed-preview.js
+++ b/packages/block-library/src/embed/embed-preview.js
@@ -21,6 +21,7 @@ import { getAuthority } from '@wordpress/url';
* Internal dependencies
*/
import WpEmbedPreview from './wp-embed-preview';
+import { Caption } from '../utils/caption';
class EmbedPreview extends Component {
constructor() {
@@ -52,8 +53,19 @@ class EmbedPreview extends Component {
}
render() {
- const { preview, previewable, url, type, className, icon, label } =
- this.props;
+ const {
+ preview,
+ previewable,
+ url,
+ type,
+ className,
+ icon,
+ label,
+ insertBlocksAfter,
+ attributes,
+ setAttributes,
+ isSelected,
+ } = this.props;
const { scripts } = preview;
const { interactive } = this.state;
@@ -123,6 +135,14 @@ class EmbedPreview extends Component {
) }
+
);
}
diff --git a/packages/block-library/src/file/index.php b/packages/block-library/src/file/index.php
index 87910f0e66a0cf..85cc840201da59 100644
--- a/packages/block-library/src/file/index.php
+++ b/packages/block-library/src/file/index.php
@@ -21,7 +21,7 @@ function render_block_core_file( $attributes, $content ) {
if ( ! empty( $attributes['displayPreview'] ) ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
- $module_url = gutenberg_url( '/build/interactivity/file.min.js' );
+ $module_url = gutenberg_url( '/build-module/block-library/file/view.min.js' );
}
wp_register_script_module(
diff --git a/packages/block-library/src/file/transforms.js b/packages/block-library/src/file/transforms.js
index b925e80e595033..c381a62f783866 100644
--- a/packages/block-library/src/file/transforms.js
+++ b/packages/block-library/src/file/transforms.js
@@ -24,12 +24,32 @@ const transforms = {
const blobURL = createBlobURL( file );
// File will be uploaded in componentDidMount()
- blocks.push(
- createBlock( 'core/file', {
- blob: blobURL,
- fileName: file.name,
- } )
- );
+ if ( file.type.startsWith( 'video/' ) ) {
+ blocks.push(
+ createBlock( 'core/video', {
+ blob: createBlobURL( file ),
+ } )
+ );
+ } else if ( file.type.startsWith( 'image/' ) ) {
+ blocks.push(
+ createBlock( 'core/image', {
+ blob: createBlobURL( file ),
+ } )
+ );
+ } else if ( file.type.startsWith( 'audio/' ) ) {
+ blocks.push(
+ createBlock( 'core/audio', {
+ blob: createBlobURL( file ),
+ } )
+ );
+ } else {
+ blocks.push(
+ createBlock( 'core/file', {
+ blob: blobURL,
+ fileName: file.name,
+ } )
+ );
+ }
} );
return blocks;
diff --git a/packages/block-library/src/freeform/editor.scss b/packages/block-library/src/freeform/editor.scss
index 7329eb6e5fb064..c2256ecd7a795f 100644
--- a/packages/block-library/src/freeform/editor.scss
+++ b/packages/block-library/src/freeform/editor.scss
@@ -299,7 +299,7 @@ div[data-type="core/freeform"] {
top: 0;
border: $border-width solid $gray-300;
border-bottom: none;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
margin-bottom: $grid-unit-10;
// On mobile, toolbars go edge to edge.
diff --git a/packages/block-library/src/freeform/modal.js b/packages/block-library/src/freeform/modal.js
index 1768022377a432..4ed4ef4d3a07ca 100644
--- a/packages/block-library/src/freeform/modal.js
+++ b/packages/block-library/src/freeform/modal.js
@@ -25,8 +25,7 @@ function ModalAuxiliaryActions( { onClick, isModalFullScreen } ) {
return (
@@ -133,8 +131,7 @@ export default function ModalEdit( props ) {
{
setAttributes( {
diff --git a/packages/block-library/src/gallery/editor.scss b/packages/block-library/src/gallery/editor.scss
index 3184b9d3d8fb22..61121f3dd866dc 100644
--- a/packages/block-library/src/gallery/editor.scss
+++ b/packages/block-library/src/gallery/editor.scss
@@ -139,7 +139,7 @@
z-index: z-index(".block-library-gallery-item__inline-menu");
transition: box-shadow 0.2s ease-out;
@include reduce-motion("transition");
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
background: $white;
border: $border-width solid $gray-900;
diff --git a/packages/block-library/src/group/editor.scss b/packages/block-library/src/group/editor.scss
index 041a2f9f884e84..11beecbab0eb68 100644
--- a/packages/block-library/src/group/editor.scss
+++ b/packages/block-library/src/group/editor.scss
@@ -43,7 +43,6 @@
pointer-events: none;
min-height: $grid-unit-60 - $border-width - $border-width;
border: $border-width dashed currentColor;
- border-radius: $radius-block-ui;
}
// Let the parent be selectable in the placeholder area.
diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js
index b7c25595dcfccf..d44dc73abfd855 100644
--- a/packages/block-library/src/image/edit.js
+++ b/packages/block-library/src/image/edit.js
@@ -6,14 +6,14 @@ import clsx from 'clsx';
/**
* WordPress dependencies
*/
-import { isBlobURL } from '@wordpress/blob';
-import { store as blocksStore } from '@wordpress/blocks';
+import { isBlobURL, createBlobURL } from '@wordpress/blob';
+import { store as blocksStore, createBlock } from '@wordpress/blocks';
import { Placeholder } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import {
BlockIcon,
- MediaPlaceholder,
useBlockProps,
+ MediaPlaceholder,
store as blockEditorStore,
__experimentalUseBorderProps as useBorderProps,
__experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles,
@@ -31,6 +31,8 @@ import { useResizeObserver } from '@wordpress/compose';
import { unlock } from '../lock-unlock';
import { useUploadMediaFromBlobURL } from '../utils/hooks';
import Image from './image';
+import { isValidFileType } from './utils';
+import { useMaxWidthObserver } from './use-max-width-observer';
/**
* Module constants
@@ -108,11 +110,23 @@ export function ImageEdit( {
align,
metadata,
} = attributes;
+
const [ temporaryURL, setTemporaryURL ] = useState( attributes.blob );
- const [ contentResizeListener, { width: containerWidth } ] =
+ const containerRef = useRef();
+ // Only observe the max width from the parent container when the parent layout is not flex nor grid.
+ // This won't work for them because the container width changes with the image.
+ // TODO: Find a way to observe the container width for flex and grid layouts.
+ const isMaxWidthContainerWidth =
+ ! parentLayout ||
+ ( parentLayout.type !== 'flex' && parentLayout.type !== 'grid' );
+ const [ maxWidthObserver, maxContentWidth ] = useMaxWidthObserver();
+
+ const [ placeholderResizeListener, { width: placeholderWidth } ] =
useResizeObserver();
+ const isSmallContainer = placeholderWidth && placeholderWidth < 160;
+
const altRef = useRef();
useEffect( () => {
altRef.current = alt;
@@ -123,7 +137,7 @@ export function ImageEdit( {
captionRef.current = caption;
}, [ caption ] );
- const { __unstableMarkNextChangeAsNotPersistent } =
+ const { __unstableMarkNextChangeAsNotPersistent, replaceBlock } =
useDispatch( blockEditorStore );
useEffect( () => {
@@ -138,7 +152,12 @@ export function ImageEdit( {
}
}, [ __unstableMarkNextChangeAsNotPersistent, align, setAttributes ] );
- const { getSettings } = useSelect( blockEditorStore );
+ const {
+ getSettings,
+ getBlockRootClientId,
+ getBlockName,
+ canInsertBlockType,
+ } = useSelect( blockEditorStore );
const blockEditingMode = useBlockEditingMode();
const { createErrorNotice } = useDispatch( noticesStore );
@@ -152,7 +171,52 @@ export function ImageEdit( {
} );
}
+ function onSelectImagesList( images ) {
+ const win = containerRef.current?.ownerDocument.defaultView;
+
+ if ( images.every( ( file ) => file instanceof win.File ) ) {
+ /** @type {File[]} */
+ const files = images;
+ const rootClientId = getBlockRootClientId( clientId );
+
+ if ( files.some( ( file ) => ! isValidFileType( file ) ) ) {
+ // Copied from the same notice in the gallery block.
+ createErrorNotice(
+ __(
+ 'If uploading to a gallery all files need to be image formats'
+ ),
+ { id: 'gallery-upload-invalid-file', type: 'snackbar' }
+ );
+ }
+
+ const imageBlocks = files
+ .filter( ( file ) => isValidFileType( file ) )
+ .map( ( file ) =>
+ createBlock( 'core/image', {
+ blob: createBlobURL( file ),
+ } )
+ );
+
+ if ( getBlockName( rootClientId ) === 'core/gallery' ) {
+ replaceBlock( clientId, imageBlocks );
+ } else if ( canInsertBlockType( 'core/gallery', rootClientId ) ) {
+ const galleryBlock = createBlock(
+ 'core/gallery',
+ {},
+ imageBlocks
+ );
+
+ replaceBlock( clientId, galleryBlock );
+ }
+ }
+ }
+
function onSelectImage( media ) {
+ if ( Array.isArray( media ) ) {
+ onSelectImagesList( media );
+ return;
+ }
+
if ( ! media || ! media.url ) {
setAttributes( {
url: undefined,
@@ -296,7 +360,10 @@ export function ImageEdit( {
Object.keys( borderProps.style ).length > 0 ),
} );
- const blockProps = useBlockProps( { className: classes } );
+ const blockProps = useBlockProps( {
+ ref: containerRef,
+ className: classes,
+ } );
// Much of this description is duplicated from MediaPlaceholder.
const { lockUrlControls = false, lockUrlControlsMessage } = useSelect(
@@ -335,13 +402,17 @@ export function ImageEdit( {
[ borderProps.className ]:
!! borderProps.className && ! isSingleSelected,
} ) }
- withIllustration
- icon={ lockUrlControls ? pluginsIcon : icon }
- label={ __( 'Image' ) }
+ icon={
+ ! isSmallContainer &&
+ ( lockUrlControls ? pluginsIcon : icon )
+ }
+ withIllustration={ ! isSingleSelected || isSmallContainer }
+ label={ ! isSmallContainer && __( 'Image' ) }
instructions={
! lockUrlControls &&
+ ! isSmallContainer &&
__(
- 'Upload an image file, pick one from your media library, or add one with a URL.'
+ 'Upload or drag an image file here, or pick one from your library.'
)
}
style={ {
@@ -356,13 +427,12 @@ export function ImageEdit( {
...shadowProps.style,
} }
>
- { lockUrlControls ? (
-
- { lockUrlControlsMessage }
-
- ) : (
- content
- ) }
+ { lockUrlControls &&
+ ! isSmallContainer &&
+ lockUrlControlsMessage }
+
+ { ! lockUrlControls && ! isSmallContainer && content }
+ { placeholderResizeListener }
);
};
@@ -384,7 +454,7 @@ export function ImageEdit( {
clientId={ clientId }
blockEditingMode={ blockEditingMode }
parentLayoutType={ parentLayout?.type }
- containerWidth={ containerWidth }
+ maxContentWidth={ maxContentWidth }
/>
}
@@ -394,6 +464,7 @@ export function ImageEdit( {
placeholder={ placeholder }
accept="image/*"
allowedTypes={ ALLOWED_MEDIA_TYPES }
+ handleUpload={ ( files ) => files.length === 1 }
value={ { id, src } }
mediaPreview={ mediaPreview }
disableMediaButtons={ temporaryURL || url }
@@ -402,7 +473,7 @@ export function ImageEdit( {
{
// The listener cannot be placed as the first element as it will break the in-between inserter.
// See https://github.com/WordPress/gutenberg/blob/71134165868298fc15e22896d0c28b41b3755ff7/packages/block-editor/src/components/block-list/use-in-between-inserter.js#L120
- contentResizeListener
+ isSingleSelected && isMaxWidthContainerWidth && maxWidthObserver
}
>
);
diff --git a/packages/block-library/src/image/editor.scss b/packages/block-library/src/image/editor.scss
index db6928766c0b72..34f65d690d3d74 100644
--- a/packages/block-library/src/image/editor.scss
+++ b/packages/block-library/src/image/editor.scss
@@ -1,47 +1,8 @@
// Provide special styling for the placeholder.
// @todo this particular minimal style of placeholder could be componentized further.
.wp-block-image.wp-block-image {
-
- // Show Placeholder style on-select.
- &.is-selected .block-editor-media-placeholder {
- // Block UI appearance.
- color: $gray-900;
- background-color: $white;
- box-shadow: inset 0 0 0 $border-width $gray-900;
- border: none;
-
- // Disable any duotone filter applied in the selected state.
- filter: none !important;
-
- // @todo this should eventually be overridden by a custom border-radius set in the inspector.
- border-radius: $radius-block-ui;
-
- > svg {
- opacity: 0;
- }
-
- .components-placeholder__illustration {
- display: none;
- }
-
- &::before {
- opacity: 0;
- }
- }
- .block-bindings-media-placeholder-message {
- opacity: 0;
- }
- &.is-selected .block-bindings-media-placeholder-message {
- opacity: 1;
- }
-
- // Remove the transition while we still have a legacy placeholder style.
- // Otherwise the content jumps between the 1px placeholder border, and any inherited custom
- // parent border that may get applied when you deselect.
- .components-placeholder__label,
- .components-placeholder__instructions,
- .components-button {
- transition: none;
+ .block-editor-media-placeholder.is-small {
+ min-height: 60px;
}
}
@@ -152,6 +113,11 @@ figure.wp-block-image:not(.wp-block) {
text-align: center;
}
+// Relatively position the alignment container to support the content resizer.
+.wp-block[data-align]:has(> .wp-block-image) {
+ position: relative;
+}
+
.wp-block-image__crop-area {
position: relative;
max-width: 100%;
diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js
index 2de316f78ba5d8..60d83f89129070 100644
--- a/packages/block-library/src/image/image.js
+++ b/packages/block-library/src/image/image.js
@@ -108,7 +108,7 @@ export default function Image( {
clientId,
blockEditingMode,
parentLayoutType,
- containerWidth,
+ maxContentWidth,
} ) {
const {
url = '',
@@ -556,6 +556,24 @@ export default function Image( {
const showBlockControls = showUrlInput || allowCrop || showCoverControls;
+ const mediaReplaceFlow = isSingleSelected &&
+ ! isEditingImage &&
+ ! lockUrlControls && (
+
+ onSelectImage( undefined ) }
+ />
+
+ );
+
const controls = (
<>
{ showBlockControls && (
@@ -592,20 +610,6 @@ export default function Image( {
) }
) }
- { isSingleSelected && ! isEditingImage && ! lockUrlControls && (
-
- onSelectImage( undefined ) }
- />
-
- ) }
{ isSingleSelected && externalBlob && (
@@ -934,7 +938,7 @@ export default function Image( {
// @todo It would be good to revisit this once a content-width variable
// becomes available.
const maxWidthBuffer = maxWidth * 2.5;
- const maxContentWidth = containerWidth || maxWidthBuffer;
+ const maxResizeWidth = maxContentWidth || maxWidthBuffer;
let showRightHandle = false;
let showLeftHandle = false;
@@ -980,9 +984,9 @@ export default function Image( {
} }
showHandle={ isSingleSelected }
minWidth={ minWidth }
- maxWidth={ maxContentWidth }
+ maxWidth={ maxResizeWidth }
minHeight={ minHeight }
- maxHeight={ maxContentWidth / ratio }
+ maxHeight={ maxResizeWidth / ratio }
lockAspectRatio={ ratio }
enable={ {
top: false,
@@ -996,6 +1000,7 @@ export default function Image( {
// Clear hardcoded width if the resized width is close to the max-content width.
if (
+ maxContentWidth &&
// Only do this if the image is bigger than the container to prevent it from being squished.
// TODO: Remove this check if the image support setting 100% width.
naturalWidth >= maxContentWidth &&
@@ -1029,12 +1034,18 @@ export default function Image( {
}
if ( ! url && ! temporaryURL ) {
- // Add all controls if the image attributes are connected.
- return metadata?.bindings ? controls : sizeControls;
+ return (
+ <>
+ { mediaReplaceFlow }
+ { /* Add all controls if the image attributes are connected. */ }
+ { metadata?.bindings ? controls : sizeControls }
+ >
+ );
}
return (
<>
+ { mediaReplaceFlow }
{ controls }
{ img }
diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php
index 75f0d404e4820c..abbb03c0952452 100644
--- a/packages/block-library/src/image/index.php
+++ b/packages/block-library/src/image/index.php
@@ -72,7 +72,7 @@ function render_block_core_image( $attributes, $content, $block ) {
) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
- $module_url = gutenberg_url( '/build/interactivity/image.min.js' );
+ $module_url = gutenberg_url( '/build-module/block-library/image/view.min.js' );
}
wp_register_script_module(
diff --git a/packages/block-library/src/image/transforms.js b/packages/block-library/src/image/transforms.js
index 0e1dfb6ee9da44..347d2408280170 100644
--- a/packages/block-library/src/image/transforms.js
+++ b/packages/block-library/src/image/transforms.js
@@ -3,9 +3,6 @@
*/
import { createBlobURL, isBlobURL } from '@wordpress/blob';
import { createBlock, getBlockAttributes } from '@wordpress/blocks';
-import { dispatch } from '@wordpress/data';
-import { store as noticesStore } from '@wordpress/notices';
-import { __ } from '@wordpress/i18n';
export function stripFirstImage( attributes, { shortcode } ) {
const { body } = document.implementation.createHTMLDocument( '' );
@@ -138,26 +135,6 @@ const transforms = {
// creating a new gallery.
type: 'files',
isMatch( files ) {
- // The following check is intended to catch non-image files when dropped together with images.
- if (
- files.some(
- ( file ) => file.type.indexOf( 'image/' ) === 0
- ) &&
- files.some(
- ( file ) => file.type.indexOf( 'image/' ) !== 0
- )
- ) {
- const { createErrorNotice } = dispatch( noticesStore );
- createErrorNotice(
- __(
- 'If uploading to a gallery all files need to be image formats'
- ),
- {
- id: 'gallery-transform-invalid-file',
- type: 'snackbar',
- }
- );
- }
return files.every(
( file ) => file.type.indexOf( 'image/' ) === 0
);
diff --git a/packages/block-library/src/image/use-max-width-observer.js b/packages/block-library/src/image/use-max-width-observer.js
new file mode 100644
index 00000000000000..684392537fac7a
--- /dev/null
+++ b/packages/block-library/src/image/use-max-width-observer.js
@@ -0,0 +1,32 @@
+/**
+ * WordPress dependencies
+ */
+import { useRef } from '@wordpress/element';
+import { useResizeObserver } from '@wordpress/compose';
+
+function useMaxWidthObserver() {
+ const [ contentResizeListener, { width } ] = useResizeObserver();
+ const observerRef = useRef();
+
+ const maxWidthObserver = (
+
+ { contentResizeListener }
+
+ );
+
+ return [ maxWidthObserver, width ];
+}
+
+export { useMaxWidthObserver };
diff --git a/packages/block-library/src/image/utils.js b/packages/block-library/src/image/utils.js
index 1ef7973b4e57a3..1541c3daac3aab 100644
--- a/packages/block-library/src/image/utils.js
+++ b/packages/block-library/src/image/utils.js
@@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
-import { NEW_TAB_REL } from './constants';
+import { NEW_TAB_REL, ALLOWED_MEDIA_TYPES } from './constants';
/**
* Evaluates a CSS aspect-ratio property value as a number.
@@ -81,3 +81,15 @@ export function getImageSizeAttributes( image, size ) {
return {};
}
+
+/**
+ * Checks if the file has a valid file type.
+ *
+ * @param {File} file - The file to check.
+ * @return {boolean} - Returns true if the file has a valid file type, otherwise false.
+ */
+export function isValidFileType( file ) {
+ return ALLOWED_MEDIA_TYPES.some(
+ ( mediaType ) => file.type.indexOf( mediaType ) === 0
+ );
+}
diff --git a/packages/block-library/src/media-text/deprecated.js b/packages/block-library/src/media-text/deprecated.js
index 1cd1472f876c05..54c6f863311ffe 100644
--- a/packages/block-library/src/media-text/deprecated.js
+++ b/packages/block-library/src/media-text/deprecated.js
@@ -30,7 +30,7 @@ const v1ToV5ImageFillStyles = ( url, focalPoint ) => {
: {};
};
-const v6ImageFillStyles = ( url, focalPoint ) => {
+const v6ToV7ImageFillStyles = ( url, focalPoint ) => {
return url
? {
backgroundImage: `url(${ url })`,
@@ -198,6 +198,20 @@ const v6Attributes = {
},
};
+const v7Attributes = {
+ ...v6Attributes,
+ align: {
+ type: 'string',
+ // v7 changed the default for the `align` attribute.
+ default: 'none',
+ },
+ // New attribute.
+ useFeaturedImage: {
+ type: 'boolean',
+ default: false,
+ },
+};
+
const v4ToV5Supports = {
anchor: true,
align: [ 'wide', 'full' ],
@@ -237,6 +251,148 @@ const v6Supports = {
},
};
+const v7Supports = {
+ ...v6Supports,
+ __experimentalBorder: {
+ color: true,
+ radius: true,
+ style: true,
+ width: true,
+ __experimentalDefaultControls: {
+ color: true,
+ radius: true,
+ style: true,
+ width: true,
+ },
+ },
+ color: {
+ gradients: true,
+ heading: true,
+ link: true,
+ __experimentalDefaultControls: {
+ background: true,
+ text: true,
+ },
+ },
+ interactivity: {
+ clientNavigation: true,
+ },
+};
+
+// Version with 'none' as the default alignment.
+// See: https://github.com/WordPress/gutenberg/pull/64981
+const v7 = {
+ attributes: v7Attributes,
+ supports: v7Supports,
+ usesContext: [ 'postId', 'postType' ],
+ save( { attributes } ) {
+ const {
+ isStackedOnMobile,
+ mediaAlt,
+ mediaPosition,
+ mediaType,
+ mediaUrl,
+ mediaWidth,
+ mediaId,
+ verticalAlignment,
+ imageFill,
+ focalPoint,
+ linkClass,
+ href,
+ linkTarget,
+ rel,
+ } = attributes;
+ const mediaSizeSlug =
+ attributes.mediaSizeSlug || DEFAULT_MEDIA_SIZE_SLUG;
+ const newRel = ! rel ? undefined : rel;
+
+ const imageClasses = clsx( {
+ [ `wp-image-${ mediaId }` ]: mediaId && mediaType === 'image',
+ [ `size-${ mediaSizeSlug }` ]: mediaId && mediaType === 'image',
+ } );
+
+ let image = mediaUrl ? (
+
+ ) : null;
+
+ if ( href ) {
+ image = (
+
+ { image }
+
+ );
+ }
+
+ const mediaTypeRenders = {
+ image: () => image,
+ video: () => ,
+ };
+ const className = clsx( {
+ 'has-media-on-the-right': 'right' === mediaPosition,
+ 'is-stacked-on-mobile': isStackedOnMobile,
+ [ `is-vertically-aligned-${ verticalAlignment }` ]:
+ verticalAlignment,
+ 'is-image-fill': imageFill,
+ } );
+ const backgroundStyles = imageFill
+ ? v6ToV7ImageFillStyles( mediaUrl, focalPoint )
+ : {};
+
+ let gridTemplateColumns;
+ if ( mediaWidth !== DEFAULT_MEDIA_WIDTH ) {
+ gridTemplateColumns =
+ 'right' === mediaPosition
+ ? `auto ${ mediaWidth }%`
+ : `${ mediaWidth }% auto`;
+ }
+ const style = {
+ gridTemplateColumns,
+ };
+
+ if ( 'right' === mediaPosition ) {
+ return (
+
+
+
+ { ( mediaTypeRenders[ mediaType ] || noop )() }
+
+
+ );
+ }
+ return (
+
+
+ { ( mediaTypeRenders[ mediaType ] || noop )() }
+
+
+
+ );
+ },
+};
+
// Version with wide as the default alignment.
// See: https://github.com/WordPress/gutenberg/pull/48404
const v6 = {
@@ -301,7 +457,7 @@ const v6 = {
'is-image-fill': imageFill,
} );
const backgroundStyles = imageFill
- ? v6ImageFillStyles( mediaUrl, focalPoint )
+ ? v6ToV7ImageFillStyles( mediaUrl, focalPoint )
: {};
let gridTemplateColumns;
@@ -902,4 +1058,4 @@ const v1 = {
},
};
-export default [ v6, v5, v4, v3, v2, v1 ];
+export default [ v7, v6, v5, v4, v3, v2, v1 ];
diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js
index 2c020506dc889c..a946a499b26f21 100644
--- a/packages/block-library/src/media-text/edit.js
+++ b/packages/block-library/src/media-text/edit.js
@@ -213,11 +213,11 @@ function MediaTextEdit( {
[ isSelected, mediaId ]
);
- const refMediaContainer = useRef();
+ const refMedia = useRef();
const imperativeFocalPointPreview = ( value ) => {
- const { style } = refMediaContainer.current.resizable;
+ const { style } = refMedia.current;
const { x, y } = value;
- style.backgroundPosition = `${ x * 100 }% ${ y * 100 }%`;
+ style.objectPosition = `${ x * 100 }% ${ y * 100 }%`;
};
const [ temporaryMediaWidth, setTemporaryMediaWidth ] = useState( null );
@@ -243,7 +243,7 @@ function MediaTextEdit( {
'is-selected': isSelected,
'is-stacked-on-mobile': isStackedOnMobile,
[ `is-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment,
- 'is-image-fill': imageFill,
+ 'is-image-fill-element': imageFill,
} );
const widthString = `${ temporaryMediaWidth || mediaWidth }%`;
const gridTemplateColumns =
@@ -480,7 +480,7 @@ function MediaTextEdit( {
onSelectMedia={ onSelectMedia }
onWidthChange={ onWidthChange }
commitWidthChange={ commitWidthChange }
- ref={ refMediaContainer }
+ refMedia={ refMedia }
enableResize={ blockEditingMode === 'default' }
toggleUseFeaturedImage={ toggleUseFeaturedImage }
{ ...{
diff --git a/packages/block-library/src/media-text/editor.scss b/packages/block-library/src/media-text/editor.scss
index b417fb951e99d0..639ceaed8ea99c 100644
--- a/packages/block-library/src/media-text/editor.scss
+++ b/packages/block-library/src/media-text/editor.scss
@@ -27,7 +27,9 @@
}
.wp-block-media-text.is-image-fill .editor-media-container__resizer,
-.wp-block-media-text.is-image-fill .components-placeholder.has-illustration {
+.wp-block-media-text.is-image-fill .components-placeholder.has-illustration,
+.wp-block-media-text.is-image-fill-element .editor-media-container__resizer,
+.wp-block-media-text.is-image-fill-element .components-placeholder.has-illustration {
// The resizer sets an inline height but for the image fill we set it to full height.
height: 100% !important;
}
diff --git a/packages/block-library/src/media-text/image-fill.js b/packages/block-library/src/media-text/image-fill.js
new file mode 100644
index 00000000000000..c277c49b5c310b
--- /dev/null
+++ b/packages/block-library/src/media-text/image-fill.js
@@ -0,0 +1,11 @@
+export function imageFillStyles( url, focalPoint ) {
+ return url
+ ? {
+ objectPosition: focalPoint
+ ? `${ Math.round( focalPoint.x * 100 ) }% ${ Math.round(
+ focalPoint.y * 100
+ ) }%`
+ : `50% 50%`,
+ }
+ : {};
+}
diff --git a/packages/block-library/src/media-text/index.php b/packages/block-library/src/media-text/index.php
index 87be164a04bb99..b65137b150ba53 100644
--- a/packages/block-library/src/media-text/index.php
+++ b/packages/block-library/src/media-text/index.php
@@ -29,15 +29,32 @@ function render_block_core_media_text( $attributes, $content ) {
return $content;
}
+ $has_media_on_right = isset( $attributes['mediaPosition'] ) && 'right' === $attributes['mediaPosition'];
+ $image_fill = isset( $attributes['imageFill'] ) && $attributes['imageFill'];
+ $focal_point = isset( $attributes['focalPoint'] ) ? round( $attributes['focalPoint']['x'] * 100 ) . '% ' . round( $attributes['focalPoint']['y'] * 100 ) . '%' : '50% 50%';
+ $unique_id = 'wp-block-media-text__media-' . wp_unique_id();
+
+ $block_tag_processor = new WP_HTML_Tag_Processor( $content );
+ $block_query = array(
+ 'tag_name' => 'div',
+ 'class_name' => 'wp-block-media-text',
+ );
+
+ while ( $block_tag_processor->next_tag( $block_query ) ) {
+ if ( $image_fill ) {
+ // The markup below does not work with the deprecated `is-image-fill` class.
+ $block_tag_processor->remove_class( 'is-image-fill' );
+ $block_tag_processor->add_class( 'is-image-fill-element' );
+ }
+ }
+
+ $content = $block_tag_processor->get_updated_html();
+
$media_tag_processor = new WP_HTML_Tag_Processor( $content );
$wrapping_figure_query = array(
'tag_name' => 'figure',
'class_name' => 'wp-block-media-text__media',
);
- $has_media_on_right = isset( $attributes['mediaPosition'] ) && 'right' === $attributes['mediaPosition'];
- $image_fill = isset( $attributes['imageFill'] ) && $attributes['imageFill'];
- $focal_point = isset( $attributes['focalPoint'] ) ? round( $attributes['focalPoint']['x'] * 100 ) . '% ' . round( $attributes['focalPoint']['y'] * 100 ) . '%' : '50% 50%';
- $unique_id = 'wp-block-media-text__media-' . wp_unique_id();
if ( $has_media_on_right ) {
// Loop through all the figure tags and set a bookmark on the last figure tag.
@@ -46,59 +63,52 @@ function render_block_core_media_text( $attributes, $content ) {
}
if ( $media_tag_processor->has_bookmark( 'last_figure' ) ) {
$media_tag_processor->seek( 'last_figure' );
- if ( $image_fill ) {
- $media_tag_processor->set_attribute( 'style', 'background-image:url(' . esc_url( $current_featured_image ) . ');background-position:' . $focal_point . ';' );
- } else {
- // Insert a unique ID to identify the figure tag.
- $media_tag_processor->set_attribute( 'id', $unique_id );
- }
+ // Insert a unique ID to identify the figure tag.
+ $media_tag_processor->set_attribute( 'id', $unique_id );
}
} else {
if ( $media_tag_processor->next_tag( $wrapping_figure_query ) ) {
- if ( $image_fill ) {
- $media_tag_processor->set_attribute( 'style', 'background-image:url(' . esc_url( $current_featured_image ) . ');background-position:' . $focal_point . ';' );
- } else {
- // Insert a unique ID to identify the figure tag.
- $media_tag_processor->set_attribute( 'id', $unique_id );
- }
+ // Insert a unique ID to identify the figure tag.
+ $media_tag_processor->set_attribute( 'id', $unique_id );
}
}
$content = $media_tag_processor->get_updated_html();
- // If the image is not set to fill, add the image tag inside the figure tag,
- // and update the image attributes in order to display the featured image.
- if ( ! $image_fill ) {
- $media_size_slug = isset( $attributes['mediaSizeSlug'] ) ? $attributes['mediaSizeSlug'] : 'full';
- $image_tag = ' ';
- $content = preg_replace(
- '/()/',
- '$1' . $image_tag,
- $content
- );
+ // Add the image tag inside the figure tag, and update the image attributes
+ // in order to display the featured image.
+ $media_size_slug = isset( $attributes['mediaSizeSlug'] ) ? $attributes['mediaSizeSlug'] : 'full';
+ $image_tag = ' ';
+ $content = preg_replace(
+ '/()/',
+ '$1' . $image_tag,
+ $content
+ );
- $image_tag_processor = new WP_HTML_Tag_Processor( $content );
+ $image_tag_processor = new WP_HTML_Tag_Processor( $content );
+ if ( $image_tag_processor->next_tag(
+ array(
+ 'tag_name' => 'figure',
+ 'id' => $unique_id,
+ )
+ ) ) {
+ // The ID is only used to ensure that the correct figure tag is selected,
+ // and can now be removed.
+ $image_tag_processor->remove_attribute( 'id' );
if ( $image_tag_processor->next_tag(
array(
- 'tag_name' => 'figure',
- 'id' => $unique_id,
+ 'tag_name' => 'img',
+ 'class_name' => 'wp-block-media-text__featured_image',
)
) ) {
- // The ID is only used to ensure that the correct figure tag is selected,
- // and can now be removed.
- $image_tag_processor->remove_attribute( 'id' );
- if ( $image_tag_processor->next_tag(
- array(
- 'tag_name' => 'img',
- 'class_name' => 'wp-block-media-text__featured_image',
- )
- ) ) {
- $image_tag_processor->set_attribute( 'src', esc_url( $current_featured_image ) );
- $image_tag_processor->set_attribute( 'class', 'wp-image-' . get_post_thumbnail_id() . ' size-' . $media_size_slug );
- $image_tag_processor->set_attribute( 'alt', trim( strip_tags( get_post_meta( get_post_thumbnail_id(), '_wp_attachment_image_alt', true ) ) ) );
-
- $content = $image_tag_processor->get_updated_html();
+ $image_tag_processor->set_attribute( 'src', esc_url( $current_featured_image ) );
+ $image_tag_processor->set_attribute( 'class', 'wp-image-' . get_post_thumbnail_id() . ' size-' . $media_size_slug );
+ $image_tag_processor->set_attribute( 'alt', trim( strip_tags( get_post_meta( get_post_thumbnail_id(), '_wp_attachment_image_alt', true ) ) ) );
+ if ( $image_fill ) {
+ $image_tag_processor->set_attribute( 'style', 'object-position:' . $focal_point . ';' );
}
+
+ $content = $image_tag_processor->get_updated_html();
}
}
diff --git a/packages/block-library/src/media-text/media-container.js b/packages/block-library/src/media-text/media-container.js
index 986ecad6cc7e69..735aaf73b88aae 100644
--- a/packages/block-library/src/media-text/media-container.js
+++ b/packages/block-library/src/media-text/media-container.js
@@ -22,25 +22,17 @@ import { isBlobURL } from '@wordpress/blob';
import { store as noticesStore } from '@wordpress/notices';
import { media as icon } from '@wordpress/icons';
+/**
+ * Internal dependencies
+ */
+import { imageFillStyles } from './image-fill';
+
/**
* Constants
*/
const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ];
const noop = () => {};
-export function imageFillStyles( url, focalPoint ) {
- return url
- ? {
- backgroundImage: `url(${ url })`,
- backgroundPosition: focalPoint
- ? `${ Math.round( focalPoint.x * 100 ) }% ${ Math.round(
- focalPoint.y * 100
- ) }%`
- : `50% 50%`,
- }
- : {};
-}
-
const ResizableBoxContainer = forwardRef(
( { isSelected, isStackedOnMobile, ...props }, ref ) => {
const isMobile = useViewportMatch( 'small', '<' );
@@ -129,6 +121,7 @@ function MediaContainer( props, ref ) {
useFeaturedImage,
featuredImageURL,
featuredImageAlt,
+ refMedia,
} = props;
const isTemporaryMedia = ! mediaId && isBlobURL( mediaUrl );
@@ -151,7 +144,7 @@ function MediaContainer( props, ref ) {
left: enableResize && mediaPosition === 'right',
};
- const backgroundStyles =
+ const positionStyles =
mediaType === 'image' && imageFill
? imageFillStyles( mediaUrl || featuredImageURL, focalPoint )
: {};
@@ -159,11 +152,23 @@ function MediaContainer( props, ref ) {
const mediaTypeRenderers = {
image: () =>
useFeaturedImage && featuredImageURL ? (
-
+
) : (
- mediaUrl &&
+ mediaUrl && (
+
+ )
),
- video: () => ,
+ video: () => ,
};
return (
@@ -174,7 +179,6 @@ function MediaContainer( props, ref ) {
'editor-media-container__resizer',
{ 'is-transient': isTemporaryMedia }
) }
- style={ backgroundStyles }
size={ { width: mediaWidth + '%' } }
minWidth="10%"
maxWidth="100%"
@@ -203,6 +207,7 @@ function MediaContainer( props, ref ) {
{ ! featuredImageURL && useFeaturedImage && (
) }
diff --git a/packages/block-library/src/media-text/media-container.native.js b/packages/block-library/src/media-text/media-container.native.js
index eaee027c061856..3bf4fbf25d8f2a 100644
--- a/packages/block-library/src/media-text/media-container.native.js
+++ b/packages/block-library/src/media-text/media-container.native.js
@@ -44,8 +44,6 @@ const ICON_TYPE = {
RETRY: 'retry',
};
-export { imageFillStyles } from './media-container.js';
-
class MediaContainer extends Component {
constructor() {
super( ...arguments );
diff --git a/packages/block-library/src/media-text/save.js b/packages/block-library/src/media-text/save.js
index 5bf9517adae669..3e660d94e789ee 100644
--- a/packages/block-library/src/media-text/save.js
+++ b/packages/block-library/src/media-text/save.js
@@ -11,7 +11,7 @@ import { useInnerBlocksProps, useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
-import { imageFillStyles } from './media-container';
+import { imageFillStyles } from './image-fill';
import { DEFAULT_MEDIA_SIZE_SLUG } from './constants';
const DEFAULT_MEDIA_WIDTH = 50;
@@ -42,11 +42,16 @@ export default function save( { attributes } ) {
[ `size-${ mediaSizeSlug }` ]: mediaId && mediaType === 'image',
} );
+ const positionStyles = imageFill
+ ? imageFillStyles( mediaUrl, focalPoint )
+ : {};
+
let image = mediaUrl ? (
) : null;
@@ -71,11 +76,8 @@ export default function save( { attributes } ) {
'has-media-on-the-right': 'right' === mediaPosition,
'is-stacked-on-mobile': isStackedOnMobile,
[ `is-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment,
- 'is-image-fill': imageFill,
+ 'is-image-fill-element': imageFill,
} );
- const backgroundStyles = imageFill
- ? imageFillStyles( mediaUrl, focalPoint )
- : {};
let gridTemplateColumns;
if ( mediaWidth !== DEFAULT_MEDIA_WIDTH ) {
@@ -96,10 +98,7 @@ export default function save( { attributes } ) {
className: 'wp-block-media-text__content',
} ) }
/>
-
+
{ ( mediaTypeRenders[ mediaType ] || noop )() }
@@ -107,10 +106,7 @@ export default function save( { attributes } ) {
}
return (
-
+
{ ( mediaTypeRenders[ mediaType ] || noop )() }
.wp-block-media-text__media {
height: 100%;
min-height: 250px;
@@ -102,6 +103,26 @@
clip: rect(0, 0, 0, 0);
border: 0;
}
+
+/* Image fill for versions 8 and onwards */
+.wp-block-media-text.is-image-fill-element > .wp-block-media-text__media {
+ position: relative;
+ height: 100%;
+ min-height: 250px;
+}
+
+.wp-block-media-text.is-image-fill-element > .wp-block-media-text__media > a {
+ display: block;
+ height: 100%;
+}
+
+.wp-block-media-text.is-image-fill-element > .wp-block-media-text__media img {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
/*
* Here we here not able to use a mobile first CSS approach.
* Custom widths are set using inline styles, and on mobile,
diff --git a/packages/block-library/src/media-text/test/image-fill.js b/packages/block-library/src/media-text/test/image-fill.js
new file mode 100644
index 00000000000000..4862fcbfb02453
--- /dev/null
+++ b/packages/block-library/src/media-text/test/image-fill.js
@@ -0,0 +1,19 @@
+/**
+ * Internal dependencies
+ */
+import { imageFillStyles } from '../image-fill';
+
+describe( 'imageFillStyles()', () => {
+ it( 'should return centered object position', () => {
+ const { objectPosition } = imageFillStyles( 'image.jpg' );
+ expect( objectPosition ).toBe( '50% 50%' );
+ } );
+
+ it( 'should return custom object position', () => {
+ const { objectPosition } = imageFillStyles( 'image.jpg', {
+ x: 0.56,
+ y: 0.57,
+ } );
+ expect( objectPosition ).toBe( '56% 57%' );
+ } );
+} );
diff --git a/packages/block-library/src/media-text/test/media-container.js b/packages/block-library/src/media-text/test/media-container.js
deleted file mode 100644
index 582ec2be8d5ad8..00000000000000
--- a/packages/block-library/src/media-text/test/media-container.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Internal dependencies
- */
-import { imageFillStyles } from '../media-container';
-
-describe( 'imageFillStyles()', () => {
- it( 'should return image url', () => {
- const { backgroundImage } = imageFillStyles( 'image.jpg' );
- expect( backgroundImage ).toBe( 'url(image.jpg)' );
- } );
-
- it( 'should return centered background position', () => {
- const { backgroundPosition } = imageFillStyles( 'image.jpg' );
- expect( backgroundPosition ).toBe( '50% 50%' );
- } );
-
- it( 'should return custom background position', () => {
- const { backgroundPosition } = imageFillStyles( 'image.jpg', {
- x: 0.56,
- y: 0.57,
- } );
- expect( backgroundPosition ).toBe( '56% 57%' );
- } );
-} );
diff --git a/packages/block-library/src/missing/edit.js b/packages/block-library/src/missing/edit.js
index 70b032cb9a2fe8..e7744839b08889 100644
--- a/packages/block-library/src/missing/edit.js
+++ b/packages/block-library/src/missing/edit.js
@@ -50,8 +50,7 @@ export default function MissingEdit( { attributes, clientId } ) {
const convertToHtmlButton = (
{
return (
{
diff --git a/packages/block-library/src/navigation/edit/deleted-navigation-warning.js b/packages/block-library/src/navigation/edit/deleted-navigation-warning.js
index 8950a700ace513..22d1e339c5c004 100644
--- a/packages/block-library/src/navigation/edit/deleted-navigation-warning.js
+++ b/packages/block-library/src/navigation/edit/deleted-navigation-warning.js
@@ -2,29 +2,32 @@
* WordPress dependencies
*/
import { Warning } from '@wordpress/block-editor';
-import { Button } from '@wordpress/components';
+import { Button, Notice } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { createInterpolateElement } from '@wordpress/element';
-function DeletedNavigationWarning( { onCreateNew } ) {
- return (
-
- { createInterpolateElement(
- __(
- 'Navigation Menu has been deleted or is unavailable. Create a new Menu? '
- ),
- {
- button: (
-
- ),
- }
- ) }
-
+function DeletedNavigationWarning( { onCreateNew, isNotice = false } ) {
+ const message = createInterpolateElement(
+ __(
+ 'Navigation Menu has been deleted or is unavailable. Create a new Menu? '
+ ),
+ {
+ button: (
+
+ ),
+ }
+ );
+
+ return isNotice ? (
+
+ { message }
+
+ ) : (
+ { message }
);
}
diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js
index c9ce17eea6b1ab..ae7dd60bd0c5ba 100644
--- a/packages/block-library/src/navigation/edit/index.js
+++ b/packages/block-library/src/navigation/edit/index.js
@@ -591,8 +591,7 @@ function Navigation( {
{ isResponsive && (
<>
{
setOverlayMenuPreview(
diff --git a/packages/block-library/src/navigation/edit/menu-inspector-controls.js b/packages/block-library/src/navigation/edit/menu-inspector-controls.js
index e21655eef90715..8f6c2e47fe7759 100644
--- a/packages/block-library/src/navigation/edit/menu-inspector-controls.js
+++ b/packages/block-library/src/navigation/edit/menu-inspector-controls.js
@@ -94,7 +94,9 @@ const MainContent = ( {
const { navigationMenu } = useNavigationMenu( currentMenuId );
if ( currentMenuId && isNavigationMenuMissing ) {
- return ;
+ return (
+
+ );
}
if ( isLoading ) {
diff --git a/packages/block-library/src/navigation/edit/navigation-menu-delete-control.js b/packages/block-library/src/navigation/edit/navigation-menu-delete-control.js
index a75ad1dd00cce2..e95e07ab778c2f 100644
--- a/packages/block-library/src/navigation/edit/navigation-menu-delete-control.js
+++ b/packages/block-library/src/navigation/edit/navigation-menu-delete-control.js
@@ -19,8 +19,7 @@ export default function NavigationMenuDeleteControl( { onDelete } ) {
return (
<>
diff --git a/packages/block-library/src/navigation/edit/responsive-wrapper.js b/packages/block-library/src/navigation/edit/responsive-wrapper.js
index 328152723026f5..886e808965ce4b 100644
--- a/packages/block-library/src/navigation/edit/responsive-wrapper.js
+++ b/packages/block-library/src/navigation/edit/responsive-wrapper.js
@@ -79,8 +79,7 @@ export default function ResponsiveWrapper( {
<>
{ ! isOpen && (
onToggle( false ) }
diff --git a/packages/block-library/src/navigation/editor.scss b/packages/block-library/src/navigation/editor.scss
index 619b8dc6f0c7bd..1d37186e4ae98b 100644
--- a/packages/block-library/src/navigation/editor.scss
+++ b/packages/block-library/src/navigation/editor.scss
@@ -104,7 +104,6 @@
background: $gray-900;
padding: 0;
width: $button-size-small;
- border-radius: $radius-block-ui;
margin-right: 0;
margin-left: auto;
}
@@ -326,7 +325,7 @@ $color-control-label-height: 20px;
}
.wp-block-navigation-placeholder__controls {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
background-color: $white;
box-shadow: inset 0 0 0 $border-width $gray-900;
display: none;
@@ -564,7 +563,11 @@ body.editor-styles-wrapper .wp-block-navigation__responsive-container.is-menu-op
width: 100%;
background-color: $gray-100;
padding: 0 $grid-unit-30;
- height: $grid-unit-40 * 2;
+
+ // Adding !important to override default 40px size.
+ // Ref - https://github.com/WordPress/gutenberg/pull/65075#discussion_r1746282734
+ height: $grid-unit-40 * 2 !important;
+
margin-bottom: $grid-unit-15;
&.open {
diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php
index ccadd5c4a222d4..ec72b03b6906f0 100644
--- a/packages/block-library/src/navigation/index.php
+++ b/packages/block-library/src/navigation/index.php
@@ -624,7 +624,7 @@ private static function handle_view_script_module_loading( $attributes, $block,
if ( static::is_interactive( $attributes, $inner_blocks ) ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
- $module_url = gutenberg_url( '/build/interactivity/navigation.min.js' );
+ $module_url = gutenberg_url( '/build-module/block-library/navigation/view.min.js' );
}
wp_register_script_module(
diff --git a/packages/block-library/src/page-list/convert-to-links-modal.js b/packages/block-library/src/page-list/convert-to-links-modal.js
index b56b3bf7c6b4f1..f2ab425a1ef10a 100644
--- a/packages/block-library/src/page-list/convert-to-links-modal.js
+++ b/packages/block-library/src/page-list/convert-to-links-modal.js
@@ -32,16 +32,14 @@ export function ConvertToLinksModal( { onClick, onClose, disabled } ) {
{ __( 'Cancel' ) }
{ convertDescription }
{
if ( 'closed' === commentStatus ) {
const actions = [
setCommentStatus( 'open' ) }
variant="primary"
diff --git a/packages/block-library/src/post-featured-image/edit.js b/packages/block-library/src/post-featured-image/edit.js
index ff777f707330df..dff34379e4f3b4 100644
--- a/packages/block-library/src/post-featured-image/edit.js
+++ b/packages/block-library/src/post-featured-image/edit.js
@@ -308,8 +308,7 @@ export default function PostFeaturedImageEdit( {
mediaLibraryButton={ ( { open } ) => {
return (
a"
+ }
}
diff --git a/packages/block-library/src/post-title/style.scss b/packages/block-library/src/post-title/style.scss
index abeb4454bcb2c5..5d0e3407105bd4 100644
--- a/packages/block-library/src/post-title/style.scss
+++ b/packages/block-library/src/post-title/style.scss
@@ -3,6 +3,30 @@
// This block has customizable padding, border-box makes that more predictable.
box-sizing: border-box;
+ &[style*="font-weight"] :where(a) {
+ font-weight: inherit;
+ }
+ &[class*="-font-family"] :where(a),
+ &[style*="font-family"] :where(a) {
+ font-family: inherit;
+ }
+ &[class*="-font-size"] :where(a),
+ &[style*="font-size"] :where(a) {
+ font-size: inherit;
+ }
+ &[style*="line-height"] :where(a) {
+ line-height: inherit;
+ }
+ &[style*="font-style"] :where(a) {
+ font-style: inherit;
+ }
+ &[style*="letter-spacing"] :where(a) {
+ letter-spacing: inherit;
+ }
+ &[style*="text-decoration"] :where(a) {
+ text-decoration: inherit;
+ }
+
a {
display: inline-block;
}
diff --git a/packages/block-library/src/query-pagination/editor.scss b/packages/block-library/src/query-pagination/editor.scss
index 0b755d155091f3..ed8a2cc47a30f7 100644
--- a/packages/block-library/src/query-pagination/editor.scss
+++ b/packages/block-library/src/query-pagination/editor.scss
@@ -1,5 +1,3 @@
-$pagination-margin: 0.5em;
-
// Center flex items. This has an equivalent in style.scss.
.wp-block[data-align="center"] > .wp-block-query-pagination {
justify-content: center;
@@ -14,21 +12,3 @@ $pagination-margin: 0.5em;
}
}
-.wp-block-query-pagination {
- > .wp-block-query-pagination-next,
- > .wp-block-query-pagination-previous,
- > .wp-block-query-pagination-numbers {
- // Override editor auto block margins.
- margin-left: 0;
- margin-top: $pagination-margin;
-
- /*rtl:ignore*/
- margin-right: $pagination-margin;
- margin-bottom: $pagination-margin;
-
- &:last-child {
- /*rtl:ignore*/
- margin-right: 0;
- }
- }
-}
diff --git a/packages/block-library/src/query-pagination/style.scss b/packages/block-library/src/query-pagination/style.scss
index a7f2832af48c7a..e949c766e56511 100644
--- a/packages/block-library/src/query-pagination/style.scss
+++ b/packages/block-library/src/query-pagination/style.scss
@@ -1,19 +1,4 @@
-$pagination-margin: 0.5em;
.wp-block-query-pagination {
- // Increased specificity to override blocks default margin.
- > .wp-block-query-pagination-next,
- > .wp-block-query-pagination-previous,
- > .wp-block-query-pagination-numbers {
- /*rtl:ignore*/
- margin-right: $pagination-margin;
- margin-bottom: $pagination-margin;
-
- &:last-child {
- /*rtl:ignore*/
- margin-right: 0;
- }
- }
-
// This moves the next link to the right side of the container,
// which is important when it's the only block displayed
// and the block has a "space-between" justification.
diff --git a/packages/block-library/src/query/edit/enhanced-pagination-modal.js b/packages/block-library/src/query/edit/enhanced-pagination-modal.js
index f33c45646e536b..332cc337bb0f67 100644
--- a/packages/block-library/src/query/edit/enhanced-pagination-modal.js
+++ b/packages/block-library/src/query/edit/enhanced-pagination-modal.js
@@ -42,7 +42,7 @@ export default function EnhancedPaginationModal( {
};
let notice = __(
- 'If you still want to prevent full page reloads, remove that block, then disable "Force page reload" again in the Query Block settings.'
+ 'If you still want to prevent full page reloads, remove that block, then disable "Reload full page" again in the Query Block settings.'
);
if ( hasBlocksFromPlugins ) {
notice =
@@ -63,7 +63,7 @@ export default function EnhancedPaginationModal( {
return (
isOpen && (
{ notice }
diff --git a/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js b/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js
index e1001cda4dab39..9d47d67e61d781 100644
--- a/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js
+++ b/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js
@@ -25,11 +25,11 @@ export default function EnhancedPaginationControl( {
);
} else if ( enhancedPagination ) {
help = __(
- "Browsing between pages won't require a full page reload, unless non-compatible blocks are detected."
+ 'Reload the full page—instead of just the posts list—when visitors navigate between pages.'
);
} else if ( hasUnsupportedBlocks ) {
help = __(
- "Force page reload can't be disabled because there are non-compatible blocks inside the Query block."
+ 'Enhancement disabled because there are non-compatible blocks inside the Query block.'
);
}
@@ -37,7 +37,7 @@ export default function EnhancedPaginationControl( {
<>
) }
-
) }
{ ! inherit && showDisplayPanel && (
diff --git a/packages/block-library/src/query/edit/query-content.js b/packages/block-library/src/query/edit/query-content.js
index 949ffc653420fe..4624b3b96049e9 100644
--- a/packages/block-library/src/query/edit/query-content.js
+++ b/packages/block-library/src/query/edit/query-content.js
@@ -18,6 +18,7 @@ import { store as coreStore } from '@wordpress/core-data';
/**
* Internal dependencies
*/
+import EnhancedPaginationControl from './inspector-controls/enhanced-pagination-control';
import QueryToolbar from './query-toolbar';
import QueryInspectorControls from './inspector-controls';
import EnhancedPaginationModal from './enhanced-pagination-modal';
@@ -36,6 +37,7 @@ export default function QueryContent( {
queryId,
query,
displayLayout,
+ enhancedPagination,
tagName: TagName = 'div',
query: { inherit } = {},
} = attributes;
@@ -161,6 +163,11 @@ export default function QueryContent( {
}
help={ htmlElementMessages[ TagName ] }
/>
+
>
diff --git a/packages/block-library/src/query/edit/query-placeholder.js b/packages/block-library/src/query/edit/query-placeholder.js
index 098c29f0ec512b..631eb64de07157 100644
--- a/packages/block-library/src/query/edit/query-placeholder.js
+++ b/packages/block-library/src/query/edit/query-placeholder.js
@@ -79,8 +79,7 @@ export default function QueryPlaceholder( {
>
{ !! hasPatterns && (
@@ -89,8 +88,7 @@ export default function QueryPlaceholder( {
) }
{
setIsStartingBlank( true );
diff --git a/packages/block-library/src/query/index.php b/packages/block-library/src/query/index.php
index 6cc57dc08388c6..d10db26529854e 100644
--- a/packages/block-library/src/query/index.php
+++ b/packages/block-library/src/query/index.php
@@ -26,7 +26,7 @@ function render_block_core_query( $attributes, $content, $block ) {
if ( $is_interactive ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
- $module_url = gutenberg_url( '/build/interactivity/query.min.js' );
+ $module_url = gutenberg_url( '/build-module/block-library/query/view.min.js' );
}
wp_register_script_module(
diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php
index 39b8591c86600f..fb09cdd36406e8 100644
--- a/packages/block-library/src/search/index.php
+++ b/packages/block-library/src/search/index.php
@@ -82,7 +82,7 @@ function render_block_core_search( $attributes ) {
if ( $is_expandable_searchfield ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
- $module_url = gutenberg_url( '/build/interactivity/search.min.js' );
+ $module_url = gutenberg_url( '/build-module/block-library/search/view.min.js' );
}
wp_register_script_module(
diff --git a/packages/block-library/src/site-logo/edit.js b/packages/block-library/src/site-logo/edit.js
index edf8bbccfeb652..dc95d5906d7345 100644
--- a/packages/block-library/src/site-logo/edit.js
+++ b/packages/block-library/src/site-logo/edit.js
@@ -423,7 +423,7 @@ export default function LogoEdit( {
context: 'view',
} );
const _isRequestingMediaItem =
- _siteLogoId &&
+ !! _siteLogoId &&
! select( coreStore ).hasFinishedResolution( 'getMedia', [
_siteLogoId,
{ context: 'view' },
@@ -627,8 +627,7 @@ export default function LogoEdit( {
render={ ( { open } ) => (
@@ -674,8 +673,7 @@ export default function LogoEdit( {
mediaLibraryButton={ ( { open } ) => {
return (
span, .wp-block-site-title > a"
+ }
}
diff --git a/packages/block-library/src/site-title/style.scss b/packages/block-library/src/site-title/style.scss
index f292326cc3259f..8647f3d9b2947c 100644
--- a/packages/block-library/src/site-title/style.scss
+++ b/packages/block-library/src/site-title/style.scss
@@ -2,6 +2,30 @@
// This block has customizable padding, border-box makes that more predictable.
box-sizing: border-box;
+ &[style*="font-weight"] :where(a) {
+ font-weight: inherit;
+ }
+ &[class*="-font-family"] :where(a),
+ &[style*="font-family"] :where(a) {
+ font-family: inherit;
+ }
+ &[class*="-font-size"] :where(a),
+ &[style*="font-size"] :where(a) {
+ font-size: inherit;
+ }
+ &[style*="line-height"] :where(a) {
+ line-height: inherit;
+ }
+ &[style*="font-style"] :where(a) {
+ font-style: inherit;
+ }
+ &[style*="letter-spacing"] :where(a) {
+ letter-spacing: inherit;
+ }
+ &[style*="text-decoration"] :where(a) {
+ text-decoration: inherit;
+ }
+
:where(a) {
color: inherit;
}
diff --git a/packages/block-library/src/social-link/edit.js b/packages/block-library/src/social-link/edit.js
index f6d46731a399f3..65d85c0400116f 100644
--- a/packages/block-library/src/social-link/edit.js
+++ b/packages/block-library/src/social-link/edit.js
@@ -81,7 +81,7 @@ const SocialLinkURLPopover = ( {
/>
+ select( blockEditorStore ).getSettings().onNavigateToEntityRecord,
+ []
+ );
+
const [ blocks, onInput, onChange ] = useEntityBlockEditor(
'postType',
'wp_template_part',
@@ -114,7 +120,20 @@ function EditableTemplatePartInnerBlocks( {
layout: useLayout( layout ),
} );
- return ;
+ const blockEditingMode = useBlockEditingMode();
+
+ const customProps =
+ blockEditingMode === 'contentOnly' && onNavigateToEntityRecord
+ ? {
+ onDoubleClick: () =>
+ onNavigateToEntityRecord( {
+ postId: id,
+ postType: 'wp_template_part',
+ } ),
+ }
+ : {};
+
+ return ;
}
export default function TemplatePartInnerBlocks( {
diff --git a/packages/block-library/src/template-part/edit/placeholder.js b/packages/block-library/src/template-part/edit/placeholder.js
index d54401c2327fbb..0b8a980b8b7e3a 100644
--- a/packages/block-library/src/template-part/edit/placeholder.js
+++ b/packages/block-library/src/template-part/edit/placeholder.js
@@ -75,8 +75,7 @@ export default function TemplatePartPlaceholder( {
{ ! isResolving &&
!! ( templateParts.length || blockPatterns.length ) && (
@@ -86,8 +85,7 @@ export default function TemplatePartPlaceholder( {
{ ! isResolving && isBlockBasedTheme && canCreateTemplatePart && (
{
setShowTitleModal( true );
diff --git a/packages/block-library/src/video/edit.js b/packages/block-library/src/video/edit.js
index d72dd5463cde33..ba6b4c869aefcb 100644
--- a/packages/block-library/src/video/edit.js
+++ b/packages/block-library/src/video/edit.js
@@ -232,8 +232,7 @@ function VideoEdit( {
}
render={ ( { open } ) => (
{ !! poster && (
diff --git a/packages/block-library/src/video/tracks-editor.js b/packages/block-library/src/video/tracks-editor.js
index 079ffd9fc46f82..d01e365985bc96 100644
--- a/packages/block-library/src/video/tracks-editor.js
+++ b/packages/block-library/src/video/tracks-editor.js
@@ -58,8 +58,7 @@ function TrackList( { tracks, onEditPress } ) {
>
{ track.label }
onEditPress( index ) }
aria-label={ sprintf(
@@ -147,8 +146,7 @@ function SingleTrackEditor( { track, onChange, onClose, onRemove } ) {
/>
{
const changes = {};
@@ -177,8 +175,7 @@ function SingleTrackEditor( { track, onChange, onClose, onRemove } ) {
{ __( 'Close' ) }
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
-
-
`Composite` provides a single tab stop on the page and allows navigation through the focusable descendants with arrow keys. This abstract component is based on the [WAI-ARIA Composite Role](https://w3c.github.io/aria/#composite).
## Usage
```jsx
-import { Composite, useCompositeStore } from '@wordpress/components';
+import { Composite } from '@wordpress/components';
-const store = useCompositeStore();
-
+
Label
Item 1
@@ -21,11 +16,11 @@ const store = useCompositeStore();
```
-## Hooks
+## Components
-### `useCompositeStore`
+### `Composite`
-Creates a composite store.
+Renders a composite widget.
#### Props
@@ -131,20 +126,6 @@ This only affects the composite widget behavior. You still need to set `dir="rtl
- Required: no
- Default: `false`
-## Components
-
-### `Composite`
-
-Renders a composite widget.
-
-#### Props
-
-##### `store`: `CompositeStore`
-
-Object returned by the `useCompositeStore` hook.
-
-- Required: yes
-
##### `render`: `RenderProp & { ref?: React.Ref | undefined; }> | React.ReactElement>`
Allows the component to be rendered as a different HTML element or React component. The value can be a React element or a function that takes in the original component props and gives back a React element with the props merged.
diff --git a/packages/components/src/composite/context.ts b/packages/components/src/composite/context.tsx
similarity index 75%
rename from packages/components/src/composite/context.ts
rename to packages/components/src/composite/context.tsx
index 69a052c5bfba19..4a8b22a712b031 100644
--- a/packages/components/src/composite/context.ts
+++ b/packages/components/src/composite/context.tsx
@@ -8,7 +8,6 @@ import { createContext, useContext } from '@wordpress/element';
*/
import type { CompositeContextProps } from './types';
-export const CompositeContext =
- createContext< CompositeContextProps >( undefined );
+export const CompositeContext = createContext< CompositeContextProps >( {} );
export const useCompositeContext = () => useContext( CompositeContext );
diff --git a/packages/components/src/composite/group-label.tsx b/packages/components/src/composite/group-label.tsx
index 26d043cd0a1400..17070dbb86bf81 100644
--- a/packages/components/src/composite/group-label.tsx
+++ b/packages/components/src/composite/group-label.tsx
@@ -22,7 +22,7 @@ export const CompositeGroupLabel = forwardRef<
const context = useCompositeContext();
return (
diff --git a/packages/components/src/composite/group.tsx b/packages/components/src/composite/group.tsx
index a6b1b8b3f5254a..ae21ca6f11dd92 100644
--- a/packages/components/src/composite/group.tsx
+++ b/packages/components/src/composite/group.tsx
@@ -22,7 +22,7 @@ export const CompositeGroup = forwardRef<
const context = useCompositeContext();
return (
diff --git a/packages/components/src/composite/hover.tsx b/packages/components/src/composite/hover.tsx
index 76a240e0767009..ca0bd9d8f6aa12 100644
--- a/packages/components/src/composite/hover.tsx
+++ b/packages/components/src/composite/hover.tsx
@@ -22,7 +22,7 @@ export const CompositeHover = forwardRef<
const context = useCompositeContext();
return (
diff --git a/packages/components/src/composite/index.tsx b/packages/components/src/composite/index.tsx
index 629010d271e811..e9e97072261fbf 100644
--- a/packages/components/src/composite/index.tsx
+++ b/packages/components/src/composite/index.tsx
@@ -16,6 +16,7 @@ import * as Ariakit from '@ariakit/react';
/**
* WordPress dependencies
*/
+import { isRTL } from '@wordpress/i18n';
import { useMemo, forwardRef } from '@wordpress/element';
/**
@@ -38,10 +39,9 @@ import type { CompositeProps } from './types';
*
* @example
* ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
+ * import { Composite } from '@wordpress/components';
*
- * const store = useCompositeStore();
- *
+ *
* Item 1
* Item 2
*
@@ -62,21 +62,18 @@ export const Composite = Object.assign(
focusShift = false,
virtualFocus = false,
orientation = 'both',
- rtl = false,
+ rtl = isRTL(),
// Composite component props
children,
disabled = false,
- // To be removed
- store: storeProp,
-
// Rest props
...props
},
ref
) {
- const newStore = Ariakit.useCompositeStore( {
+ const store = Ariakit.useCompositeStore( {
activeId,
defaultActiveId,
setActiveId,
@@ -88,8 +85,6 @@ export const Composite = Object.assign(
rtl,
} );
- const store = storeProp || newStore;
-
const contextValue = useMemo(
() => ( {
store,
@@ -116,10 +111,9 @@ export const Composite = Object.assign(
*
* @example
* ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
+ * import { Composite } from '@wordpress/components';
*
- * const store = useCompositeStore();
- *
+ *
*
* Label
* Item 1
@@ -138,10 +132,9 @@ export const Composite = Object.assign(
*
* @example
* ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
+ * import { Composite } from '@wordpress/components';
*
- * const store = useCompositeStore();
- *
+ *
*
* Label
* Item 1
@@ -158,10 +151,9 @@ export const Composite = Object.assign(
*
* @example
* ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
+ * import { Composite } from '@wordpress/components';
*
- * const store = useCompositeStore();
- *
+ *
* Item 1
* Item 2
* Item 3
@@ -176,10 +168,9 @@ export const Composite = Object.assign(
*
* @example
* ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
+ * import { Composite } from '@wordpress/components';
*
- * const store = useCompositeStore();
- *
+ *
*
* Item 1.1
* Item 1.2
@@ -201,10 +192,9 @@ export const Composite = Object.assign(
*
* @example
* ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
+ * import { Composite } from '@wordpress/components';
*
- * const store = useCompositeStore();
- *
+ *
* }>
* Item 1
*
@@ -224,10 +214,9 @@ export const Composite = Object.assign(
*
* @example
* ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
+ * import { Composite } from '@wordpress/components';
*
- * const store = useCompositeStore();
- * }>
+ * }>
* Item 1
* Item 2
*
diff --git a/packages/components/src/composite/item.tsx b/packages/components/src/composite/item.tsx
index 8067c222c1c97f..6d75b90f0baaaa 100644
--- a/packages/components/src/composite/item.tsx
+++ b/packages/components/src/composite/item.tsx
@@ -22,7 +22,7 @@ export const CompositeItem = forwardRef<
const context = useCompositeContext();
return (
diff --git a/packages/components/src/composite/legacy/index.tsx b/packages/components/src/composite/legacy/index.tsx
index 41ecc6ceb3f66b..22ddff6572dd01 100644
--- a/packages/components/src/composite/legacy/index.tsx
+++ b/packages/components/src/composite/legacy/index.tsx
@@ -13,17 +13,22 @@
* @see https://ariakit.org/components/composite
*/
+/**
+ * External dependencies
+ */
+import * as Ariakit from '@ariakit/react';
+
/**
* WordPress dependencies
*/
import { forwardRef } from '@wordpress/element';
+import { useInstanceId } from '@wordpress/compose';
+import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
*/
import { Composite as Current } from '..';
-import { useCompositeStore } from '../store';
-import { useInstanceId } from '@wordpress/compose';
type Orientation = 'horizontal' | 'vertical';
@@ -79,7 +84,7 @@ export interface LegacyStateOptions {
type Component = React.FunctionComponent< any >;
-type CompositeStore = ReturnType< typeof useCompositeStore >;
+type CompositeStore = ReturnType< typeof Ariakit.useCompositeStore >;
type CompositeStoreState = { store: CompositeStore };
export type CompositeState = CompositeStoreState &
Required< Pick< LegacyStateOptions, 'baseId' > >;
@@ -119,12 +124,31 @@ function mapLegacyStatePropsToComponentProps(
return legacyProps;
}
+const LEGACY_TO_NEW_DISPLAY_NAME = {
+ __unstableComposite: 'Composite',
+ __unstableCompositeGroup: 'Composite.Group or Composite.Row',
+ __unstableCompositeItem: 'Composite.Item',
+ __unstableUseCompositeState: 'Composite',
+};
+
function proxyComposite< C extends Component >(
ProxiedComponent: C | React.ForwardRefExoticComponent< C >,
propMap: Record< string, string > = {}
): CompositeComponent< C > {
- const displayName = ProxiedComponent.displayName;
+ const displayName = ProxiedComponent.displayName ?? '';
+
const Component = ( legacyProps: CompositeStateProps ) => {
+ deprecated( `wp.components.${ displayName }`, {
+ since: '6.7',
+ alternative: LEGACY_TO_NEW_DISPLAY_NAME.hasOwnProperty(
+ displayName
+ )
+ ? LEGACY_TO_NEW_DISPLAY_NAME[
+ displayName as keyof typeof LEGACY_TO_NEW_DISPLAY_NAME
+ ]
+ : undefined,
+ } );
+
const { store, ...rest } =
mapLegacyStatePropsToComponentProps( legacyProps );
const props = rest as ComponentProps< C >;
@@ -149,7 +173,7 @@ function proxyComposite< C extends Component >(
// `CompositeRow`, but this has been split into two different
// components. We handle that difference by checking on the
// provided role, and returning the appropriate component.
-const unproxiedCompositeGroup = forwardRef<
+const UnproxiedCompositeGroup = forwardRef<
any,
React.ComponentPropsWithoutRef< typeof Current.Group | typeof Current.Row >
>( ( { role, ...props }, ref ) => {
@@ -157,15 +181,52 @@ const unproxiedCompositeGroup = forwardRef<
return ;
} );
-export const Composite = proxyComposite( Current, { baseId: 'id' } );
-export const CompositeGroup = proxyComposite( unproxiedCompositeGroup );
-export const CompositeItem = proxyComposite( Current.Item, {
- focusable: 'accessibleWhenDisabled',
-} );
+/**
+ * _Note: please use the `Composite` component instead._
+ *
+ * @deprecated
+ */
+export const Composite = proxyComposite(
+ Object.assign( Current, { displayName: '__unstableComposite' } ),
+ { baseId: 'id' }
+);
+/**
+ * _Note: please use the `Composite.Row` or `Composite.Group` components instead._
+ *
+ * @deprecated
+ */
+export const CompositeGroup = proxyComposite(
+ Object.assign( UnproxiedCompositeGroup, {
+ displayName: '__unstableCompositeGroup',
+ } )
+);
+/**
+ * _Note: please use the `Composite.Item` component instead._
+ *
+ * @deprecated
+ */
+export const CompositeItem = proxyComposite(
+ Object.assign( Current.Item, {
+ displayName: '__unstableCompositeItem',
+ } ),
+ {
+ focusable: 'accessibleWhenDisabled',
+ }
+);
+/**
+ * _Note: please use the `Composite` component instead._
+ *
+ * @deprecated
+ */
export function useCompositeState(
legacyStateOptions: LegacyStateOptions = {}
): CompositeState {
+ deprecated( `wp.components.__unstableUseCompositeState`, {
+ since: '6.7',
+ alternative: LEGACY_TO_NEW_DISPLAY_NAME.__unstableUseCompositeState,
+ } );
+
const {
baseId,
currentId: defaultActiveId,
@@ -180,7 +241,7 @@ export function useCompositeState(
return {
baseId: useInstanceId( Composite, 'composite', baseId ),
- store: useCompositeStore( {
+ store: Ariakit.useCompositeStore( {
defaultActiveId,
rtl,
orientation,
diff --git a/packages/components/src/composite/legacy/stories/index.story.tsx b/packages/components/src/composite/legacy/stories/index.story.tsx
index e46d656a16810e..1b8e07e9bbf560 100644
--- a/packages/components/src/composite/legacy/stories/index.story.tsx
+++ b/packages/components/src/composite/legacy/stories/index.story.tsx
@@ -15,7 +15,8 @@ import {
import { UseCompositeStatePlaceholder, transform } from './utils';
const meta: Meta< typeof UseCompositeStatePlaceholder > = {
- title: 'Components/Composite',
+ title: 'Components (Deprecated)/Composite (Unstable)',
+ id: 'components-composite-unstable',
component: UseCompositeStatePlaceholder,
subcomponents: {
Composite,
diff --git a/packages/components/src/composite/legacy/test/index.tsx b/packages/components/src/composite/legacy/test/index.tsx
index 07398e54b27ca3..c034d31442ca8d 100644
--- a/packages/components/src/composite/legacy/test/index.tsx
+++ b/packages/components/src/composite/legacy/test/index.tsx
@@ -1,7 +1,12 @@
/**
* External dependencies
*/
-import { queryByAttribute, render, screen } from '@testing-library/react';
+import {
+ queryByAttribute,
+ render,
+ screen,
+ renderHook,
+} from '@testing-library/react';
import { press, waitFor } from '@ariakit/test';
/**
@@ -156,6 +161,57 @@ function getShiftTestItems() {
};
}
+// Checking for deprecation warnings before other tests because the `deprecated`
+// utility only fires a console.warn the first time a component is rendered.
+describe( 'Shows a deprecation warning', () => {
+ it( 'useCompositeState', () => {
+ renderHook( () => useCompositeState() );
+ expect( console ).toHaveWarnedWith(
+ 'wp.components.__unstableUseCompositeState is deprecated since version 6.7. Please use Composite instead.'
+ );
+ } );
+ it( 'Composite', () => {
+ const Test = () => {
+ const props = useCompositeState();
+ return ;
+ };
+ render( );
+ expect( console ).toHaveWarnedWith(
+ 'wp.components.__unstableComposite is deprecated since version 6.7. Please use Composite instead.'
+ );
+ } );
+ it( 'CompositeItem', () => {
+ const Test = () => {
+ const props = useCompositeState();
+ return (
+
+
+
+ );
+ };
+ render( );
+ expect( console ).toHaveWarnedWith(
+ 'wp.components.__unstableCompositeItem is deprecated since version 6.7. Please use Composite.Item instead.'
+ );
+ } );
+ it( 'CompositeGroup', () => {
+ const Test = () => {
+ const props = useCompositeState();
+ return (
+
+
+
+
+
+ );
+ };
+ render( );
+ expect( console ).toHaveWarnedWith(
+ 'wp.components.__unstableCompositeGroup is deprecated since version 6.7. Please use Composite.Group or Composite.Row instead.'
+ );
+ } );
+} );
+
describe.each( [
[
'With "spread" state',
diff --git a/packages/components/src/composite/row.tsx b/packages/components/src/composite/row.tsx
index cbb9f80045de76..a082af03ad6785 100644
--- a/packages/components/src/composite/row.tsx
+++ b/packages/components/src/composite/row.tsx
@@ -22,7 +22,7 @@ export const CompositeRow = forwardRef<
const context = useCompositeContext();
return (
diff --git a/packages/components/src/composite/store.ts b/packages/components/src/composite/store.ts
deleted file mode 100644
index acf24b96cb8128..00000000000000
--- a/packages/components/src/composite/store.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * External dependencies
- */
-import * as Ariakit from '@ariakit/react';
-
-/**
- * Internal dependencies
- */
-import type { CompositeStoreProps } from './types';
-
-// Props are already documented in TypeScript types.
-// eslint-disable-next-line jsdoc/require-param
-/**
- * Creates a composite store.
- *
- * @example
- * ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
- *
- * const store = useCompositeStore();
- *
- * Item
- * Item
- * Item
- *
- * ```
- */
-export function useCompositeStore( {
- focusLoop = false,
- focusWrap = false,
- focusShift = false,
- virtualFocus = false,
- orientation = 'both',
- rtl = false,
- ...props
-}: CompositeStoreProps = {} ) {
- return Ariakit.useCompositeStore( {
- focusLoop,
- focusWrap,
- focusShift,
- virtualFocus,
- orientation,
- rtl,
- ...props,
- } );
-}
diff --git a/packages/components/src/composite/stories/index.story.tsx b/packages/components/src/composite/stories/index.story.tsx
index 80e3d85e3ce296..d6e4999407e993 100644
--- a/packages/components/src/composite/stories/index.story.tsx
+++ b/packages/components/src/composite/stories/index.story.tsx
@@ -1,26 +1,23 @@
/**
* External dependencies
*/
-import type { Meta, StoryFn } from '@storybook/react';
+import type { Meta, StoryObj } from '@storybook/react';
/**
* WordPress dependencies
*/
-import { isRTL } from '@wordpress/i18n';
+import { useContext, useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
+import { createSlotFill, Provider as SlotFillProvider } from '../../slot-fill';
import { Composite } from '..';
-import { useCompositeStore } from '../store';
-import { UseCompositeStorePlaceholder, transform } from './utils';
-const meta: Meta< typeof UseCompositeStorePlaceholder > = {
- title: 'Components/Composite (V2)',
- component: UseCompositeStorePlaceholder,
+const meta: Meta< typeof Composite > = {
+ title: 'Components/Composite',
+ component: Composite,
subcomponents: {
- // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
- Composite,
// @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
'Composite.Group': Composite.Group,
// @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
@@ -33,8 +30,12 @@ const meta: Meta< typeof UseCompositeStorePlaceholder > = {
'Composite.Hover': Composite.Hover,
// @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
'Composite.Typeahead': Composite.Typeahead,
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
+ 'Composite.Context': Composite.Context,
},
argTypes: {
+ children: { control: { type: null } },
+ render: { control: { type: null } },
setActiveId: { control: { type: null } },
focusLoop: {
control: 'select',
@@ -45,12 +46,10 @@ const meta: Meta< typeof UseCompositeStorePlaceholder > = {
options: [ true, false, 'horizontal', 'vertical', 'both' ],
},
},
- tags: [ 'status-private' ],
parameters: {
controls: { expanded: true },
docs: {
canvas: { sourceState: 'shown' },
- source: { transform },
},
},
decorators: [
@@ -91,116 +90,266 @@ const meta: Meta< typeof UseCompositeStorePlaceholder > = {
};
export default meta;
-export const Default: StoryFn< typeof UseCompositeStorePlaceholder > = (
- storeProps
-) => {
- const rtl = isRTL();
- const store = useCompositeStore( { rtl, ...storeProps } );
+export const Default: StoryObj< typeof Composite > = {
+ args: {
+ children: (
+ <>
+ Item one
+ Item two
+ Item three
+ >
+ ),
+ },
+};
- return (
-
- Item one
- Item two
- Item three
-
- );
+export const Groups: StoryObj< typeof Composite > = {
+ ...Default,
+ args: {
+ ...Default.args,
+ children: (
+ <>
+
+ Group one
+ Item 1.1
+ Item 1.2
+
+
+ Group two
+ Item 2.1
+ Item 2.1
+
+ >
+ ),
+ },
};
-export const Groups: StoryFn< typeof UseCompositeStorePlaceholder > = (
- storeProps
-) => {
- const rtl = isRTL();
- const store = useCompositeStore( { rtl, ...storeProps } );
+export const Grid: StoryObj< typeof Composite > = {
+ ...Default,
+ args: {
+ ...Default.args,
+ role: 'grid',
+ 'aria-label': 'Composite',
+ children: (
+ <>
+
+ Item A1
+ Item A2
+ Item A3
+
+
+ Item B1
+ Item B2
+ Item B3
+
+
+ Item C1
+ Item C2
+ Item C3
+
+ >
+ ),
+ },
+};
- return (
-
-
- Group one
- Item 1.1
- Item 1.2
-
-
- Group two
- Item 2.1
- Item 2.1
-
-
- );
+export const Hover: StoryObj< typeof Composite > = {
+ ...Default,
+ args: {
+ ...Default.args,
+ children: (
+ <>
+ }>
+ Hover item one
+
+ }>
+ Hover item two
+
+ }>
+ Hover item three
+
+ >
+ ),
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: 'Elements in the composite widget will receive focus on mouse move and lose focus to the composite base element on mouse leave.',
+ },
+ },
+ },
+};
+
+export const Typeahead: StoryObj< typeof Composite > = {
+ args: {
+ ...Default.args,
+ render: ,
+ children: (
+ <>
+ Apple
+ Banana
+ Peach
+ >
+ ),
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: 'When focus in on the composite widget, hitting printable character keys will move focus to the next composite item that begins with the input characters.',
+ },
+ },
+ },
};
-export const Grid: StoryFn< typeof UseCompositeStorePlaceholder > = (
- storeProps
-) => {
- const rtl = isRTL();
- const store = useCompositeStore( { rtl, ...storeProps } );
+const ExampleSlotFill = createSlotFill( 'Example' );
+
+const Slot = () => {
+ const compositeContext = useContext( Composite.Context );
+
+ // Forward the Slot's composite context to the Fill via fillProps, so that
+ // Composite components rendered inside the Fill can work as expected.
+ const fillProps = useMemo(
+ () => ( {
+ forwardedContext: [
+ [ Composite.Context.Provider, { value: compositeContext } ],
+ ],
+ } ),
+ [ compositeContext ]
+ );
return (
-
-
- Item A1
- Item A2
- Item A3
-
-
- Item B1
- Item B2
- Item B3
-
-
- Item C1
- Item C2
- Item C3
-
-
+
);
};
-export const Hover: StoryFn< typeof UseCompositeStorePlaceholder > = (
- storeProps
-) => {
- const rtl = isRTL();
- const store = useCompositeStore( { rtl, ...storeProps } );
+type ForwardedContextTuple< P = {} > = [
+ React.ComponentType< React.PropsWithChildren< P > >,
+ P,
+];
+
+const Fill = ( { children }: { children: React.ReactNode } ) => {
+ const innerMarkup = <>{ children }>;
return (
-
- }>
- Hover item one
-
- }>
- Hover item two
-
- }>
- Hover item three
-
-
+
+ { ( fillProps: { forwardedContext?: ForwardedContextTuple[] } ) => {
+ const { forwardedContext = [] } = fillProps;
+
+ // Render all context providers forwarded by the Slot via fillProps.
+ return forwardedContext.reduce(
+ ( inner: JSX.Element, [ Provider, props ] ) => (
+ { inner }
+ ),
+ innerMarkup
+ );
+ } }
+
);
};
-Hover.parameters = {
- docs: {
- description: {
- story: 'Elements in the composite widget will receive focus on mouse move and lose focus to the composite base element on mouse leave.',
- },
+
+export const WithSlotFill: StoryObj< typeof Composite > = {
+ ...Default,
+ args: {
+ ...Default.args,
+ children: (
+ <>
+ Item one (direct child)
+
+ Item four (direct child)
+ >
+ ),
},
+ decorators: [
+ ( Story ) => {
+ return (
+
+
+
+
+
+ Item two (from slot fill)
+
+
+ Item three (from slot fill)
+
+
+
+ );
+ },
+ ],
+ parameters: {
+ docs: {
+ description: {
+ story: 'When rendering Composite components across a SlotFill, the Composite.Context should be manually forwarded from the Slot to the Fill component.',
+ },
+ source: {
+ transform: ( code: string ) => {
+ return `const ExampleSlotFill = createSlotFill( 'Example' );
+
+const Slot = () => {
+ const compositeContext = useContext( Composite.Context );
+
+ // Forward the Slot's composite context to the Fill via fillProps, so that
+ // Composite components rendered inside the Fill can work as expected.
+ const fillProps = useMemo(
+ () => ( {
+ forwardedContext: [
+ [ Composite.Context.Provider, { value: compositeContext } ],
+ ],
+ } ),
+ [ compositeContext ]
+ );
+
+ return (
+
+ );
};
-export const Typeahead: StoryFn< typeof UseCompositeStorePlaceholder > = (
- storeProps
-) => {
- const rtl = isRTL();
- const store = useCompositeStore( { rtl, ...storeProps } );
+const Fill = ( { children } ) => {
+ const innerMarkup = <>{ children }>;
- return (
- }>
- Apple
- Banana
- Peach
-
- );
+ return (
+
+ { ( fillProps ) => {
+ const { forwardedContext = [] } = fillProps;
+
+ // Render all context providers forwarded by the Slot via fillProps.
+ return forwardedContext.reduce(
+ ( inner, [ Provider, props ] ) => (
+ { inner }
+ ),
+ innerMarkup
+ );
+ } }
+
+ );
};
-Typeahead.parameters = {
- docs: {
- description: {
- story: 'When focus in on the composite widget, hitting printable character keys will move focus to the next composite item that begins with the input characters.',
+
+// In a separate component:
+
+
+ ${
+ // Add one level of indentation to match the surrounding code.
+ code.replaceAll( '\n', '\n ' )
+ }
+
+
+
+ Item two (from slot fill)
+
+
+ Item three (from slot fill)
+
+
+ `;
+ },
+ },
},
},
};
diff --git a/packages/components/src/composite/stories/utils.tsx b/packages/components/src/composite/stories/utils.tsx
deleted file mode 100644
index f2f197877ff76d..00000000000000
--- a/packages/components/src/composite/stories/utils.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * External dependencies
- */
-import type { StoryContext } from '@storybook/react';
-
-/**
- * Internal dependencies
- */
-import type { CompositeStoreProps } from '../types';
-
-/**
- * Renders a widget based on the WAI-ARIA [`composite`](https://w3c.github.io/aria/#composite)
- * role, which provides a single tab stop on the page and arrow key navigation
- * through the focusable descendants.
- *
- * ```jsx
- * import { Composite, useCompositeStore } from '@wordpress/components';
- *
- * const store = useCompositeStore();
- *
- * Item 1
- * Item 2
- *
- * ```
- */
-export function UseCompositeStorePlaceholder( props: CompositeStoreProps ) {
- return (
-
- { Object.entries( props ).map( ( [ name, value ] ) => (
- <>
- { name }
- { JSON.stringify( value ) }
- >
- ) ) }
-
- );
-}
-UseCompositeStorePlaceholder.displayName = 'useCompositeStore';
-
-// The output generated by Storybook for these components is
-// messy, so we apply this transform to make it more useful
-// for anyone reading the docs.
-export function transform( code: string, context: StoryContext ) {
- const storeConfig = ` ${ JSON.stringify( context.args, null, 2 ) } `;
- const formattedStoreConfig = storeConfig.replace( ' {} ', '' );
- return [
- // Include a setup line, showing how to make use of
- // `useCompositeStore` to convert store options into
- // a composite store prop.
- `const store = useCompositeStore(${ formattedStoreConfig });`,
- '',
- 'return (',
- ' ' +
- code
- // The generated output includes a full dump of everything
- // in the store; the reader probably isn't interested in
- // what that looks like, so instead we drop all of that
- // in favor of the store generated above.
- .replaceAll( /store=\{\{[\s\S]*?\}\}/g, 'store={ store }' )
- // Now we tidy the output by removing any unnecessary
- // whitespace...
- .replaceAll( //g, ( match ) =>
- match.replaceAll( /\s+\s/g, ' ' )
- )
- // ...including around children...
- .replaceAll(
- />\s*(\w[\w ]*?)\s*<\//g,
- ( _, value ) => `>${ value }`
- )
- // ...and inside JSX definitions.
- .replaceAll( '} >', '}>' )
- // Finally we indent everything to make it more readable.
- .replaceAll( /\n/g, '\n ' ),
- ');',
- ].join( '\n' );
-}
diff --git a/packages/components/src/composite/typeahead.tsx b/packages/components/src/composite/typeahead.tsx
index d5bb7e6e2e4cc5..771d58bcb6c25c 100644
--- a/packages/components/src/composite/typeahead.tsx
+++ b/packages/components/src/composite/typeahead.tsx
@@ -22,7 +22,7 @@ export const CompositeTypeahead = forwardRef<
const context = useCompositeContext();
return (
diff --git a/packages/components/src/composite/types.ts b/packages/components/src/composite/types.ts
index fcde842e1cba77..ed95c7f0253b33 100644
--- a/packages/components/src/composite/types.ts
+++ b/packages/components/src/composite/types.ts
@@ -3,16 +3,18 @@
*/
import type * as Ariakit from '@ariakit/react';
-export type CompositeContextProps =
- | {
- /**
- * Object returned by the `useCompositeStore` hook.
- */
- store: Ariakit.CompositeStore;
- }
- | undefined;
+export type CompositeContextProps = {
+ /**
+ * The component store, used for advanced usage of the component.
+ *
+ * _Note: Using the store directly is not recommended. Instead, use the props
+ * exposed by the `Composite` component._
+ *
+ */
+ store?: unknown;
+};
-export type CompositeStoreProps = {
+type CompositeStoreProps = {
/**
* The current active item `id`. The active item is the element within the
* composite widget that has either DOM or virtual focus (in case
@@ -117,22 +119,18 @@ export type CompositeStoreProps = {
*/
orientation?: Ariakit.CompositeStoreProps[ 'orientation' ];
/**
- * Determines how the `store`'s `next` and `previous` functions will behave.
+ * Controls how the previous and next items are determined.
* If `rtl` is set to `true`, they will be inverted.
*
* This only affects the composite widget behavior. You still need to set
* `dir="rtl"` on HTML/CSS.
*
- * @default false
+ * @default `isRtl()`
*/
rtl?: Ariakit.CompositeStoreProps[ 'rtl' ];
};
export type CompositeProps = CompositeStoreProps & {
- /**
- * Object returned by the `useCompositeStore` hook.
- */
- store?: Ariakit.CompositeStore;
/**
* Allows the component to be rendered as a different HTML element or React
* component. The value can be a React element or a function that takes in the
diff --git a/packages/components/src/date-time/date/index.tsx b/packages/components/src/date-time/date/index.tsx
index 75bfbb76ba2116..33fc736564d5e6 100644
--- a/packages/components/src/date-time/date/index.tsx
+++ b/packages/components/src/date-time/date/index.tsx
@@ -1,7 +1,6 @@
/**
* External dependencies
*/
-import { useLilius } from 'use-lilius';
import {
format,
isSameDay,
@@ -29,6 +28,7 @@ import { useState, useRef, useEffect } from '@wordpress/element';
/**
* Internal dependencies
*/
+import { useLilius } from './use-lilius';
import type { DatePickerProps } from '../types';
import {
Wrapper,
diff --git a/packages/components/src/date-time/date/styles.ts b/packages/components/src/date-time/date/styles.ts
index 8f208b53fac22a..84405854827ec9 100644
--- a/packages/components/src/date-time/date/styles.ts
+++ b/packages/components/src/date-time/date/styles.ts
@@ -7,13 +7,13 @@ import styled from '@emotion/styled';
* Internal dependencies
*/
import Button from '../../button';
-import { COLORS, CONFIG } from '../../utils';
+import { boxSizingReset, COLORS, CONFIG } from '../../utils';
import { HStack } from '../../h-stack';
import { Heading } from '../../heading';
import { space } from '../../utils/space';
export const Wrapper = styled.div`
- box-sizing: border-box;
+ ${ boxSizingReset }
`;
export const Navigator = styled( HStack )`
@@ -38,7 +38,7 @@ export const Calendar = styled.div`
`;
export const DayOfWeek = styled.div`
- color: ${ COLORS.gray[ 700 ] };
+ color: ${ COLORS.theme.gray[ 700 ] };
font-size: ${ CONFIG.fontSize };
line-height: ${ CONFIG.fontLineHeightBase };
@@ -90,15 +90,34 @@ export const DayButton = styled( Button, {
${ ( props ) =>
props.isSelected &&
`
- background: ${ COLORS.theme.accent };
- color: ${ COLORS.white };
+ background: ${ COLORS.theme.accent };
+
+ &,
+ &:hover:not(:disabled, [aria-disabled=true]) {
+ color: ${ COLORS.theme.accentInverted };
+ }
+
+ &:focus:not(:disabled),
+ &:focus:not(:disabled) {
+ border: ${ CONFIG.borderWidthFocus } solid currentColor;
+ }
+
+ /* Highlight the selected day for high-contrast mode */
+ &::after {
+ content: '';
+ position: absolute;
+ pointer-events: none;
+ inset: 0;
+ border-radius: inherit;
+ border: 1px solid transparent;
+ }
` }
${ ( props ) =>
! props.isSelected &&
props.isToday &&
`
- background: ${ COLORS.gray[ 200 ] };
+ background: ${ COLORS.theme.gray[ 200 ] };
` }
}
@@ -106,14 +125,16 @@ export const DayButton = styled( Button, {
props.hasEvents &&
`
::before {
- background: ${ props.isSelected ? COLORS.white : COLORS.theme.accent };
- bottom: 2px;
+ border: 2px solid ${
+ props.isSelected
+ ? COLORS.theme.accentInverted
+ : COLORS.theme.accent
+ };
+ border-radius: ${ CONFIG.radiusRound };
content: " ";
- height: 4px;
left: 50%;
- margin-left: -2px;
position: absolute;
- width: 4px;
+ transform: translate(-50%, 9px);
}
` }
`;
diff --git a/packages/components/src/date-time/date/test/use-lilius.ts b/packages/components/src/date-time/date/test/use-lilius.ts
new file mode 100644
index 00000000000000..31b81fa11bb74b
--- /dev/null
+++ b/packages/components/src/date-time/date/test/use-lilius.ts
@@ -0,0 +1,417 @@
+/**
+ * External dependencies
+ */
+import { act, renderHook } from '@testing-library/react';
+import { addYears, getYear, set, startOfToday, subYears } from 'date-fns';
+
+/**
+ * Internal dependencies
+ */
+import { Month, useLilius } from '../use-lilius';
+
+const getDate = ( {
+ year = 1999,
+ month = Month.NOVEMBER,
+ date = 24,
+ hours = 0,
+ minutes = 0,
+ seconds = 0,
+ milliseconds = 0,
+}: {
+ year?: number;
+ month?: number;
+ date?: number;
+ hours?: number;
+ minutes?: number;
+ seconds?: number;
+ milliseconds?: number;
+} = {} ) => {
+ return set( new Date(), {
+ year,
+ month,
+ date,
+ hours,
+ minutes,
+ seconds,
+ milliseconds,
+ } );
+};
+
+describe( 'helpers', () => {
+ describe( 'clearTime', () => {
+ it( 'returns a copy of the given date with the time set to 00:00:00:00', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate( { hours: 7, minutes: 30 } );
+
+ expect( result.current.clearTime( date ) ).toStrictEqual(
+ set( date, {
+ hours: 0,
+ minutes: 0,
+ seconds: 0,
+ milliseconds: 0,
+ } )
+ );
+ } );
+ } );
+
+ describe( 'inRange', () => {
+ it( 'returns whether or not a date is between 2 other dates (inclusive)', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+ const min = subYears( date, 1 );
+ const max = addYears( date, 1 );
+
+ expect( result.current.inRange( date, min, max ) ).toBe( true );
+ expect(
+ result.current.inRange( addYears( date, 10 ), min, max )
+ ).toBe( false );
+ } );
+ } );
+} );
+
+describe( 'viewing', () => {
+ describe( 'viewing', () => {
+ it( 'returns the date represented in the calendar matrix', () => {
+ const date = getDate();
+
+ const { result } = renderHook( () =>
+ useLilius( { viewing: date } )
+ );
+
+ expect( result.current.viewing ).toStrictEqual( date );
+ } );
+ } );
+
+ describe( 'setViewing', () => {
+ it( 'sets the date represented in the calendar matrix', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+
+ act( () => result.current.setViewing( date ) );
+ expect( result.current.viewing ).toStrictEqual( date );
+ } );
+ } );
+
+ describe( 'viewToday', () => {
+ it( 'sets the viewing date to today', () => {
+ const { result } = renderHook( () =>
+ useLilius( { viewing: getDate( { year: 1999 } ) } )
+ );
+
+ act( () => result.current.viewToday() );
+ expect( result.current.viewing ).toStrictEqual( startOfToday() );
+ } );
+ } );
+
+ describe( 'viewMonth', () => {
+ it( 'sets the viewing date to the given month', () => {
+ const date = getDate( { month: Month.JANUARY } );
+
+ const { result } = renderHook( () =>
+ useLilius( { viewing: date } )
+ );
+
+ act( () => result.current.viewMonth( Month.FEBRUARY ) );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, { month: Month.FEBRUARY } )
+ );
+ } );
+ } );
+
+ describe( 'viewPreviousMonth', () => {
+ it( 'sets the viewing date to the month before the current', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate( { month: Month.OCTOBER } );
+
+ act( () => result.current.setViewing( date ) );
+ act( () => result.current.viewPreviousMonth() );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, { month: Month.SEPTEMBER } )
+ );
+ } );
+
+ it( 'wraps to december of the previous year if the current month is january', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate( { month: Month.JANUARY } );
+
+ act( () => result.current.setViewing( date ) );
+ act( () => result.current.viewPreviousMonth() );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, {
+ month: Month.DECEMBER,
+ year: getYear( date ) - 1,
+ } )
+ );
+ } );
+ } );
+
+ describe( 'viewNextMonth', () => {
+ it( 'sets the viewing date to the month after the current', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate( { month: Month.OCTOBER } );
+
+ act( () => result.current.setViewing( date ) );
+ act( () => result.current.viewNextMonth() );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, { month: Month.NOVEMBER } )
+ );
+ } );
+
+ it( 'wraps to january of the next year if the current month is december', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate( { month: Month.DECEMBER } );
+
+ act( () => result.current.setViewing( date ) );
+ act( () => result.current.viewNextMonth() );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, { month: Month.JANUARY, year: getYear( date ) + 1 } )
+ );
+ } );
+ } );
+
+ describe( 'viewYear', () => {
+ it( 'sets the viewing date to the given year', () => {
+ const date = getDate( { year: 1999 } );
+
+ const { result } = renderHook( () =>
+ useLilius( { viewing: date } )
+ );
+
+ act( () => result.current.viewYear( 1997 ) );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, { year: 1997 } )
+ );
+ } );
+ } );
+
+ describe( 'viewPreviousYear', () => {
+ it( 'sets the viewing date to the year before the current', () => {
+ const date = getDate( { year: 1999 } );
+
+ const { result } = renderHook( () =>
+ useLilius( { viewing: date } )
+ );
+
+ act( () => result.current.viewPreviousYear() );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, { year: 1998 } )
+ );
+ } );
+ } );
+
+ describe( 'viewNextYear', () => {
+ it( 'sets the viewing date to the year after the current', () => {
+ const date = getDate( { year: 1999 } );
+
+ const { result } = renderHook( () =>
+ useLilius( { viewing: date } )
+ );
+
+ act( () => result.current.viewNextYear() );
+ expect( result.current.viewing ).toStrictEqual(
+ set( date, { year: 2000 } )
+ );
+ } );
+ } );
+} );
+
+describe( 'selected', () => {
+ describe( 'selected', () => {
+ it( 'returns the dates currently selected', () => {
+ const date = getDate();
+
+ const { result } = renderHook( () =>
+ useLilius( {
+ selected: [ date ],
+ } )
+ );
+
+ expect( result.current.selected ).toStrictEqual( [ date ] );
+ } );
+ } );
+
+ describe( 'clearSelected', () => {
+ it( 'resets the selected dates to []', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+
+ act( () =>
+ result.current.selectRange(
+ set( date, { date: 1 } ),
+ set( date, { date: 5 } )
+ )
+ );
+ expect( result.current.selected.length ).toBe( 5 );
+
+ act( () => result.current.clearSelected() );
+ expect( result.current.selected.length ).toBe( 0 );
+ } );
+ } );
+
+ describe( 'isSelected', () => {
+ it( 'returns whether or not a date has been selected', () => {
+ const date = getDate();
+
+ const { result } = renderHook( () =>
+ useLilius( {
+ selected: [ date ],
+ } )
+ );
+
+ expect( result.current.isSelected( date ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'select', () => {
+ it( 'selects a date', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+
+ act( () => result.current.select( date ) );
+ expect( result.current.isSelected( date ) ).toBe( true );
+ } );
+
+ it( 'selects multiple dates', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const dateOne = getDate( { date: 1 } );
+ const dateTwo = getDate( { date: 2 } );
+
+ act( () => result.current.select( [ dateOne, dateTwo ] ) );
+ expect( result.current.isSelected( dateOne ) ).toBe( true );
+ expect( result.current.isSelected( dateTwo ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'deselect', () => {
+ it( 'deselects a date', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+
+ act( () => result.current.select( date ) );
+ act( () => result.current.deselect( date ) );
+ expect( result.current.isSelected( date ) ).toBe( false );
+ } );
+
+ it( 'deselects multiple dates', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const dateOne = getDate( { date: 1 } );
+ const dateTwo = getDate( { date: 2 } );
+
+ act( () => result.current.select( [ dateOne, dateTwo ] ) );
+ act( () => result.current.deselect( [ dateOne, dateTwo ] ) );
+ expect( result.current.isSelected( dateOne ) ).toBe( false );
+ expect( result.current.isSelected( dateTwo ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'toggle', () => {
+ it( 'toggles the selection of a date', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+
+ act( () => result.current.toggle( date ) );
+ expect( result.current.isSelected( date ) ).toBe( true );
+
+ act( () => result.current.toggle( date ) );
+ expect( result.current.isSelected( date ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'selectRange', () => {
+ it( 'selects a range of dates (inclusive)', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+
+ const first = set( date, { date: 1 } );
+ const second = set( date, { date: 2 } );
+ const third = set( date, { date: 3 } );
+
+ act( () => result.current.selectRange( first, third ) );
+
+ expect( result.current.selected.length ).toBe( 3 );
+ expect( result.current.isSelected( first ) ).toBe( true );
+ expect( result.current.isSelected( second ) ).toBe( true );
+ expect( result.current.isSelected( third ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'deselectRange', () => {
+ it( 'deselects a range of dates (inclusive)', () => {
+ const { result } = renderHook( () => useLilius() );
+
+ const date = getDate();
+
+ act( () =>
+ result.current.selectRange(
+ set( date, { date: 1 } ),
+ set( date, { date: 3 } )
+ )
+ );
+ act( () =>
+ result.current.deselectRange(
+ set( date, { date: 1 } ),
+ set( date, { date: 3 } )
+ )
+ );
+
+ expect( result.current.selected.length ).toBe( 0 );
+ expect(
+ result.current.isSelected( set( date, { date: 1 } ) )
+ ).toBe( false );
+ expect(
+ result.current.isSelected( set( date, { date: 2 } ) )
+ ).toBe( false );
+ expect(
+ result.current.isSelected( set( date, { date: 3 } ) )
+ ).toBe( false );
+ } );
+ } );
+} );
+
+describe( 'calendar', () => {
+ it( 'returns a matrix of days based on the current viewing date', () => {
+ const { result } = renderHook( () =>
+ useLilius( { viewing: new Date( 1582, Month.OCTOBER, 1 ) } )
+ );
+
+ expect( result.current.calendar![ 0 ][ 0 ][ 0 ] ).toStrictEqual(
+ new Date( 1582, Month.SEPTEMBER, 26 )
+ );
+ expect( result.current.calendar![ 0 ][ 0 ][ 5 ] ).toStrictEqual(
+ new Date( 1582, Month.OCTOBER, 1 )
+ );
+ expect( result.current.calendar![ 0 ][ 5 ][ 6 ] ).toStrictEqual(
+ new Date( 1582, Month.NOVEMBER, 6 )
+ );
+ } );
+
+ it( 'supports returning multiple months', () => {
+ const { result } = renderHook( () =>
+ useLilius( {
+ viewing: new Date( 1582, Month.OCTOBER, 1 ),
+ numberOfMonths: 2,
+ } )
+ );
+
+ expect( result.current.calendar![ 0 ][ 0 ][ 0 ] ).toStrictEqual(
+ new Date( 1582, Month.SEPTEMBER, 26 )
+ );
+ expect( result.current.calendar![ 1 ][ 0 ][ 0 ] ).toStrictEqual(
+ new Date( 1582, Month.OCTOBER, 31 )
+ );
+ } );
+} );
diff --git a/packages/components/src/date-time/date/use-lilius/index.ts b/packages/components/src/date-time/date/use-lilius/index.ts
new file mode 100644
index 00000000000000..2da204b3c76419
--- /dev/null
+++ b/packages/components/src/date-time/date/use-lilius/index.ts
@@ -0,0 +1,394 @@
+/**
+ * This source is a local copy of the use-lilius library, since the original
+ * library is not actively maintained.
+ * @see https://github.com/WordPress/gutenberg/discussions/64968
+ *
+ * use-lilius@2.0.5
+ * https://github.com/Avarios/use-lilius
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021-Present Danny Tatom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/**
+ * External dependencies
+ */
+import {
+ addMonths,
+ addYears,
+ eachDayOfInterval,
+ eachMonthOfInterval,
+ eachWeekOfInterval,
+ endOfMonth,
+ endOfWeek,
+ isAfter,
+ isBefore,
+ isEqual,
+ set,
+ setMonth,
+ setYear,
+ startOfMonth,
+ startOfToday,
+ startOfWeek,
+ subMonths,
+ subYears,
+} from 'date-fns';
+
+/**
+ * WordPress dependencies
+ */
+import { useCallback, useMemo, useState } from '@wordpress/element';
+
+export enum Month {
+ JANUARY,
+ FEBRUARY,
+ MARCH,
+ APRIL,
+ MAY,
+ JUNE,
+ JULY,
+ AUGUST,
+ SEPTEMBER,
+ OCTOBER,
+ NOVEMBER,
+ DECEMBER,
+}
+
+export enum Day {
+ SUNDAY,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY,
+ SATURDAY,
+}
+
+export interface Options {
+ /**
+ * What day a week starts on within the calendar matrix.
+ *
+ * @default Day.SUNDAY
+ */
+ weekStartsOn?: Day;
+
+ /**
+ * The initial viewing date.
+ *
+ * @default new Date()
+ */
+ viewing?: Date;
+
+ /**
+ * The initial date(s) selection.
+ *
+ * @default []
+ */
+ selected?: Date[];
+
+ /**
+ * The number of months in the calendar.
+ *
+ * @default 1
+ */
+ numberOfMonths?: number;
+}
+
+export interface Returns {
+ /**
+ * Returns a copy of the given date with the time set to 00:00:00:00.
+ */
+ clearTime: ( date: Date ) => Date;
+
+ /**
+ * Returns whether or not a date is between 2 other dates (inclusive).
+ */
+ inRange: ( date: Date, min: Date, max: Date ) => boolean;
+
+ /**
+ * The date represented in the calendar matrix. Note that
+ * the month and year are the only parts used.
+ */
+ viewing: Date;
+
+ /**
+ * Set the date represented in the calendar matrix. Note that
+ * the month and year are the only parts used.
+ */
+ setViewing: React.Dispatch< React.SetStateAction< Date > >;
+
+ /**
+ * Set the viewing date to today.
+ */
+ viewToday: () => void;
+
+ /**
+ * Set the viewing date to the given month.
+ */
+ viewMonth: ( month: Month ) => void;
+
+ /**
+ * Set the viewing date to the month before the current.
+ */
+ viewPreviousMonth: () => void;
+
+ /**
+ * Set the viewing date to the month after the current.
+ */
+ viewNextMonth: () => void;
+
+ /**
+ * Set the viewing date to the given year.
+ */
+ viewYear: ( year: number ) => void;
+
+ /**
+ * Set the viewing date to the year before the current.
+ */
+ viewPreviousYear: () => void;
+
+ /**
+ * Set the viewing date to the year after the current.
+ */
+ viewNextYear: () => void;
+
+ /**
+ * The dates currently selected.
+ */
+ selected: Date[];
+
+ /**
+ * Override the currently selected dates.
+ */
+ setSelected: React.Dispatch< React.SetStateAction< Date[] > >;
+
+ /**
+ * Reset the selected dates to [].
+ */
+ clearSelected: () => void;
+
+ /**
+ * Determine whether or not a date has been selected.
+ */
+ isSelected: ( date: Date ) => boolean;
+
+ /**
+ * Select one or more dates.
+ */
+ select: ( date: Date | Date[], replaceExisting?: boolean ) => void;
+
+ /**
+ * Deselect one or more dates.
+ */
+ deselect: ( date: Date | Date[] ) => void;
+
+ /**
+ * Toggle the selection of a date.
+ */
+ toggle: ( date: Date, replaceExisting?: boolean ) => void;
+
+ /**
+ * Select a range of dates (inclusive).
+ */
+ selectRange: ( start: Date, end: Date, replaceExisting?: boolean ) => void;
+
+ /**
+ * Deselect a range of dates (inclusive).
+ */
+ deselectRange: ( start: Date, end: Date ) => void;
+
+ /**
+ * A matrix of days based on the current viewing date.
+ */
+ calendar: Date[][][];
+}
+
+const inRange = ( date: Date, min: Date, max: Date ) =>
+ ( isEqual( date, min ) || isAfter( date, min ) ) &&
+ ( isEqual( date, max ) || isBefore( date, max ) );
+
+const clearTime = ( date: Date ) =>
+ set( date, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 } );
+
+export const useLilius = ( {
+ weekStartsOn = Day.SUNDAY,
+ viewing: initialViewing = new Date(),
+ selected: initialSelected = [],
+ numberOfMonths = 1,
+}: Options = {} ): Returns => {
+ const [ viewing, setViewing ] = useState< Date >( initialViewing );
+
+ const viewToday = useCallback(
+ () => setViewing( startOfToday() ),
+ [ setViewing ]
+ );
+
+ const viewMonth = useCallback(
+ ( month: Month ) => setViewing( ( v ) => setMonth( v, month ) ),
+ []
+ );
+
+ const viewPreviousMonth = useCallback(
+ () => setViewing( ( v ) => subMonths( v, 1 ) ),
+ []
+ );
+
+ const viewNextMonth = useCallback(
+ () => setViewing( ( v ) => addMonths( v, 1 ) ),
+ []
+ );
+
+ const viewYear = useCallback(
+ ( year: number ) => setViewing( ( v ) => setYear( v, year ) ),
+ []
+ );
+
+ const viewPreviousYear = useCallback(
+ () => setViewing( ( v ) => subYears( v, 1 ) ),
+ []
+ );
+
+ const viewNextYear = useCallback(
+ () => setViewing( ( v ) => addYears( v, 1 ) ),
+ []
+ );
+
+ const [ selected, setSelected ] = useState< Date[] >(
+ initialSelected.map( clearTime )
+ );
+
+ const clearSelected = () => setSelected( [] );
+
+ const isSelected = useCallback(
+ ( date: Date ) =>
+ selected.findIndex( ( s ) => isEqual( s, date ) ) > -1,
+ [ selected ]
+ );
+
+ const select = useCallback(
+ ( date: Date | Date[], replaceExisting?: boolean ) => {
+ if ( replaceExisting ) {
+ setSelected( Array.isArray( date ) ? date : [ date ] );
+ } else {
+ setSelected( ( selectedItems ) =>
+ selectedItems.concat(
+ Array.isArray( date ) ? date : [ date ]
+ )
+ );
+ }
+ },
+ []
+ );
+
+ const deselect = useCallback(
+ ( date: Date | Date[] ) =>
+ setSelected( ( selectedItems ) =>
+ Array.isArray( date )
+ ? selectedItems.filter(
+ ( s ) =>
+ ! date
+ .map( ( d ) => d.getTime() )
+ .includes( s.getTime() )
+ )
+ : selectedItems.filter( ( s ) => ! isEqual( s, date ) )
+ ),
+ []
+ );
+
+ const toggle = useCallback(
+ ( date: Date, replaceExisting?: boolean ) =>
+ isSelected( date )
+ ? deselect( date )
+ : select( date, replaceExisting ),
+ [ deselect, isSelected, select ]
+ );
+
+ const selectRange = useCallback(
+ ( start: Date, end: Date, replaceExisting?: boolean ) => {
+ if ( replaceExisting ) {
+ setSelected( eachDayOfInterval( { start, end } ) );
+ } else {
+ setSelected( ( selectedItems ) =>
+ selectedItems.concat( eachDayOfInterval( { start, end } ) )
+ );
+ }
+ },
+ []
+ );
+
+ const deselectRange = useCallback( ( start: Date, end: Date ) => {
+ setSelected( ( selectedItems ) =>
+ selectedItems.filter(
+ ( s ) =>
+ ! eachDayOfInterval( { start, end } )
+ .map( ( d ) => d.getTime() )
+ .includes( s.getTime() )
+ )
+ );
+ }, [] );
+
+ const calendar = useMemo< Date[][][] >(
+ () =>
+ eachMonthOfInterval( {
+ start: startOfMonth( viewing ),
+ end: endOfMonth( addMonths( viewing, numberOfMonths - 1 ) ),
+ } ).map( ( month ) =>
+ eachWeekOfInterval(
+ {
+ start: startOfMonth( month ),
+ end: endOfMonth( month ),
+ },
+ { weekStartsOn }
+ ).map( ( week ) =>
+ eachDayOfInterval( {
+ start: startOfWeek( week, { weekStartsOn } ),
+ end: endOfWeek( week, { weekStartsOn } ),
+ } )
+ )
+ ),
+ [ viewing, weekStartsOn, numberOfMonths ]
+ );
+
+ return {
+ clearTime,
+ inRange,
+ viewing,
+ setViewing,
+ viewToday,
+ viewMonth,
+ viewPreviousMonth,
+ viewNextMonth,
+ viewYear,
+ viewPreviousYear,
+ viewNextYear,
+ selected,
+ setSelected,
+ clearSelected,
+ isSelected,
+ select,
+ deselect,
+ toggle,
+ selectRange,
+ deselectRange,
+ calendar,
+ };
+};
diff --git a/packages/components/src/dimension-control/README.md b/packages/components/src/dimension-control/README.md
index 3cd0191a046068..78c1a60275c13a 100644
--- a/packages/components/src/dimension-control/README.md
+++ b/packages/components/src/dimension-control/README.md
@@ -1,5 +1,9 @@
# DimensionControl
+
+This component is deprecated.
+
+
This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
diff --git a/packages/components/src/dimension-control/index.tsx b/packages/components/src/dimension-control/index.tsx
index 52662f31c3f24c..25880f9b4fdb38 100644
--- a/packages/components/src/dimension-control/index.tsx
+++ b/packages/components/src/dimension-control/index.tsx
@@ -17,6 +17,7 @@ import sizesTable, { findSizeBySlug } from './sizes';
import type { DimensionControlProps, Size } from './types';
import type { SelectControlSingleSelectionProps } from '../select-control/types';
import { ContextSystemProvider } from '../context';
+import deprecated from '@wordpress/deprecated';
const CONTEXT_VALUE = {
BaseControl: {
@@ -29,7 +30,7 @@ const CONTEXT_VALUE = {
/**
* `DimensionControl` is a component designed to provide a UI to control spacing and/or dimensions.
*
- * This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
+ * @deprecated
*
* ```jsx
* import { __experimentalDimensionControl as DimensionControl } from '@wordpress/components';
@@ -62,6 +63,11 @@ export function DimensionControl( props: DimensionControlProps ) {
className = '',
} = props;
+ deprecated( 'wp.components.DimensionControl', {
+ since: '6.7',
+ version: '7.0',
+ } );
+
const onChangeSpacingSize: SelectControlSingleSelectionProps[ 'onChange' ] =
( val ) => {
const theSize = findSizeBySlug( sizes, val );
diff --git a/packages/components/src/dimension-control/stories/index.story.tsx b/packages/components/src/dimension-control/stories/index.story.tsx
index 3a6da44f461164..15a63fcf6ccf6c 100644
--- a/packages/components/src/dimension-control/stories/index.story.tsx
+++ b/packages/components/src/dimension-control/stories/index.story.tsx
@@ -13,9 +13,15 @@ import sizes from '../sizes';
*/
import { desktop, tablet, mobile } from '@wordpress/icons';
+/**
+ * `DimensionControl` is a component designed to provide a UI to control spacing and/or dimensions.
+ *
+ * This component is deprecated.
+ */
const meta: Meta< typeof DimensionControl > = {
component: DimensionControl,
- title: 'Components (Experimental)/DimensionControl',
+ title: 'Components (Deprecated)/DimensionControl',
+ id: 'components-dimensioncontrol',
argTypes: {
onChange: { action: 'onChange' },
value: { control: { type: null } },
@@ -42,7 +48,6 @@ const Template: StoryFn< typeof DimensionControl > = ( args ) => (
);
export const Default = Template.bind( {} );
-
Default.args = {
__nextHasNoMarginBottom: true,
label: 'Please select a size',
diff --git a/packages/components/src/dimension-control/test/index.test.js b/packages/components/src/dimension-control/test/index.test.js
index 1b34d2983ad0f1..14f1c509f70cf9 100644
--- a/packages/components/src/dimension-control/test/index.test.js
+++ b/packages/components/src/dimension-control/test/index.test.js
@@ -31,6 +31,7 @@ describe( 'DimensionControl', () => {
const { container } = render(
);
+ expect( console ).toHaveWarned();
expect( container ).toMatchSnapshot();
} );
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index 3ddfbd05cd6581..32195ebc444ce6 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -66,6 +66,7 @@ export {
CompositeItem as __unstableCompositeItem,
useCompositeState as __unstableUseCompositeState,
} from './composite/legacy';
+export { Composite } from './composite';
export { ConfirmDialog as __experimentalConfirmDialog } from './confirm-dialog';
export { default as CustomSelectControl } from './custom-select-control';
export { default as Dashicon } from './dashicon';
diff --git a/packages/components/src/modal/index.tsx b/packages/components/src/modal/index.tsx
index e1f103b98c2735..7d988a0e7240b3 100644
--- a/packages/components/src/modal/index.tsx
+++ b/packages/components/src/modal/index.tsx
@@ -37,6 +37,7 @@ import Button from '../button';
import StyleProvider from '../style-provider';
import type { ModalProps } from './types';
import { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events';
+import { Spacer } from '../spacer';
// Used to track and dismiss the prior modal when another opens unless nested.
type Dismissers = Set<
@@ -323,13 +324,21 @@ function UnforwardedModal(
{ headerActions }
{ isDismissible && (
-
+ <>
+
+
+ >
) }
) }
diff --git a/packages/components/src/modal/stories/index.story.tsx b/packages/components/src/modal/stories/index.story.tsx
index 60a53947116fac..f180de0dc06a46 100644
--- a/packages/components/src/modal/stories/index.story.tsx
+++ b/packages/components/src/modal/stories/index.story.tsx
@@ -7,7 +7,7 @@ import type { StoryFn, Meta } from '@storybook/react';
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
-import { starEmpty, starFilled } from '@wordpress/icons';
+import { fullscreen } from '@wordpress/icons';
/**
* Internal dependencies
@@ -103,22 +103,16 @@ WithsizeSmall.args = {
};
WithsizeSmall.storyName = 'With size: small';
-const LikeButton = () => {
- const [ isLiked, setIsLiked ] = useState( false );
- return (
- setIsLiked( ! isLiked ) }
- />
- );
-};
-
+/**
+ * The `headerActions` prop can be used to add auxiliary actions to the header, for example a fullscreen mode toggle.
+ */
export const WithHeaderActions: StoryFn< typeof Modal > = Template.bind( {} );
WithHeaderActions.args = {
...Default.args,
- headerActions: ,
- isDismissible: false,
+ headerActions: (
+
+ ),
+ children:
,
};
WithHeaderActions.parameters = {
...Default.parameters,
diff --git a/packages/components/src/modal/style.scss b/packages/components/src/modal/style.scss
index 47d5df116d8865..8c7c8416f0190b 100644
--- a/packages/components/src/modal/style.scss
+++ b/packages/components/src/modal/style.scss
@@ -116,11 +116,6 @@
margin: 0;
}
- .components-button {
- position: relative;
- left: $grid-unit-10;
- }
-
.components-modal__content.has-scrolled-content:not(.hide-header) & {
border-bottom-color: $gray-300;
}
diff --git a/packages/components/src/navigator/navigator-screen/component.tsx b/packages/components/src/navigator/navigator-screen/component.tsx
index be5c4bfaf41ec4..5882f271d4518f 100644
--- a/packages/components/src/navigator/navigator-screen/component.tsx
+++ b/packages/components/src/navigator/navigator-screen/component.tsx
@@ -17,6 +17,7 @@ import {
import { useMergeRefs } from '@wordpress/compose';
import { isRTL as isRTLFn } from '@wordpress/i18n';
import { escapeAttribute } from '@wordpress/escape-html';
+import warning from '@wordpress/warning';
/**
* Internal dependencies
@@ -33,6 +34,12 @@ function UnconnectedNavigatorScreen(
props: WordPressComponentProps< NavigatorScreenProps, 'div', false >,
forwardedRef: ForwardedRef< any >
) {
+ if ( ! /^\//.test( props.path ) ) {
+ warning(
+ 'wp.components.NavigatorScreen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.'
+ );
+ }
+
const screenId = useId();
const { children, className, path, ...otherProps } = useContextSystem(
props,
diff --git a/packages/components/src/navigator/test/index.tsx b/packages/components/src/navigator/test/index.tsx
index 9b9b257ea09681..820942a22644ba 100644
--- a/packages/components/src/navigator/test/index.tsx
+++ b/packages/components/src/navigator/test/index.tsx
@@ -642,6 +642,14 @@ describe( 'Navigator', () => {
).toHaveAttribute( 'id', INVALID_HTML_ATTRIBUTE.escaped );
} );
+ it( 'should warn if the `path` prop does not follow the required format', () => {
+ render( Test );
+
+ expect( console ).toHaveWarnedWith(
+ 'wp.components.NavigatorScreen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.'
+ );
+ } );
+
it( 'should match correctly paths with named arguments', async () => {
const user = userEvent.setup();
diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts
index 7bcab0e052e022..b6e033ab24ab74 100644
--- a/packages/components/src/private-apis.ts
+++ b/packages/components/src/private-apis.ts
@@ -1,8 +1,6 @@
/**
* Internal dependencies
*/
-import { Composite } from './composite';
-import { useCompositeStore } from './composite/store';
import { positionToPlacement as __experimentalPopoverLegacyPositionToPlacement } from './popover/utils';
import { createPrivateSlotFill } from './slot-fill';
import { DropdownMenuV2 } from './dropdown-menu-v2';
@@ -14,13 +12,6 @@ import { lock } from './lock-unlock';
export const privateApis = {};
lock( privateApis, {
- CompositeV2: Composite,
- CompositeGroupV2: Composite.Group,
- CompositeItemV2: Composite.Item,
- CompositeRowV2: Composite.Row,
- CompositeTypeaheadV2: Composite.Typeahead,
- CompositeHoverV2: Composite.Hover,
- useCompositeStoreV2: useCompositeStore,
__experimentalPopoverLegacyPositionToPlacement,
createPrivateSlotFill,
ComponentsContext,
diff --git a/packages/components/src/slot-fill/index.tsx b/packages/components/src/slot-fill/index.tsx
index b2df054973a5ba..03ed33a67f13b6 100644
--- a/packages/components/src/slot-fill/index.tsx
+++ b/packages/components/src/slot-fill/index.tsx
@@ -71,6 +71,7 @@ export function Provider( {
);
}
+Provider.displayName = 'SlotFillProvider';
export function createSlotFill( key: SlotKey ) {
const baseName = typeof key === 'symbol' ? key.description : key;
diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md
index 951f70604f41c6..cc0d7ef333f0d6 100644
--- a/packages/compose/CHANGELOG.md
+++ b/packages/compose/CHANGELOG.md
@@ -2,6 +2,13 @@
## Unreleased
+### New Features
+
+- `useEvent`: a new utility that creates a stable callback function that has access to the latest state and can be used within event handlers and effect callbacks ([#64943](https://github.com/WordPress/gutenberg/pull/64943)).
+- `useResizeObserver`: new and improved version of the utility (legacy API is still supported) ([#64943](https://github.com/WordPress/gutenberg/pull/64943)).
+
+## 7.7.0 (2024-09-05)
+
## 7.6.0 (2024-08-21)
## 7.5.0 (2024-08-07)
@@ -203,8 +210,8 @@
### Breaking Changes
-- Drop support for Internet Explorer 11 ([#31110](https://github.com/WordPress/gutenberg/pull/31110)). Learn more at https://make.wordpress.org/core/2021/04/22/ie-11-support-phase-out-plan/.
-- Increase the minimum Node.js version to v12 matching Long Term Support releases ([#31270](https://github.com/WordPress/gutenberg/pull/31270)). Learn more at https://nodejs.org/en/about/releases/.
+- Drop support for Internet Explorer 11 ([#31110](https://github.com/WordPress/gutenberg/pull/31110)). Learn more at .
+- Increase the minimum Node.js version to v12 matching Long Term Support releases ([#31270](https://github.com/WordPress/gutenberg/pull/31270)). Learn more at .
## 3.25.0 (2021-03-17)
diff --git a/packages/compose/README.md b/packages/compose/README.md
index 0da853ad75c5af..b4e20a79bab0cc 100644
--- a/packages/compose/README.md
+++ b/packages/compose/README.md
@@ -305,6 +305,29 @@ _Returns_
- `import('react').RefCallback`: Element Ref.
+### useEvent
+
+Creates a stable callback function that has access to the latest state and can be used within event handlers and effect callbacks. Throws when used in the render phase.
+
+_Usage_
+
+```tsx
+function Component( props ) {
+ const onClick = useEvent( props.onClick );
+ useEffect( () => {
+ onClick();
+ // Won't trigger the effect again when props.onClick is updated.
+ }, [ onClick ] );
+ // Won't re-render Button when props.onClick is updated (if `Button` is
+ // wrapped in `React.memo`).
+ return ;
+}
+```
+
+_Parameters_
+
+- _callback_ `T`: The callback function to wrap.
+
### useFocusableIframe
Dispatches a bubbling focus event when the iframe receives focus. Use `onFocus` as usual on the iframe or a parent element.
@@ -500,23 +523,30 @@ _Returns_
### useResizeObserver
-Hook which allows to listen to the resize event of any target element when it changes size. \_Note: `useResizeObserver` will report `null` sizes until after first render.
+Sets up a [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API) for an HTML or SVG element.
+
+Pass the returned setter as a callback ref to the React element you want to observe, or use it in layout effects for advanced use cases.
_Usage_
-```js
-const App = () => {
- const [ resizeListener, sizes ] = useResizeObserver();
+```tsx
+const setElement = useResizeObserver(
+ ( resizeObserverEntries ) => console.log( resizeObserverEntries ),
+ { box: 'border-box' }
+);
+
;
- return (
-
- { resizeListener }
- Your content here
-
- );
-};
+// The setter can be used in other ways, for example:
+useLayoutEffect( () => {
+ setElement( document.querySelector( `data-element-id="${ elementId }"` ) );
+}, [ elementId ] );
```
+_Parameters_
+
+- _callback_ `ResizeObserverCallback`: The `ResizeObserver` callback - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver#callback).
+- _options_ `ResizeObserverOptions`: Options passed to `ResizeObserver.observe` when called - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options). Changes will be ignored.
+
### useStateWithHistory
useState with undo/redo history.
diff --git a/packages/compose/package.json b/packages/compose/package.json
index 92b3085e585755..4b68841d1777e6 100644
--- a/packages/compose/package.json
+++ b/packages/compose/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/compose",
- "version": "7.6.0",
+ "version": "7.7.0",
"description": "WordPress higher-order components (HOCs).",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/compose/src/hooks/use-drop-zone/index.js b/packages/compose/src/hooks/use-drop-zone/index.js
index b537f0a5ab6227..c570c034415923 100644
--- a/packages/compose/src/hooks/use-drop-zone/index.js
+++ b/packages/compose/src/hooks/use-drop-zone/index.js
@@ -86,6 +86,7 @@ export default function useDropZone( {
*/
function isElementInZone( targetToCheck ) {
const { defaultView } = ownerDocument;
+
if (
! targetToCheck ||
! defaultView ||
@@ -165,6 +166,7 @@ export default function useDropZone( {
// zone.
// Note: This is not entirely reliable in Safari due to this bug
// https://bugs.webkit.org/show_bug.cgi?id=66547
+
if ( isElementInZone( event.relatedTarget ) ) {
return;
}
diff --git a/packages/compose/src/hooks/use-event/index.ts b/packages/compose/src/hooks/use-event/index.ts
new file mode 100644
index 00000000000000..d381bcb595bf65
--- /dev/null
+++ b/packages/compose/src/hooks/use-event/index.ts
@@ -0,0 +1,51 @@
+/**
+ * WordPress dependencies
+ */
+import { useRef, useInsertionEffect, useCallback } from '@wordpress/element';
+
+/**
+ * Any function.
+ */
+export type AnyFunction = ( ...args: any ) => any;
+
+/**
+ * Creates a stable callback function that has access to the latest state and
+ * can be used within event handlers and effect callbacks. Throws when used in
+ * the render phase.
+ *
+ * @param callback The callback function to wrap.
+ *
+ * @example
+ *
+ * ```tsx
+ * function Component( props ) {
+ * const onClick = useEvent( props.onClick );
+ * useEffect( () => {
+ * onClick();
+ * // Won't trigger the effect again when props.onClick is updated.
+ * }, [ onClick ] );
+ * // Won't re-render Button when props.onClick is updated (if `Button` is
+ * // wrapped in `React.memo`).
+ * return ;
+ * }
+ * ```
+ */
+export default function useEvent< T extends AnyFunction >(
+ /**
+ * The callback function to wrap.
+ */
+ callback?: T
+) {
+ const ref = useRef< AnyFunction | undefined >( () => {
+ throw new Error(
+ 'Callbacks created with `useEvent` cannot be called during rendering.'
+ );
+ } );
+ useInsertionEffect( () => {
+ ref.current = callback;
+ } );
+ return useCallback< AnyFunction >(
+ ( ...args ) => ref.current?.( ...args ),
+ []
+ ) as T;
+}
diff --git a/packages/compose/src/hooks/use-focus-on-mount/index.js b/packages/compose/src/hooks/use-focus-on-mount/index.js
index 77bee27772d2d0..bb02eb78e5689a 100644
--- a/packages/compose/src/hooks/use-focus-on-mount/index.js
+++ b/packages/compose/src/hooks/use-focus-on-mount/index.js
@@ -64,19 +64,17 @@ export default function useFocusOnMount( focusOnMount = 'firstElement' ) {
return;
}
- if ( focusOnMountRef.current === 'firstElement' ) {
- timerIdRef.current = setTimeout( () => {
- const firstTabbable = focus.tabbable.find( node )[ 0 ];
-
- if ( firstTabbable ) {
- setFocus( firstTabbable );
- }
- }, 0 );
-
+ if ( focusOnMountRef.current !== 'firstElement' ) {
+ setFocus( node );
return;
}
- setFocus( node );
+ timerIdRef.current = setTimeout( () => {
+ const firstTabbable = focus.tabbable.find( node )[ 0 ];
+ if ( firstTabbable ) {
+ setFocus( firstTabbable );
+ }
+ }, 0 );
return () => {
if ( timerIdRef.current ) {
diff --git a/packages/compose/src/hooks/use-resize-observer/index.native.js b/packages/compose/src/hooks/use-resize-observer/_legacy/index.native.js
similarity index 100%
rename from packages/compose/src/hooks/use-resize-observer/index.native.js
rename to packages/compose/src/hooks/use-resize-observer/_legacy/index.native.js
diff --git a/packages/compose/src/hooks/use-resize-observer/index.tsx b/packages/compose/src/hooks/use-resize-observer/_legacy/index.tsx
similarity index 86%
rename from packages/compose/src/hooks/use-resize-observer/index.tsx
rename to packages/compose/src/hooks/use-resize-observer/_legacy/index.tsx
index 05461bb2468b07..b44bd841964164 100644
--- a/packages/compose/src/hooks/use-resize-observer/index.tsx
+++ b/packages/compose/src/hooks/use-resize-observer/_legacy/index.tsx
@@ -6,14 +6,13 @@ import type { ReactElement } from 'react';
/**
* WordPress dependencies
*/
-import {
- useCallback,
- useLayoutEffect,
- useRef,
- useState,
-} from '@wordpress/element';
+import { useCallback, useRef, useState } from '@wordpress/element';
+/**
+ * Internal dependencies
+ */
+import useResizeObserver from '../index';
-type ObservedSize = {
+export type ObservedSize = {
width: number | null;
height: number | null;
};
@@ -84,28 +83,10 @@ type ResizeElementProps = {
};
function ResizeElement( { onResize }: ResizeElementProps ) {
- const resizeElementRef = useRef< HTMLDivElement >( null );
- const resizeCallbackRef = useRef( onResize );
-
- useLayoutEffect( () => {
- resizeCallbackRef.current = onResize;
- }, [ onResize ] );
-
- useLayoutEffect( () => {
- const resizeElement = resizeElementRef.current as HTMLDivElement;
- const resizeObserver = new ResizeObserver( ( entries ) => {
- for ( const entry of entries ) {
- const newSize = extractSize( entry );
- resizeCallbackRef.current( newSize );
- }
- } );
-
- resizeObserver.observe( resizeElement );
-
- return () => {
- resizeObserver.unobserve( resizeElement );
- };
- }, [] );
+ const resizeElementRef = useResizeObserver( ( entries ) => {
+ const newSize = extractSize( entries.at( -1 )! ); // Entries are never empty.
+ onResize( newSize );
+ } );
return (
{
const [ resizeObserver, sizes ] = useResizeObserver();
diff --git a/packages/compose/src/hooks/use-resize-observer/index.ts b/packages/compose/src/hooks/use-resize-observer/index.ts
new file mode 100644
index 00000000000000..2a76b2aa6ab590
--- /dev/null
+++ b/packages/compose/src/hooks/use-resize-observer/index.ts
@@ -0,0 +1,119 @@
+/**
+ * WordPress dependencies
+ */
+import { useRef } from '@wordpress/element';
+/**
+ * Internal dependencies
+ */
+import useEvent from '../use-event';
+import type { ObservedSize } from './_legacy';
+import _useLegacyResizeObserver from './_legacy';
+/**
+ * External dependencies
+ */
+import type { ReactElement } from 'react';
+
+// This is the current implementation of `useResizeObserver`.
+//
+// The legacy implementation is still supported for backwards compatibility.
+// This is achieved by overloading the exported function with both signatures,
+// and detecting which API is being used at runtime.
+function _useResizeObserver< T extends HTMLElement >(
+ callback: ResizeObserverCallback,
+ resizeObserverOptions: ResizeObserverOptions = {}
+): ( element?: T | null ) => void {
+ const callbackEvent = useEvent( callback );
+
+ const observedElementRef = useRef< T | null >();
+ const resizeObserverRef = useRef< ResizeObserver >();
+ return useEvent( ( element?: T | null ) => {
+ if ( element === observedElementRef.current ) {
+ return;
+ }
+ observedElementRef.current = element;
+
+ // Set up `ResizeObserver`.
+ resizeObserverRef.current ??= new ResizeObserver( callbackEvent );
+ const { current: resizeObserver } = resizeObserverRef;
+
+ // Unobserve previous element.
+ if ( observedElementRef.current ) {
+ resizeObserver.unobserve( observedElementRef.current );
+ }
+
+ // Observe new element.
+ if ( element ) {
+ resizeObserver.observe( element, resizeObserverOptions );
+ }
+ } );
+}
+
+/**
+ * Sets up a [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API)
+ * for an HTML or SVG element.
+ *
+ * Pass the returned setter as a callback ref to the React element you want
+ * to observe, or use it in layout effects for advanced use cases.
+ *
+ * @example
+ *
+ * ```tsx
+ * const setElement = useResizeObserver(
+ * ( resizeObserverEntries ) => console.log( resizeObserverEntries ),
+ * { box: 'border-box' }
+ * );
+ *
;
+ *
+ * // The setter can be used in other ways, for example:
+ * useLayoutEffect( () => {
+ * setElement( document.querySelector( `data-element-id="${ elementId }"` ) );
+ * }, [ elementId ] );
+ * ```
+ *
+ * @param callback The `ResizeObserver` callback - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver#callback).
+ * @param options Options passed to `ResizeObserver.observe` when called - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options). Changes will be ignored.
+ */
+export default function useResizeObserver< T extends Element >(
+ /**
+ * The `ResizeObserver` callback - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver#callback).
+ */
+ callback: ResizeObserverCallback,
+ /**
+ * Options passed to `ResizeObserver.observe` when called - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options). Changes will be ignored.
+ */
+ options?: ResizeObserverOptions
+): ( element?: T | null ) => void;
+
+/**
+ * **This is a legacy API and should not be used.**
+ *
+ * @deprecated Use the other `useResizeObserver` API instead: `const ref = useResizeObserver( ( entries ) => { ... } )`.
+ *
+ * Hook which allows to listen to the resize event of any target element when it changes size.
+ * _Note: `useResizeObserver` will report `null` sizes until after first render.
+ *
+ * @example
+ *
+ * ```js
+ * const App = () => {
+ * const [ resizeListener, sizes ] = useResizeObserver();
+ *
+ * return (
+ *
+ * { resizeListener }
+ * Your content here
+ *
+ * );
+ * };
+ * ```
+ */
+export default function useResizeObserver(): [ ReactElement, ObservedSize ];
+
+export default function useResizeObserver< T extends HTMLElement >(
+ callback?: ResizeObserverCallback,
+ options: ResizeObserverOptions = {}
+): ( ( element?: T | null ) => void ) | [ ReactElement, ObservedSize ] {
+ return callback
+ ? _useResizeObserver( callback, options )
+ : _useLegacyResizeObserver();
+}
diff --git a/packages/compose/src/index.js b/packages/compose/src/index.js
index f7e1d1618f97fb..29a9aa61737440 100644
--- a/packages/compose/src/index.js
+++ b/packages/compose/src/index.js
@@ -25,6 +25,7 @@ export { default as useCopyOnClick } from './hooks/use-copy-on-click';
export { default as useCopyToClipboard } from './hooks/use-copy-to-clipboard';
export { default as __experimentalUseDialog } from './hooks/use-dialog';
export { default as useDisabled } from './hooks/use-disabled';
+export { default as useEvent } from './hooks/use-event';
export { default as __experimentalUseDragging } from './hooks/use-dragging';
export { default as useFocusOnMount } from './hooks/use-focus-on-mount';
export { default as __experimentalUseFocusOutside } from './hooks/use-focus-outside';
diff --git a/packages/core-commands/CHANGELOG.md b/packages/core-commands/CHANGELOG.md
index 16bf8c495eb03c..02871d4f31e135 100644
--- a/packages/core-commands/CHANGELOG.md
+++ b/packages/core-commands/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 1.7.0 (2024-09-05)
+
## 1.6.0 (2024-08-21)
## 1.5.0 (2024-08-07)
diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json
index 02b8b76b4f145e..bae1e4c211cf0b 100644
--- a/packages/core-commands/package.json
+++ b/packages/core-commands/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/core-commands",
- "version": "1.6.0",
+ "version": "1.7.0",
"description": "WordPress core reusable commands.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md
index caed77084733bf..6286a55291a54f 100644
--- a/packages/core-data/CHANGELOG.md
+++ b/packages/core-data/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 7.7.0 (2024-09-05)
+
## 7.6.0 (2024-08-21)
## 7.5.0 (2024-08-07)
diff --git a/packages/core-data/package.json b/packages/core-data/package.json
index e96f3247086073..ddb2d2ef091778 100644
--- a/packages/core-data/package.json
+++ b/packages/core-data/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/core-data",
- "version": "7.6.0",
+ "version": "7.7.0",
"description": "Access to and manipulation of core WordPress entities.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js
index d1aaf0b447cfed..9229673903623a 100644
--- a/packages/core-data/src/resolvers.js
+++ b/packages/core-data/src/resolvers.js
@@ -155,7 +155,7 @@ export const getEntityRecord =
}
);
- if ( query !== undefined ) {
+ if ( query !== undefined && query._fields ) {
query = { ...query, include: [ key ] };
// The resolution cache won't consider query as reusable based on the
@@ -755,7 +755,7 @@ export const getUserPatternCategories =
export const getNavigationFallbackId =
() =>
- async ( { dispatch, select } ) => {
+ async ( { dispatch, select, registry } ) => {
const fallback = await apiFetch( {
path: addQueryArgs( '/wp-block-editor/v1/navigation-fallback', {
_embed: true,
@@ -764,9 +764,13 @@ export const getNavigationFallbackId =
const record = fallback?._embedded?.self;
- dispatch.receiveNavigationFallbackId( fallback?.id );
+ registry.batch( () => {
+ dispatch.receiveNavigationFallbackId( fallback?.id );
+
+ if ( ! record ) {
+ return;
+ }
- if ( record ) {
// If the fallback is already in the store, don't invalidate navigation queries.
// Otherwise, invalidate the cache for the scenario where there were no Navigation
// posts in the state and the fallback created one.
@@ -790,7 +794,7 @@ export const getNavigationFallbackId =
'wp_navigation',
fallback.id,
] );
- }
+ } );
};
export const getDefaultTemplateId =
@@ -817,7 +821,7 @@ export const getDefaultTemplateId =
*/
export const getRevisions =
( kind, name, recordKey, query = {} ) =>
- async ( { dispatch } ) => {
+ async ( { dispatch, registry } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
@@ -884,32 +888,36 @@ export const getRevisions =
} );
}
- dispatch.receiveRevisions(
- kind,
- name,
- recordKey,
- records,
- query,
- false,
- meta
- );
+ registry.batch( () => {
+ dispatch.receiveRevisions(
+ kind,
+ name,
+ recordKey,
+ records,
+ query,
+ false,
+ meta
+ );
- // When requesting all fields, the list of results can be used to
- // resolve the `getRevision` selector in addition to `getRevisions`.
- if ( ! query?._fields && ! query.context ) {
- const key = entityConfig.key || DEFAULT_ENTITY_KEY;
- const resolutionsArgs = records
- .filter( ( record ) => record[ key ] )
- .map( ( record ) => [
- kind,
- name,
- recordKey,
- record[ key ],
- ] );
+ // When requesting all fields, the list of results can be used to
+ // resolve the `getRevision` selector in addition to `getRevisions`.
+ if ( ! query?._fields && ! query.context ) {
+ const key = entityConfig.key || DEFAULT_ENTITY_KEY;
+ const resolutionsArgs = records
+ .filter( ( record ) => record[ key ] )
+ .map( ( record ) => [
+ kind,
+ name,
+ recordKey,
+ record[ key ],
+ ] );
- dispatch.startResolutions( 'getRevision', resolutionsArgs );
- dispatch.finishResolutions( 'getRevision', resolutionsArgs );
- }
+ dispatch.finishResolutions(
+ 'getRevision',
+ resolutionsArgs
+ );
+ }
+ } );
}
};
diff --git a/packages/core-data/src/test/resolvers.js b/packages/core-data/src/test/resolvers.js
index 946b795726d2a2..f15bee3b3c2b9b 100644
--- a/packages/core-data/src/test/resolvers.js
+++ b/packages/core-data/src/test/resolvers.js
@@ -80,7 +80,6 @@ describe( 'getEntityRecord', () => {
it( 'accepts a query that overrides default api path', async () => {
const query = { context: 'view', _envelope: '1' };
- const queryObj = { include: [ 'post' ], ...query };
const select = {
hasEntityRecords: jest.fn( () => {} ),
@@ -98,13 +97,6 @@ describe( 'getEntityRecord', () => {
query
)( { dispatch, select, registry } );
- // Check resolution cache for an existing entity that fulfills the request with query.
- expect( select.hasEntityRecords ).toHaveBeenCalledWith(
- 'root',
- 'postType',
- queryObj
- );
-
// Trigger apiFetch, test that the query is present in the url.
expect( triggerFetch ).toHaveBeenCalledWith( {
path: '/wp/v2/types/post?context=view&_envelope=1',
@@ -116,7 +108,7 @@ describe( 'getEntityRecord', () => {
'root',
'postType',
POST_TYPE,
- queryObj
+ query
);
// Locks should have been acquired and released.
diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md
index 52f97a68cbf87a..c044dfff15e3ac 100644
--- a/packages/create-block-interactive-template/CHANGELOG.md
+++ b/packages/create-block-interactive-template/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 2.7.0 (2024-09-05)
+
### Enhancements
- Set the minimum supported WordPress version to 6.6 to make it work seamlessly with the latest version of `@wordpress/scripts` package ([#64920](https://github.com/WordPress/gutenberg/pull/64920)).
diff --git a/packages/create-block-interactive-template/package.json b/packages/create-block-interactive-template/package.json
index 848cd757652150..667d3998ba820d 100644
--- a/packages/create-block-interactive-template/package.json
+++ b/packages/create-block-interactive-template/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/create-block-interactive-template",
- "version": "2.6.0",
+ "version": "2.7.0",
"description": "Template for @wordpress/create-block to create interactive blocks with the Interactivity API.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache b/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache
index 56f660ce2b1257..48469aa7d0d931 100644
--- a/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache
+++ b/packages/create-block-interactive-template/plugin-templates/$slug.php.mustache
@@ -9,7 +9,7 @@
{{/description}}
* Version: {{version}}
* Requires at least: 6.6
- * Requires PHP: 7.0
+ * Requires PHP: 7.2
{{#author}}
* Author: {{author}}
{{/author}}
diff --git a/packages/create-block-tutorial-template/CHANGELOG.md b/packages/create-block-tutorial-template/CHANGELOG.md
index 50290a8e07e90e..0fe0f748b8804d 100644
--- a/packages/create-block-tutorial-template/CHANGELOG.md
+++ b/packages/create-block-tutorial-template/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
### Enhancements
- Set the minimum supported WordPress version to 6.6 to make it work seamlessly with the latest version of `@wordpress/scripts` package ([#64920](https://github.com/WordPress/gutenberg/pull/64920)).
diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json
index 0d9baf39d8db80..64a8a90bc167c0 100644
--- a/packages/create-block-tutorial-template/package.json
+++ b/packages/create-block-tutorial-template/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/create-block-tutorial-template",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "This is a template for @wordpress/create-block that creates an example 'Copyright Date' block. This block is used in the official WordPress block development Quick Start Guide.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/create-block-tutorial-template/plugin-templates/$slug.php.mustache b/packages/create-block-tutorial-template/plugin-templates/$slug.php.mustache
index bcc7f7537d95a8..7ce4be3f7cc739 100644
--- a/packages/create-block-tutorial-template/plugin-templates/$slug.php.mustache
+++ b/packages/create-block-tutorial-template/plugin-templates/$slug.php.mustache
@@ -9,7 +9,7 @@
{{/description}}
* Version: {{version}}
* Requires at least: 6.6
- * Requires PHP: 7.0
+ * Requires PHP: 7.2
{{#author}}
* Author: {{author}}
{{/author}}
diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md
index 3f742b352614e0..99e7b1fd37a33a 100644
--- a/packages/create-block/CHANGELOG.md
+++ b/packages/create-block/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.50.0 (2024-09-05)
+
### Enhancements
- Unpin the `@wordpress/scripts` version and set the minimum supported WordPress version to 6.6 ([#64920](https://github.com/WordPress/gutenberg/pull/64920)).
diff --git a/packages/create-block/lib/templates/es5/$slug.php.mustache b/packages/create-block/lib/templates/es5/$slug.php.mustache
index a67c3ad55d3fec..825fd1bfd8b5aa 100644
--- a/packages/create-block/lib/templates/es5/$slug.php.mustache
+++ b/packages/create-block/lib/templates/es5/$slug.php.mustache
@@ -8,7 +8,7 @@
* Description: {{description}}
{{/description}}
* Requires at least: 6.6
- * Requires PHP: 7.0
+ * Requires PHP: 7.2
* Version: {{version}}
{{#author}}
* Author: {{author}}
diff --git a/packages/create-block/lib/templates/plugin/$slug.php.mustache b/packages/create-block/lib/templates/plugin/$slug.php.mustache
index 4524477ba73df2..75666af3a850b2 100644
--- a/packages/create-block/lib/templates/plugin/$slug.php.mustache
+++ b/packages/create-block/lib/templates/plugin/$slug.php.mustache
@@ -8,7 +8,7 @@
* Description: {{description}}
{{/description}}
* Requires at least: 6.6
- * Requires PHP: 7.0
+ * Requires PHP: 7.2
* Version: {{version}}
{{#author}}
* Author: {{author}}
diff --git a/packages/create-block/package.json b/packages/create-block/package.json
index 7548fca1532f07..2103a85c5894f2 100644
--- a/packages/create-block/package.json
+++ b/packages/create-block/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/create-block",
- "version": "4.49.0",
+ "version": "4.50.0",
"description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/customize-widgets/CHANGELOG.md b/packages/customize-widgets/CHANGELOG.md
index aeec00430f47d1..4e0d5a37553577 100644
--- a/packages/customize-widgets/CHANGELOG.md
+++ b/packages/customize-widgets/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json
index 9c8c0a79b771ab..a8b50cf9cb318c 100644
--- a/packages/customize-widgets/package.json
+++ b/packages/customize-widgets/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/customize-widgets",
- "version": "5.6.1",
+ "version": "5.7.0",
"description": "Widgets blocks in Customizer Module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/data-controls/CHANGELOG.md b/packages/data-controls/CHANGELOG.md
index ae38ef764c535d..e6af7fdcb6e240 100644
--- a/packages/data-controls/CHANGELOG.md
+++ b/packages/data-controls/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json
index 216f4000adff8e..e97e59b74700cd 100644
--- a/packages/data-controls/package.json
+++ b/packages/data-controls/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/data-controls",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "A set of common controls for the @wordpress/data api.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md
index 52b88a1c5d4297..4ba90611c2a787 100644
--- a/packages/data/CHANGELOG.md
+++ b/packages/data/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 10.7.0 (2024-09-05)
+
## 10.6.0 (2024-08-21)
## 10.5.0 (2024-08-07)
diff --git a/packages/data/package.json b/packages/data/package.json
index fb27caaf37798f..84ad7a7973bc49 100644
--- a/packages/data/package.json
+++ b/packages/data/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/data",
- "version": "10.6.0",
+ "version": "10.7.0",
"description": "Data module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md
index bf93b9e52f657a..1e323714c4d5f7 100644
--- a/packages/dataviews/CHANGELOG.md
+++ b/packages/dataviews/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.3.0 (2024-09-05)
+
## 4.2.0 (2024-08-21)
## New features
diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json
index e02c0a624aa41c..e327a217435c52 100644
--- a/packages/dataviews/package.json
+++ b/packages/dataviews/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/dataviews",
- "version": "4.2.0",
+ "version": "4.3.0",
"description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx
index 24ef3b5594b413..459b68990753c1 100644
--- a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx
+++ b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx
@@ -11,27 +11,15 @@ import removeAccents from 'remove-accents';
import { useInstanceId } from '@wordpress/compose';
import { __, sprintf } from '@wordpress/i18n';
import { useState, useMemo, useDeferredValue } from '@wordpress/element';
-import {
- VisuallyHidden,
- Icon,
- privateApis as componentsPrivateApis,
-} from '@wordpress/components';
+import { VisuallyHidden, Icon, Composite } from '@wordpress/components';
import { search, check } from '@wordpress/icons';
import { SVG, Circle } from '@wordpress/primitives';
/**
* Internal dependencies
*/
-import { unlock } from '../../lock-unlock';
import type { Filter, NormalizedFilter, View } from '../../types';
-const {
- CompositeV2: Composite,
- CompositeItemV2: CompositeItem,
- CompositeHoverV2: CompositeHover,
- CompositeTypeaheadV2: CompositeTypeahead,
-} = unlock( componentsPrivateApis );
-
interface SearchWidgetProps {
view: View;
filter: NormalizedFilter;
@@ -126,8 +114,7 @@ function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) {
) }
onFocusVisible={ () => {
// `onFocusVisible` needs the `Composite` component to be focusable,
- // which is implicitly achieved via the `virtualFocus: true` option
- // in the `useCompositeStore` hook.
+ // which is implicitly achieved via the `virtualFocus` prop.
if ( ! activeCompositeId && filter.elements.length ) {
setActiveCompositeId(
generateFilterElementCompositeItemId(
@@ -137,13 +124,13 @@ function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) {
);
}
} }
- render={
}
+ render={
}
>
{ filter.elements.map( ( element ) => (
-
{ element.label }
-
+
) ) }
);
diff --git a/packages/dataviews/src/components/dataviews-filters/style.scss b/packages/dataviews/src/components/dataviews-filters/style.scss
index f2841c16f0fec4..ad834fb224e2e4 100644
--- a/packages/dataviews/src/components/dataviews-filters/style.scss
+++ b/packages/dataviews/src/components/dataviews-filters/style.scss
@@ -169,7 +169,7 @@
display: flex;
align-items: center;
gap: $grid-unit-10;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
box-sizing: border-box;
padding: $grid-unit-10 $grid-unit-15;
cursor: default;
diff --git a/packages/dataviews/src/components/dataviews/stories/fixtures.tsx b/packages/dataviews/src/components/dataviews/stories/fixtures.tsx
index 2ab02ec728e5e0..ff098209b34684 100644
--- a/packages/dataviews/src/components/dataviews/stories/fixtures.tsx
+++ b/packages/dataviews/src/components/dataviews/stories/fixtures.tsx
@@ -547,7 +547,15 @@ export const themeData: Theme[] = [
export const themeFields: Field< Theme >[] = [
{ id: 'slug', label: 'Slug' },
{ id: 'name', label: 'Name' },
- { id: 'description', label: 'Description' },
+ {
+ id: 'description',
+ label: 'Description',
+ render: ( { item } ) => (
+
+ { item.description }
+
+ ),
+ },
{ id: 'requires', label: 'Requires at least' },
{ id: 'tested', label: 'Tested up to' },
{
diff --git a/packages/dataviews/src/components/dataviews/stories/index.story.tsx b/packages/dataviews/src/components/dataviews/stories/index.story.tsx
index 645c6d7ddcd922..3b3e1326115b0e 100644
--- a/packages/dataviews/src/components/dataviews/stories/index.story.tsx
+++ b/packages/dataviews/src/components/dataviews/stories/index.story.tsx
@@ -17,7 +17,9 @@ import {
} from './fixtures';
import { LAYOUT_GRID, LAYOUT_LIST, LAYOUT_TABLE } from '../../../constants';
import { filterSortAndPaginate } from '../../../filter-and-sort-data-view';
-import type { View } from '../../../types';
+import type { CombinedField, View } from '../../../types';
+
+import './style.css';
const meta = {
title: 'DataViews/DataViews',
@@ -132,19 +134,39 @@ export const FieldsNoSortableNoHidable = () => {
};
export const CombinedFields = () => {
- const [ view, setView ] = useState< View >( {
- ...DEFAULT_VIEW,
- fields: [ 'theme', 'requires', 'tested' ],
- layout: {
- combinedFields: [
- {
- id: 'theme',
- label: 'Theme',
- children: [ 'name', 'description' ],
- direction: 'vertical',
+ const defaultLayoutsThemes = {
+ table: {
+ fields: [ 'theme', 'requires', 'tested' ],
+ layout: {
+ primaryField: 'name',
+ combinedFields: [
+ {
+ id: 'theme',
+ label: 'Theme',
+ children: [ 'name', 'description' ],
+ direction: 'vertical',
+ },
+ ] as CombinedField[],
+ styles: {
+ theme: {
+ maxWidth: 300,
+ },
},
- ],
+ },
},
+ grid: {
+ fields: [ 'description', 'requires', 'tested' ],
+ layout: { primaryField: 'name', columnFields: [ 'description' ] },
+ },
+ list: {
+ fields: [ 'requires', 'tested' ],
+ layout: { primaryField: 'name' },
+ },
+ };
+ const [ view, setView ] = useState< View >( {
+ ...DEFAULT_VIEW,
+ fields: defaultLayoutsThemes.table.fields,
+ layout: defaultLayoutsThemes.table.layout,
} );
const { data: shownData, paginationInfo } = useMemo( () => {
return filterSortAndPaginate( themeData, view, themeFields );
@@ -158,7 +180,7 @@ export const CombinedFields = () => {
view={ view }
fields={ themeFields }
onChangeView={ setView }
- defaultLayouts={ { table: {} } }
+ defaultLayouts={ defaultLayoutsThemes }
/>
);
};
diff --git a/packages/dataviews/src/components/dataviews/stories/style.css b/packages/dataviews/src/components/dataviews/stories/style.css
new file mode 100644
index 00000000000000..7c0c6565a6edf4
--- /dev/null
+++ b/packages/dataviews/src/components/dataviews/stories/style.css
@@ -0,0 +1,4 @@
+.theme-field-description {
+ text-wrap: balance;
+ text-wrap: pretty;
+}
\ No newline at end of file
diff --git a/packages/dataviews/src/dataviews-layouts/grid/style.scss b/packages/dataviews/src/dataviews-layouts/grid/style.scss
index d154d2b614def5..5fab362b0b47b6 100644
--- a/packages/dataviews/src/dataviews-layouts/grid/style.scss
+++ b/packages/dataviews/src/dataviews-layouts/grid/style.scss
@@ -118,7 +118,7 @@
background: $gray-100;
padding: 0 $grid-unit-10;
min-height: $grid-unit-30;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
display: flex;
align-items: center;
font-size: 12px;
diff --git a/packages/dataviews/src/dataviews-layouts/list/index.tsx b/packages/dataviews/src/dataviews-layouts/list/index.tsx
index 00146c3ee35220..8a3f6a297338c8 100644
--- a/packages/dataviews/src/dataviews-layouts/list/index.tsx
+++ b/packages/dataviews/src/dataviews-layouts/list/index.tsx
@@ -14,6 +14,7 @@ import {
privateApis as componentsPrivateApis,
Spinner,
VisuallyHidden,
+ Composite,
} from '@wordpress/components';
import {
useCallback,
@@ -48,12 +49,7 @@ interface ListViewItemProps< Item > {
onDropdownTriggerKeyDown: React.KeyboardEventHandler< HTMLButtonElement >;
}
-const {
- CompositeV2: Composite,
- CompositeItemV2: CompositeItem,
- CompositeRowV2: CompositeRow,
- DropdownMenuV2: DropdownMenu,
-} = unlock( componentsPrivateApis );
+const { DropdownMenuV2: DropdownMenu } = unlock( componentsPrivateApis );
function generateItemWrapperCompositeId( idPrefix: string ) {
return `${ idPrefix }-item-wrapper`;
@@ -92,7 +88,7 @@ function PrimaryActionGridCell< Item >( {
return 'RenderModal' in primaryAction ? (
- ( {
closeModal={ () => setIsModalOpen( false ) }
/>
) }
-
+
) : (
-
( {
visibleFields,
onDropdownTriggerKeyDown,
}: ListViewItemProps< Item > ) {
- const itemRef = useRef< HTMLElement >( null );
+ const itemRef = useRef< HTMLDivElement >( null );
const labelId = `${ idPrefix }-label`;
const descriptionId = `${ idPrefix }-description`;
@@ -192,7 +188,7 @@ function ListItem< Item >( {
) : null;
return (
- }
role="row"
@@ -209,7 +205,7 @@ function ListItem< Item >( {
spacing={ 0 }
>
- }
role="button"
id={ generateItemWrapperCompositeId( idPrefix ) }
@@ -260,7 +256,7 @@ function ListItem< Item >( {
-
+
{ eligibleActions?.length > 0 && (
( {
( {
) }
-
+
);
}
diff --git a/packages/dataviews/src/dataviews-layouts/list/style.scss b/packages/dataviews/src/dataviews-layouts/list/style.scss
index 7344af6c3d1e82..ea3236f6d75e1e 100644
--- a/packages/dataviews/src/dataviews-layouts/list/style.scss
+++ b/packages/dataviews/src/dataviews-layouts/list/style.scss
@@ -114,12 +114,12 @@ ul.dataviews-view-list {
&::before {
position: absolute;
content: "";
- top: calc(var(--wp-admin-border-width-focus) + 1px);
+ top: var(--wp-admin-border-width-focus);
right: var(--wp-admin-border-width-focus);
bottom: var(--wp-admin-border-width-focus);
left: var(--wp-admin-border-width-focus);
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
}
}
.dataviews-view-list__primary-field {
diff --git a/packages/dataviews/src/dataviews-layouts/table/style.scss b/packages/dataviews/src/dataviews-layouts/table/style.scss
index dd091db4ec99f5..ea2c614e4339df 100644
--- a/packages/dataviews/src/dataviews-layouts/table/style.scss
+++ b/packages/dataviews/src/dataviews-layouts/table/style.scss
@@ -171,10 +171,6 @@
}
}
-.dataviews-view-table__cell-content-wrapper:empty {
- display: none;
-}
-
/* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */
@container (max-width: 430px) {
.dataviews-view-table tr td:first-child,
diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md
index 3940814a2f5d88..e19aa08ab6fe50 100644
--- a/packages/date/CHANGELOG.md
+++ b/packages/date/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/date/package.json b/packages/date/package.json
index b927d596864433..488db71cfd693a 100644
--- a/packages/date/package.json
+++ b/packages/date/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/date",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Date module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md
index ddfd7fb32710a7..1da5f074301fd6 100644
--- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md
+++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/dependency-extraction-webpack-plugin/lib/util.js b/packages/dependency-extraction-webpack-plugin/lib/util.js
index ee5c2face1b9d1..fcc6e5a0ed1731 100644
--- a/packages/dependency-extraction-webpack-plugin/lib/util.js
+++ b/packages/dependency-extraction-webpack-plugin/lib/util.js
@@ -9,6 +9,7 @@ const BUNDLED_PACKAGES = [
'@wordpress/interface',
'@wordpress/sync',
'@wordpress/undo-manager',
+ '@wordpress/fields',
];
/**
diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json
index 5fd9b52f1acfdd..f720a1df826e80 100644
--- a/packages/dependency-extraction-webpack-plugin/package.json
+++ b/packages/dependency-extraction-webpack-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/dependency-extraction-webpack-plugin",
- "version": "6.6.0",
+ "version": "6.7.0",
"description": "Extract WordPress script dependencies from webpack bundles.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/deprecated/CHANGELOG.md b/packages/deprecated/CHANGELOG.md
index 5a19167c70965e..673567a2a3e492 100644
--- a/packages/deprecated/CHANGELOG.md
+++ b/packages/deprecated/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json
index 244a8a0825e297..ed2f9434539efe 100644
--- a/packages/deprecated/package.json
+++ b/packages/deprecated/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/deprecated",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Deprecation utility for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/docgen/CHANGELOG.md b/packages/docgen/CHANGELOG.md
index db2451d2184338..3aefc7cae99ae2 100644
--- a/packages/docgen/CHANGELOG.md
+++ b/packages/docgen/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 2.7.0 (2024-09-05)
+
## 2.6.0 (2024-08-21)
## 2.5.0 (2024-08-07)
diff --git a/packages/docgen/lib/get-type-annotation.js b/packages/docgen/lib/get-type-annotation.js
index b844c86ef7958a..5e72724952f29e 100644
--- a/packages/docgen/lib/get-type-annotation.js
+++ b/packages/docgen/lib/get-type-annotation.js
@@ -401,6 +401,10 @@ function getTypeAnnotation( typeAnnotation ) {
* TODO: Remove the special-casing here once we're able to infer the types from TypeScript itself.
*/
function unwrapWrappedSelectors( token ) {
+ if ( babelTypes.isTSDeclareFunction( token ) ) {
+ return token;
+ }
+
if ( babelTypes.isFunctionDeclaration( token ) ) {
return token;
}
diff --git a/packages/docgen/package.json b/packages/docgen/package.json
index e5b8b294cde520..38a228d50a8e0f 100644
--- a/packages/docgen/package.json
+++ b/packages/docgen/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/docgen",
- "version": "2.6.0",
+ "version": "2.7.0",
"description": "Autogenerate public API documentation from exports and JSDoc comments.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/dom-ready/CHANGELOG.md b/packages/dom-ready/CHANGELOG.md
index 45cf7f02b51061..f3290182dff5c2 100644
--- a/packages/dom-ready/CHANGELOG.md
+++ b/packages/dom-ready/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json
index 5911fba9c58ebd..89acde8f2b1245 100644
--- a/packages/dom-ready/package.json
+++ b/packages/dom-ready/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/dom-ready",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Execute callback after the DOM is loaded.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md
index 6444d64321a28d..1857e3d51cb70f 100644
--- a/packages/dom/CHANGELOG.md
+++ b/packages/dom/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/dom/package.json b/packages/dom/package.json
index d861e4263accea..dd05677fcb9303 100644
--- a/packages/dom/package.json
+++ b/packages/dom/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/dom",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "DOM utilities module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-test-utils-playwright/CHANGELOG.md b/packages/e2e-test-utils-playwright/CHANGELOG.md
index 2344239ebcdb27..eb3cbd95fe3ad0 100644
--- a/packages/e2e-test-utils-playwright/CHANGELOG.md
+++ b/packages/e2e-test-utils-playwright/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 1.7.0 (2024-09-05)
+
## 1.6.0 (2024-08-21)
## 1.5.0 (2024-08-07)
diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json
index 2430644c77b3b4..1bb80bc4cc7643 100644
--- a/packages/e2e-test-utils-playwright/package.json
+++ b/packages/e2e-test-utils-playwright/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/e2e-test-utils-playwright",
- "version": "1.6.0",
+ "version": "1.7.0",
"description": "End-To-End (E2E) test utils for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md
index da33570821a311..b22c016a799bc5 100644
--- a/packages/e2e-test-utils/CHANGELOG.md
+++ b/packages/e2e-test-utils/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 11.7.0 (2024-09-05)
+
## 11.6.0 (2024-08-21)
## 11.5.0 (2024-08-07)
diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json
index d533cacee7a8be..02cee410f33d66 100644
--- a/packages/e2e-test-utils/package.json
+++ b/packages/e2e-test-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/e2e-test-utils",
- "version": "11.6.0",
+ "version": "11.7.0",
"description": "End-To-End (E2E) test utils for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-tests/CHANGELOG.md b/packages/e2e-tests/CHANGELOG.md
index 568e58c11cdb0a..a7ba03f05f4668 100644
--- a/packages/e2e-tests/CHANGELOG.md
+++ b/packages/e2e-tests/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 8.7.0 (2024-09-05)
+
## 8.6.0 (2024-08-21)
## 8.5.0 (2024-08-07)
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index f6491e6d58c531..81e7fa1802c65b 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/e2e-tests",
- "version": "8.6.0",
+ "version": "8.7.0",
"description": "End-To-End (E2E) tests for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php
index b2eb9d797610d5..143feb240ac2ef 100644
--- a/packages/e2e-tests/plugins/block-bindings.php
+++ b/packages/e2e-tests/plugins/block-bindings.php
@@ -41,6 +41,16 @@ function gutenberg_test_block_bindings_registration() {
'default' => '#url-custom-field',
)
);
+ register_meta(
+ 'post',
+ 'empty_field',
+ array(
+ 'show_in_rest' => true,
+ 'type' => 'string',
+ 'single' => true,
+ 'default' => '',
+ )
+ );
register_meta(
'post',
'_protected_field',
diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md
index c715ddebcd82a1..cd4b4a36295f07 100644
--- a/packages/edit-post/CHANGELOG.md
+++ b/packages/edit-post/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 8.7.0 (2024-09-05)
+
## 8.6.0 (2024-08-21)
## 8.5.0 (2024-08-07)
diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json
index 74152b849cf22b..0d8e0b28f7c7c5 100644
--- a/packages/edit-post/package.json
+++ b/packages/edit-post/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/edit-post",
- "version": "8.6.1",
+ "version": "8.7.0",
"description": "Edit Post module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/edit-post/src/components/back-button/style.scss b/packages/edit-post/src/components/back-button/style.scss
index 8ffae89e31e054..aced752bfedfae 100644
--- a/packages/edit-post/src/components/back-button/style.scss
+++ b/packages/edit-post/src/components/back-button/style.scss
@@ -32,7 +32,7 @@
right: 9px;
bottom: 9px + $border-width; // Height of toolbar in edit-post (not edit-site) is 61px tall.
left: 9px;
- border-radius: $radius-block-ui + $border-width + $border-width;
+ border-radius: $radius-small + $border-width + $border-width;
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) $gray-900;
}
@@ -54,7 +54,7 @@
.edit-post-fullscreen-mode-close_site-icon {
width: $button-size;
height: $button-size;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
object-fit: cover;
// Compensate for the top-bar border.
margin-top: -($border-width);
diff --git a/packages/edit-post/src/components/browser-url/index.js b/packages/edit-post/src/components/browser-url/index.js
index 5f389c4c82818f..12292cb8447217 100644
--- a/packages/edit-post/src/components/browser-url/index.js
+++ b/packages/edit-post/src/components/browser-url/index.js
@@ -17,22 +17,6 @@ export function getPostEditURL( postId ) {
return addQueryArgs( 'post.php', { post: postId, action: 'edit' } );
}
-/**
- * Returns the Post's Trashed URL.
- *
- * @param {number} postId Post ID.
- * @param {string} postType Post Type.
- *
- * @return {string} Post trashed URL.
- */
-export function getPostTrashedURL( postId, postType ) {
- return addQueryArgs( 'edit.php', {
- trashed: 1,
- post_type: postType,
- ids: postId,
- } );
-}
-
export class BrowserURL extends Component {
constructor() {
super( ...arguments );
@@ -43,17 +27,9 @@ export class BrowserURL extends Component {
}
componentDidUpdate( prevProps ) {
- const { postId, postStatus, postType, isSavingPost, hasHistory } =
- this.props;
+ const { postId, postStatus, hasHistory } = this.props;
const { historyId } = this.state;
- // Posts are still dirty while saving so wait for saving to finish
- // to avoid the unsaved changes warning when trashing posts.
- if ( postStatus === 'trash' && ! isSavingPost ) {
- this.setTrashURL( postId, postType );
- return;
- }
-
if (
( postId !== prevProps.postId || postId !== historyId ) &&
postStatus !== 'auto-draft' &&
@@ -64,16 +40,6 @@ export class BrowserURL extends Component {
}
}
- /**
- * Navigates the browser to the post trashed URL to show a notice about the trashed post.
- *
- * @param {number} postId Post ID.
- * @param {string} postType Post Type.
- */
- setTrashURL( postId, postType ) {
- window.location.href = getPostTrashedURL( postId, postType );
- }
-
/**
* Replaces the browser URL with a post editor link for the given post ID.
*
@@ -101,7 +67,7 @@ export class BrowserURL extends Component {
}
export default withSelect( ( select ) => {
- const { getCurrentPost, isSavingPost } = select( editorStore );
+ const { getCurrentPost } = select( editorStore );
const post = getCurrentPost();
let { id, status, type } = post;
const isTemplate = [ 'wp_template', 'wp_template_part' ].includes( type );
@@ -112,7 +78,5 @@ export default withSelect( ( select ) => {
return {
postId: id,
postStatus: status,
- postType: type,
- isSavingPost: isSavingPost(),
};
} )( BrowserURL );
diff --git a/packages/edit-post/src/components/browser-url/test/index.js b/packages/edit-post/src/components/browser-url/test/index.js
index d4b09fc29d7eff..01522680419fe2 100644
--- a/packages/edit-post/src/components/browser-url/test/index.js
+++ b/packages/edit-post/src/components/browser-url/test/index.js
@@ -6,7 +6,7 @@ import { render } from '@testing-library/react';
/**
* Internal dependencies
*/
-import { getPostEditURL, getPostTrashedURL, BrowserURL } from '../';
+import { getPostEditURL, BrowserURL } from '../';
describe( 'getPostEditURL', () => {
it( 'should generate relative path with post and action arguments', () => {
@@ -16,14 +16,6 @@ describe( 'getPostEditURL', () => {
} );
} );
-describe( 'getPostTrashedURL', () => {
- it( 'should generate relative path with post and action arguments', () => {
- const url = getPostTrashedURL( 1, 'page' );
-
- expect( url ).toBe( 'edit.php?trashed=1&post_type=page&ids=1' );
- } );
-} );
-
describe( 'BrowserURL', () => {
let replaceStateSpy;
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js
index 519b32691c6a49..185f97ba45a56f 100644
--- a/packages/edit-post/src/components/layout/index.js
+++ b/packages/edit-post/src/components/layout/index.js
@@ -126,8 +126,8 @@ function useEditorStyles() {
? editorSettings.styles ?? []
: defaultEditorStyles;
- // Add a constant padding for the typewriter effect. When typing at the
- // bottom, there needs to be room to scroll up.
+ // Add a space for the typewriter effect. When typing in the last block,
+ // there needs to be room to scroll up.
if (
! isZoomedOutView &&
renderingMode === 'post-only' &&
diff --git a/packages/edit-post/src/components/layout/use-padding-appender.js b/packages/edit-post/src/components/layout/use-padding-appender.js
index ff342ded90817a..efd46a485058ca 100644
--- a/packages/edit-post/src/components/layout/use-padding-appender.js
+++ b/packages/edit-post/src/components/layout/use-padding-appender.js
@@ -18,12 +18,12 @@ export function usePaddingAppender() {
const { ownerDocument } = node;
const { defaultView } = ownerDocument;
- const paddingBottom = defaultView.parseInt(
- defaultView.getComputedStyle( node ).paddingBottom,
+ const pseudoHeight = defaultView.parseInt(
+ defaultView.getComputedStyle( node, ':after' ).height,
10
);
- if ( ! paddingBottom ) {
+ if ( ! pseudoHeight ) {
return;
}
@@ -38,25 +38,20 @@ export function usePaddingAppender() {
return;
}
- event.preventDefault();
+ event.stopPropagation();
const blockOrder = registry
.select( blockEditorStore )
.getBlockOrder( '' );
const lastBlockClientId = blockOrder[ blockOrder.length - 1 ];
- // Do nothing when only default block appender is present.
- if ( ! lastBlockClientId ) {
- return;
- }
-
const lastBlock = registry
.select( blockEditorStore )
.getBlock( lastBlockClientId );
const { selectBlock, insertDefaultBlock } =
registry.dispatch( blockEditorStore );
- if ( isUnmodifiedDefaultBlock( lastBlock ) ) {
+ if ( lastBlock && isUnmodifiedDefaultBlock( lastBlock ) ) {
selectBlock( lastBlockClientId );
} else {
insertDefaultBlock();
diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js
index 10f4a9838c8dad..daf789cb0a2ec9 100644
--- a/packages/edit-post/src/index.js
+++ b/packages/edit-post/src/index.js
@@ -70,9 +70,17 @@ export function initializeEditor(
showBlockBreadcrumbs: true,
showIconLabels: false,
showListViewByDefault: false,
+ enableChoosePatternModal: true,
isPublishSidebarEnabled: true,
} );
+ if ( window.__experimentalMediaProcessing ) {
+ dispatch( preferencesStore ).setDefaults( 'core/media', {
+ requireApproval: true,
+ optimizeOnUpload: true,
+ } );
+ }
+
dispatch( blocksStore ).reapplyBlockTypeFilters();
// Check if the block list view should be open by default.
diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md
index 2bed952f89a4e6..025d2389e4b01d 100644
--- a/packages/edit-site/CHANGELOG.md
+++ b/packages/edit-site/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json
index 52292464a21d69..43bcf68aa68e68 100644
--- a/packages/edit-site/package.json
+++ b/packages/edit-site/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/edit-site",
- "version": "6.6.1",
+ "version": "6.7.0",
"description": "Edit Site Page module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/edit-site/src/components/add-new-template/add-custom-template-modal-content.js b/packages/edit-site/src/components/add-new-template/add-custom-template-modal-content.js
index 0ef403379ccdb8..4993f12153b9e4 100644
--- a/packages/edit-site/src/components/add-new-template/add-custom-template-modal-content.js
+++ b/packages/edit-site/src/components/add-new-template/add-custom-template-modal-content.js
@@ -9,7 +9,7 @@ import {
FlexItem,
SearchControl,
TextHighlight,
- privateApis as componentsPrivateApis,
+ Composite,
__experimentalText as Text,
__experimentalVStack as VStack,
} from '@wordpress/components';
@@ -20,15 +20,8 @@ import { useDebouncedInput } from '@wordpress/compose';
/**
* Internal dependencies
*/
-import { unlock } from '../../lock-unlock';
import { mapToIHasNameAndId } from './utils';
-const {
- CompositeV2: Composite,
- CompositeItemV2: CompositeItem,
- useCompositeStoreV2: useCompositeStore,
-} = unlock( componentsPrivateApis );
-
const EMPTY_ARRAY = [];
function SuggestionListItem( {
@@ -40,7 +33,7 @@ function SuggestionListItem( {
const baseCssClass =
'edit-site-custom-template-modal__suggestions_list__list-item';
return (
-
) }
-
+
);
}
@@ -122,7 +115,6 @@ function useSearchSuggestions( entityForSuggestions, search ) {
}
function SuggestionList( { entityForSuggestions, onSelect } ) {
- const composite = useCompositeStore( { orientation: 'vertical' } );
const [ search, setSearch, debouncedSearch ] = useDebouncedInput();
const suggestions = useSearchSuggestions(
entityForSuggestions,
@@ -146,7 +138,7 @@ function SuggestionList( { entityForSuggestions, onSelect } ) {
) }
{ !! suggestions?.length && (
.components-button {
padding: $grid-unit-40;
- border-radius: $radius-block-ui;
display: flex;
flex-direction: column;
border: $border-width solid $gray-300;
diff --git a/packages/edit-site/src/components/editor-canvas-container/style.scss b/packages/edit-site/src/components/editor-canvas-container/style.scss
index fad566212e7328..7df2b0ceca552f 100644
--- a/packages/edit-site/src/components/editor-canvas-container/style.scss
+++ b/packages/edit-site/src/components/editor-canvas-container/style.scss
@@ -16,7 +16,7 @@
.edit-site-editor-canvas-container__section {
background: $white; // Fallback color, overridden by JavaScript.
- border-radius: $radius-block-ui;
+ border-radius: $radius-large;
bottom: 0;
left: 0;
overflow: hidden;
diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/style.scss b/packages/edit-site/src/components/global-styles/screen-revisions/style.scss
index 46972f4b7c52ff..5d0e7cb185137a 100644
--- a/packages/edit-site/src/components/global-styles/screen-revisions/style.scss
+++ b/packages/edit-site/src/components/global-styles/screen-revisions/style.scss
@@ -43,7 +43,7 @@
}
&.is-selected {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
// Only visible in Windows High Contrast mode.
outline: 3px solid transparent;
diff --git a/packages/edit-site/src/components/global-styles/style.scss b/packages/edit-site/src/components/global-styles/style.scss
index f85d69ad864ad8..9bab48e308947a 100644
--- a/packages/edit-site/src/components/global-styles/style.scss
+++ b/packages/edit-site/src/components/global-styles/style.scss
@@ -19,7 +19,7 @@
min-height: 100px;
margin-bottom: $grid-unit-20;
background: $gray-100;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
overflow: hidden;
}
@@ -45,7 +45,7 @@
display: flex !important;
align-items: center;
justify-content: center;
- border-radius: $radius-block-ui;
+ border-radius: $radius-x-small;
}
.edit-site-global-styles-screen-typography__font-variants-count {
@@ -113,14 +113,14 @@
position: relative;
width: 100%;
border: $gray-200 $border-width solid;
- border-radius: $radius-block-ui;
+ border-radius: $radius-medium;
overflow: hidden;
}
.edit-site-global-styles__shadow-preview-panel {
height: $grid-unit-60 * 3;
border: $gray-200 $border-width solid;
- border-radius: $radius-block-ui;
+ border-radius: $radius-medium;
overflow: auto;
background-image: repeating-linear-gradient(45deg, #f5f5f5 25%, #0000 0, #0000 75%, #f5f5f5 0, #f5f5f5), repeating-linear-gradient(45deg, #f5f5f5 25%, #0000 0, #0000 75%, #f5f5f5 0, #f5f5f5);
background-position: 0 0, 8px 8px;
@@ -128,7 +128,7 @@
.edit-site-global-styles__shadow-preview-block {
border: $gray-200 $border-width solid;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
background-color: $white;
width: 60%;
height: 60px;
diff --git a/packages/edit-site/src/components/global-styles/variations/style.scss b/packages/edit-site/src/components/global-styles/variations/style.scss
index 8d9b4238efa337..5f57c72f180b12 100644
--- a/packages/edit-site/src/components/global-styles/variations/style.scss
+++ b/packages/edit-site/src/components/global-styles/variations/style.scss
@@ -1,11 +1,10 @@
.edit-site-global-styles-variations_item {
box-sizing: border-box;
// To round the outline in Windows 10 high contrast mode.
- border-radius: $radius-block-ui;
cursor: pointer;
.edit-site-global-styles-variations_item-preview {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
outline: $border-width solid rgba($black, 0.1);
outline-offset: -$border-width;
overflow: hidden;
diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss
index 64fb1914af3dd3..b2d929a7943dbf 100644
--- a/packages/edit-site/src/components/layout/style.scss
+++ b/packages/edit-site/src/components/layout/style.scss
@@ -120,7 +120,7 @@
overflow: hidden;
.edit-site-layout:not(.is-full-canvas) & {
- border-radius: $radius-block-ui * 4;
+ border-radius: $radius-large;
}
}
}
@@ -200,7 +200,7 @@ html.canvas-mode-edit-transition::view-transition-group(toggle) {
right: 9px;
bottom: 9px;
left: 9px;
- border-radius: $radius-block-ui + $border-width + $border-width;
+ border-radius: $radius-medium;
box-shadow: none;
}
diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss
index a5aa1eb9ac796e..c62fe34daf7d67 100644
--- a/packages/edit-site/src/components/page-patterns/style.scss
+++ b/packages/edit-site/src/components/page-patterns/style.scss
@@ -126,8 +126,8 @@
.components-form-token-field__suggestions-list:not(:empty) {
position: absolute;
border: $border-width solid var(--wp-admin-theme-color);
- border-bottom-left-radius: $radius-block-ui;
- border-bottom-right-radius: $radius-block-ui;
+ border-bottom-left-radius: $radius-small;
+ border-bottom-right-radius: $radius-small;
box-shadow: 0 0 0.5px 0.5px var(--wp-admin-theme-color);
box-sizing: border-box;
z-index: 1;
diff --git a/packages/edit-site/src/components/page-templates/style.scss b/packages/edit-site/src/components/page-templates/style.scss
index 4e21ca2c25be28..6a753921f6f40a 100644
--- a/packages/edit-site/src/components/page-templates/style.scss
+++ b/packages/edit-site/src/components/page-templates/style.scss
@@ -36,7 +36,6 @@
}
.dataviews-view-table & {
- border-radius: $radius-block-ui;
position: relative;
width: 120px;
max-height: 160px;
@@ -49,7 +48,7 @@
width: 100%;
height: 100%;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
- border-radius: $radius-block-ui;
+ border-radius: $radius-medium;
}
}
}
diff --git a/packages/edit-site/src/components/post-list/style.scss b/packages/edit-site/src/components/post-list/style.scss
index a86783c163c83e..895a02b3106725 100644
--- a/packages/edit-site/src/components/post-list/style.scss
+++ b/packages/edit-site/src/components/post-list/style.scss
@@ -68,7 +68,7 @@
background: $gray-100;
color: $gray-700;
padding: 0 $grid-unit-05;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
font-size: 12px;
font-weight: 400;
flex-shrink: 0;
diff --git a/packages/edit-site/src/components/sidebar-dataviews/style.scss b/packages/edit-site/src/components/sidebar-dataviews/style.scss
index 9c5b8f48e944f2..14e6bf1d03fca8 100644
--- a/packages/edit-site/src/components/sidebar-dataviews/style.scss
+++ b/packages/edit-site/src/components/sidebar-dataviews/style.scss
@@ -8,7 +8,7 @@
}
.edit-site-sidebar-dataviews-dataview-item {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
padding-right: $grid-unit-10;
.edit-site-sidebar-dataviews-dataview-item__dropdown-menu {
diff --git a/packages/edit-site/src/components/sidebar-navigation-item/style.scss b/packages/edit-site/src/components/sidebar-navigation-item/style.scss
index 908056d52af48c..016027ef715a45 100644
--- a/packages/edit-site/src/components/sidebar-navigation-item/style.scss
+++ b/packages/edit-site/src/components/sidebar-navigation-item/style.scss
@@ -4,7 +4,6 @@
padding: $grid-unit-10 6px $grid-unit-10 $grid-unit-20;
border: none;
min-height: $grid-unit-50;
- border-radius: $radius-block-ui;
&:hover,
&:focus,
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss
index 43b789d669ba4d..f0260581a8988f 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss
+++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss
@@ -146,7 +146,6 @@
.components-input-control__input {
color: $gray-200 !important;
background: $gray-800 !important;
- border-radius: $radius-block-ui;
}
.components-input-control__backdrop {
border: 4px !important;
diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js
index 7ee8eed375f9a9..64503dcf7a6dbb 100644
--- a/packages/edit-site/src/components/style-book/index.js
+++ b/packages/edit-site/src/components/style-book/index.js
@@ -8,6 +8,7 @@ import clsx from 'clsx';
*/
import {
Disabled,
+ Composite,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
@@ -45,12 +46,7 @@ const {
} = unlock( blockEditorPrivateApis );
const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis );
-const {
- CompositeV2: Composite,
- CompositeItemV2: CompositeItem,
- useCompositeStoreV2: useCompositeStore,
- Tabs,
-} = unlock( componentsPrivateApis );
+const { Tabs } = unlock( componentsPrivateApis );
// The content area of the Style Book is rendered within an iframe so that global styles
// are applied to elements within the entire content area. To support elements that are
@@ -383,11 +379,9 @@ const StyleBookBody = ( {
const Examples = memo(
( { className, examples, category, label, isSelected, onSelect } ) => {
- const compositeStore = useCompositeStore( { orientation: 'vertical' } );
-
return (
{
return (
);
diff --git a/packages/edit-site/src/components/style-book/style.scss b/packages/edit-site/src/components/style-book/style.scss
index 3b2c6ab0867dbe..ab66ec288da310 100644
--- a/packages/edit-site/src/components/style-book/style.scss
+++ b/packages/edit-site/src/components/style-book/style.scss
@@ -3,13 +3,13 @@
// This is useful when the style book is used to fill a frame.
height: 100%;
&.is-button {
- border-radius: $radius-block-ui * 4;
+ border-radius: $radius-large;
}
}
.edit-site-style-book__iframe {
&.is-button {
- border-radius: $radius-block-ui * 4;
+ border-radius: $radius-large;
}
&.is-focused {
outline: calc(2 * var(--wp-admin-border-width-focus)) solid var(--wp-admin-theme-color);
diff --git a/packages/edit-site/src/components/welcome-guide/template.js b/packages/edit-site/src/components/welcome-guide/template.js
index 0e699e6b4b242a..e6568a23bb3a3c 100644
--- a/packages/edit-site/src/components/welcome-guide/template.js
+++ b/packages/edit-site/src/components/welcome-guide/template.js
@@ -10,29 +10,23 @@ import { store as editorStore } from '@wordpress/editor';
/**
* Internal dependencies
*/
-import { store as editSiteStore } from '../../store';
+import useEditedEntityRecord from '../use-edited-entity-record';
export default function WelcomeGuideTemplate() {
const { toggle } = useDispatch( preferencesStore );
- const isVisible = useSelect( ( select ) => {
- const isTemplateActive = !! select( preferencesStore ).get(
- 'core/edit-site',
- 'welcomeGuideTemplate'
- );
- const isEditorActive = !! select( preferencesStore ).get(
- 'core/edit-site',
- 'welcomeGuide'
- );
- const { isPage } = select( editSiteStore );
- const { getCurrentPostType } = select( editorStore );
- return (
- isTemplateActive &&
- ! isEditorActive &&
- isPage() &&
- getCurrentPostType() === 'wp_template'
- );
+ const { isLoaded, record } = useEditedEntityRecord();
+ const isPostTypeTemplate = isLoaded && record.type === 'wp_template';
+ const { isActive, hasPreviousEntity } = useSelect( ( select ) => {
+ const { getEditorSettings } = select( editorStore );
+ const { get } = select( preferencesStore );
+ return {
+ isActive: get( 'core/edit-site', 'welcomeGuideTemplate' ),
+ hasPreviousEntity:
+ !! getEditorSettings().onNavigateToPreviousEntityRecord,
+ };
}, [] );
+ const isVisible = isActive && isPostTypeTemplate && hasPreviousEntity;
if ( ! isVisible ) {
return null;
diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js
index 9face28c1bfe19..1aceecc4d8b1fc 100644
--- a/packages/edit-site/src/index.js
+++ b/packages/edit-site/src/index.js
@@ -79,8 +79,16 @@ export function initializeEditor( id, settings ) {
openPanels: [ 'post-status' ],
showBlockBreadcrumbs: true,
showListViewByDefault: false,
+ enableChoosePatternModal: true,
} );
+ if ( window.__experimentalMediaProcessing ) {
+ dispatch( preferencesStore ).setDefaults( 'core/media', {
+ requireApproval: true,
+ optimizeOnUpload: true,
+ } );
+ }
+
dispatch( editSiteStore ).updateSettings( settings );
// Keep the defaultTemplateTypes in the core/editor settings too,
diff --git a/packages/edit-site/src/posts.js b/packages/edit-site/src/posts.js
index 9e2582ac23328a..78d823b56c4c11 100644
--- a/packages/edit-site/src/posts.js
+++ b/packages/edit-site/src/posts.js
@@ -72,6 +72,7 @@ export function initializePostsDashboard( id, settings ) {
openPanels: [ 'post-status' ],
showBlockBreadcrumbs: true,
showListViewByDefault: false,
+ enableChoosePatternModal: true,
} );
dispatch( editSiteStore ).updateSettings( settings );
diff --git a/packages/edit-widgets/CHANGELOG.md b/packages/edit-widgets/CHANGELOG.md
index eb185d530887d3..f1cbcef0667d7c 100644
--- a/packages/edit-widgets/CHANGELOG.md
+++ b/packages/edit-widgets/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json
index ed48d07a1d1e5c..d24857f0f8a0ce 100644
--- a/packages/edit-widgets/package.json
+++ b/packages/edit-widgets/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/edit-widgets",
- "version": "6.6.1",
+ "version": "6.7.0",
"description": "Widgets Page module for WordPress..",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js
index 0aadec83d5d2f3..80dda479d59ed6 100644
--- a/packages/edit-widgets/src/components/header/index.js
+++ b/packages/edit-widgets/src/components/header/index.js
@@ -61,8 +61,8 @@ function Header() {
) }
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 05bb9beb3b1fcc..b380c2fd296d55 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 14.7.0 (2024-09-05)
+
## 14.6.0 (2024-08-21)
## 14.5.0 (2024-08-07)
diff --git a/packages/editor/README.md b/packages/editor/README.md
index ebd4af31e287d8..d18513b151beae 100644
--- a/packages/editor/README.md
+++ b/packages/editor/README.md
@@ -1471,6 +1471,10 @@ Undocumented declaration.
Displays the Post Trash Button and Confirm Dialog in the Editor.
+_Parameters_
+
+- _An_ `?{onActionPerformed: Object}`: object containing the onActionPerformed function.
+
_Returns_
- `JSX.Element|null`: The rendered PostTrash component.
diff --git a/packages/editor/package.json b/packages/editor/package.json
index 7e120895c782f3..a9d486b74e79cf 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/editor",
- "version": "14.6.0",
+ "version": "14.7.0",
"description": "Enhanced block editor for WordPress posts.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js
index 1ecd6ea50ba274..7618ba6c360236 100644
--- a/packages/editor/src/bindings/post-meta.js
+++ b/packages/editor/src/bindings/post-meta.js
@@ -22,7 +22,7 @@ export default {
for ( const [ attributeName, source ] of Object.entries( bindings ) ) {
// Use the key if the value is not set.
newValues[ attributeName ] =
- meta?.[ source.args.key ] || source.args.key;
+ meta?.[ source.args.key ] ?? source.args.key;
}
return newValues;
},
@@ -62,6 +62,12 @@ export default {
if ( fieldValue === undefined ) {
return false;
}
+ // Check that custom fields metabox is not enabled.
+ const areCustomFieldsEnabled =
+ select( editorStore ).getEditorSettings().enableCustomFields;
+ if ( areCustomFieldsEnabled ) {
+ return false;
+ }
// Check that the user has the capability to edit post meta.
const canUserEdit = select( coreDataStore ).canUser( 'update', {
diff --git a/packages/editor/src/components/document-outline/style.scss b/packages/editor/src/components/document-outline/style.scss
index efd2606b82d394..49ce0c9b2d1325 100644
--- a/packages/editor/src/components/document-outline/style.scss
+++ b/packages/editor/src/components/document-outline/style.scss
@@ -51,7 +51,7 @@
padding: 2px 5px 2px 1px;
color: $gray-900;
text-align: left;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
&:disabled {
cursor: default;
diff --git a/packages/editor/src/components/editor/index.js b/packages/editor/src/components/editor/index.js
index b094c3ceb44376..21becea43cd7ee 100644
--- a/packages/editor/src/components/editor/index.js
+++ b/packages/editor/src/components/editor/index.js
@@ -74,11 +74,11 @@ function Editor( {
{ extraContent }
+ { children }
- { children }
) }
>
diff --git a/packages/editor/src/components/global-styles-provider/index.js b/packages/editor/src/components/global-styles-provider/index.js
index 8426593d8f5f51..8268997d1f1def 100644
--- a/packages/editor/src/components/global-styles-provider/index.js
+++ b/packages/editor/src/components/global-styles-provider/index.js
@@ -146,13 +146,17 @@ function useGlobalStylesUserConfig() {
function useGlobalStylesBaseConfig() {
const baseConfig = useSelect( ( select ) => {
- const { __experimentalGetCurrentThemeBaseGlobalStyles, canUser } =
- select( coreStore );
-
- return (
- canUser( 'read', { kind: 'root', name: 'theme' } ) &&
- __experimentalGetCurrentThemeBaseGlobalStyles()
- );
+ const {
+ __experimentalGetCurrentThemeBaseGlobalStyles,
+ getCurrentTheme,
+ canUser,
+ } = select( coreStore );
+ const currentTheme = getCurrentTheme();
+
+ return currentTheme &&
+ canUser( 'read', 'global-styles/themes', currentTheme.stylesheet )
+ ? __experimentalGetCurrentThemeBaseGlobalStyles()
+ : undefined;
}, [] );
return [ !! baseConfig, baseConfig ];
diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js
index b8eda30c721860..2e045ad7329428 100644
--- a/packages/editor/src/components/header/index.js
+++ b/packages/editor/src/components/header/index.js
@@ -142,6 +142,9 @@ function Header( {
forceIsAutosaveable={ forceIsDirty }
/>
+ { ( isWideViewport || ! showIconLabels ) && (
+
+ ) }
{ ! customSaveButton && (
) }
{ customSaveButton }
- { ( isWideViewport || ! showIconLabels ) && (
-
- ) }
diff --git a/packages/editor/src/components/header/style.scss b/packages/editor/src/components/header/style.scss
index 99c9cc70e166e4..8712121fff3ea6 100644
--- a/packages/editor/src/components/header/style.scss
+++ b/packages/editor/src/components/header/style.scss
@@ -243,7 +243,7 @@
width: 100%;
@include break-medium {
- border-bottom: 1px solid #e0e0e0;
+ box-shadow: 0 $border-width 0 0 rgba($color: #000, $alpha: 0.133); // 0.133 = $gray-200 but with alpha.
position: absolute;
}
diff --git a/packages/editor/src/components/inserter-sidebar/index.js b/packages/editor/src/components/inserter-sidebar/index.js
index 3e54fec6cc06b8..b98770b7afe8fa 100644
--- a/packages/editor/src/components/inserter-sidebar/index.js
+++ b/packages/editor/src/components/inserter-sidebar/index.js
@@ -18,13 +18,10 @@ import { store as interfaceStore } from '@wordpress/interface';
import { unlock } from '../../lock-unlock';
import { store as editorStore } from '../../store';
-const { PrivateInserterLibrary, sectionRootClientIdKey } = unlock(
- blockEditorPrivateApis
-);
+const { PrivateInserterLibrary } = unlock( blockEditorPrivateApis );
export default function InserterSidebar() {
const {
- blockInsertionPoint,
blockSectionRootClientId,
inserterSidebarToggleRef,
insertionPoint,
@@ -37,17 +34,16 @@ export default function InserterSidebar() {
isPublishSidebarOpened,
} = unlock( select( editorStore ) );
const {
- getBlockInsertionPoint,
getBlockRootClientId,
__unstableGetEditorMode,
- getSettings,
- } = select( blockEditorStore );
+ getSectionRootClientId,
+ } = unlock( select( blockEditorStore ) );
const { get } = select( preferencesStore );
const { getActiveComplementaryArea } = select( interfaceStore );
const getBlockSectionRootClientId = () => {
if ( __unstableGetEditorMode() === 'zoom-out' ) {
- const { [ sectionRootClientIdKey ]: sectionRootClientId } =
- getSettings();
+ const sectionRootClientId = getSectionRootClientId();
+
if ( sectionRootClientId ) {
return sectionRootClientId;
}
@@ -55,7 +51,6 @@ export default function InserterSidebar() {
return getBlockRootClientId();
};
return {
- blockInsertionPoint: getBlockInsertionPoint(),
inserterSidebarToggleRef: getInserterSidebarToggleRef(),
insertionPoint: getInsertionPoint(),
showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ),
@@ -94,9 +89,9 @@ export default function InserterSidebar() {
showInserterHelpPanel
shouldFocusBlock={ isMobileViewport }
rootClientId={
- blockSectionRootClientId ?? blockInsertionPoint.rootClientId
+ blockSectionRootClientId ?? insertionPoint.rootClientId
}
- __experimentalInsertionIndex={ blockInsertionPoint.index }
+ __experimentalInsertionIndex={ insertionPoint.insertionIndex }
onSelect={ insertionPoint.onSelect }
__experimentalInitialTab={ insertionPoint.tab }
__experimentalInitialCategory={ insertionPoint.category }
diff --git a/packages/editor/src/components/post-card-panel/style.scss b/packages/editor/src/components/post-card-panel/style.scss
index 3547b0ab104936..73b638673f3e91 100644
--- a/packages/editor/src/components/post-card-panel/style.scss
+++ b/packages/editor/src/components/post-card-panel/style.scss
@@ -46,7 +46,7 @@
background: $gray-100;
color: $gray-700;
padding: 0 $grid-unit-05;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
font-size: 12px;
font-weight: 400;
flex-shrink: 0;
diff --git a/packages/editor/src/components/post-featured-image/style.scss b/packages/editor/src/components/post-featured-image/style.scss
index d3a4fbbcaef689..3a537471b42f29 100644
--- a/packages/editor/src/components/post-featured-image/style.scss
+++ b/packages/editor/src/components/post-featured-image/style.scss
@@ -22,7 +22,7 @@
}
.components-drop-zone__content {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
}
// Align text and icons horizontally to avoid clipping when the featured image is not set.
@@ -65,7 +65,6 @@
}
.editor-post-featured-image__toggle {
- border-radius: $radius-block-ui;
height: 100%;
line-height: 20px;
padding: $grid-unit-10 0;
diff --git a/packages/editor/src/components/post-locked-modal/style.scss b/packages/editor/src/components/post-locked-modal/style.scss
index 03e86642493df3..7f680022344664 100644
--- a/packages/editor/src/components/post-locked-modal/style.scss
+++ b/packages/editor/src/components/post-locked-modal/style.scss
@@ -3,7 +3,7 @@
}
.editor-post-locked-modal__avatar {
- border-radius: $radius-block-ui;
+ border-radius: $radius-round;
margin-top: $grid-unit-20;
min-width: initial !important;
}
diff --git a/packages/editor/src/components/post-publish-panel/maybe-upload-media.js b/packages/editor/src/components/post-publish-panel/maybe-upload-media.js
index b090e25cd4dbff..6b252b9f2b399e 100644
--- a/packages/editor/src/components/post-publish-panel/maybe-upload-media.js
+++ b/packages/editor/src/components/post-publish-panel/maybe-upload-media.js
@@ -14,6 +14,11 @@ import { store as blockEditorStore } from '@wordpress/block-editor';
import { useState } from '@wordpress/element';
import { isBlobURL } from '@wordpress/blob';
+/**
+ * Internal dependencies
+ */
+import { fetchMedia } from './media-util';
+
function flattenBlocks( blocks ) {
const result = [];
@@ -25,7 +30,53 @@ function flattenBlocks( blocks ) {
return result;
}
-function Image( block ) {
+/**
+ * Determine whether a block has external media.
+ *
+ * Different blocks use different attribute names (and potentially
+ * different logic as well) in determining whether the media is
+ * present, and whether it's external.
+ *
+ * @param {{name: string, attributes: Object}} block The block.
+ * @return {boolean?} Whether the block has external media
+ */
+function hasExternalMedia( block ) {
+ if ( block.name === 'core/image' || block.name === 'core/cover' ) {
+ return block.attributes.url && ! block.attributes.id;
+ }
+
+ if ( block.name === 'core/media-text' ) {
+ return block.attributes.mediaUrl && ! block.attributes.mediaId;
+ }
+
+ return undefined;
+}
+
+/**
+ * Retrieve media info from a block.
+ *
+ * Different blocks use different attribute names, so we need this
+ * function to normalize things into a consistent naming scheme.
+ *
+ * @param {{name: string, attributes: Object}} block The block.
+ * @return {{url: ?string, alt: ?string, id: ?number}} The media info for the block.
+ */
+function getMediaInfo( block ) {
+ if ( block.name === 'core/image' || block.name === 'core/cover' ) {
+ const { url, alt, id } = block.attributes;
+ return { url, alt, id };
+ }
+
+ if ( block.name === 'core/media-text' ) {
+ const { mediaUrl: url, mediaAlt: alt, mediaId: id } = block.attributes;
+ return { url, alt, id };
+ }
+
+ return {};
+}
+
+// Image component to represent a single image in the upload dialog.
+function Image( { clientId, alt, url } ) {
const { selectBlock } = useDispatch( blockEditorStore );
return (
{
- selectBlock( block.clientId );
+ selectBlock( clientId );
} }
onKeyDown={ ( event ) => {
if ( event.key === 'Enter' || event.key === ' ' ) {
- selectBlock( block.clientId );
+ selectBlock( clientId );
event.preventDefault();
}
} }
- key={ block.clientId }
- alt={ block.attributes.alt }
- src={ block.attributes.url }
+ key={ clientId }
+ alt={ alt }
+ src={ url }
animate={ { opacity: 1 } }
exit={ { opacity: 0, scale: 0 } }
style={ {
@@ -58,7 +109,7 @@ function Image( block ) {
);
}
-export default function PostFormatPanel() {
+export default function MaybeUploadMediaPanel() {
const [ isUploading, setIsUploading ] = useState( false );
const [ isAnimating, setIsAnimating ] = useState( false );
const [ hadUploadError, setHadUploadError ] = useState( false );
@@ -69,15 +120,14 @@ export default function PostFormatPanel() {
} ),
[]
);
- const externalImages = flattenBlocks( editorBlocks ).filter(
- ( block ) =>
- block.name === 'core/image' &&
- block.attributes.url &&
- ! block.attributes.id
+
+ // Get a list of blocks with external media.
+ const blocksWithExternalMedia = flattenBlocks( editorBlocks ).filter(
+ ( block ) => hasExternalMedia( block )
);
const { updateBlockAttributes } = useDispatch( blockEditorStore );
- if ( ! mediaUpload || ! externalImages.length ) {
+ if ( ! mediaUpload || ! blocksWithExternalMedia.length ) {
return null;
}
@@ -88,43 +138,86 @@ export default function PostFormatPanel() {
,
];
+ /**
+ * Update an individual block to point to newly-added library media.
+ *
+ * Different blocks use different attribute names, so we need this
+ * function to ensure we modify the correct attributes for each type.
+ *
+ * @param {{name: string, attributes: Object}} block The block.
+ * @param {{id: number, url: string}} media Media library file info.
+ */
+ function updateBlockWithUploadedMedia( block, media ) {
+ if ( block.name === 'core/image' || block.name === 'core/cover' ) {
+ updateBlockAttributes( block.clientId, {
+ id: media.id,
+ url: media.url,
+ } );
+ }
+
+ if ( block.name === 'core/media-text' ) {
+ updateBlockAttributes( block.clientId, {
+ mediaId: media.id,
+ mediaUrl: media.url,
+ } );
+ }
+ }
+
+ // Handle fetching and uploading all external media in the post.
function uploadImages() {
setIsUploading( true );
setHadUploadError( false );
- Promise.all(
- externalImages.map( ( image ) =>
- window
- .fetch(
- image.attributes.url.includes( '?' )
- ? image.attributes.url
- : image.attributes.url + '?'
- )
- .then( ( response ) => response.blob() )
- .then( ( blob ) =>
- new Promise( ( resolve, reject ) => {
- mediaUpload( {
- filesList: [ blob ],
- onFileChange: ( [ media ] ) => {
- if ( isBlobURL( media.url ) ) {
- return;
- }
-
- updateBlockAttributes( image.clientId, {
- id: media.id,
- url: media.url,
- } );
- resolve();
- },
- onError() {
- reject();
- },
- } );
- } ).then( () => setIsAnimating( true ) )
- )
- .catch( () => {
- setHadUploadError( true );
- } )
+
+ // Multiple blocks can be using the same URL, so we
+ // should ensure we only fetch and upload each of them once.
+ const mediaUrls = new Set(
+ blocksWithExternalMedia.map( ( block ) => {
+ const { url } = getMediaInfo( block );
+ return url;
+ } )
+ );
+
+ // Create an upload promise for each URL, that we can wait for in all
+ // blocks that make use of that media.
+ const uploadPromises = Object.fromEntries(
+ Object.entries( fetchMedia( [ ...mediaUrls ] ) ).map(
+ ( [ url, filePromise ] ) => {
+ const uploadPromise = filePromise.then(
+ ( blob ) =>
+ new Promise( ( resolve, reject ) => {
+ mediaUpload( {
+ filesList: [ blob ],
+ onFileChange: ( [ media ] ) => {
+ if ( isBlobURL( media.url ) ) {
+ return;
+ }
+
+ resolve( media );
+ },
+ onError() {
+ reject();
+ },
+ } );
+ } )
+ );
+
+ return [ url, uploadPromise ];
+ }
)
+ );
+
+ // Wait for all blocks to be updated with library media.
+ Promise.allSettled(
+ blocksWithExternalMedia.map( ( block ) => {
+ const { url } = getMediaInfo( block );
+
+ return uploadPromises[ url ]
+ .then( ( media ) =>
+ updateBlockWithUploadedMedia( block, media )
+ )
+ .then( () => setIsAnimating( true ) )
+ .catch( () => setHadUploadError( true ) );
+ } )
).finally( () => {
setIsUploading( false );
} );
@@ -147,8 +240,16 @@ export default function PostFormatPanel() {
setIsAnimating( false ) }
>
- { externalImages.map( ( image ) => {
- return ;
+ { blocksWithExternalMedia.map( ( block ) => {
+ const { url, alt } = getMediaInfo( block );
+ return (
+
+ );
} ) }
{ isUploading || isAnimating ? (
diff --git a/packages/editor/src/components/post-publish-panel/media-util.js b/packages/editor/src/components/post-publish-panel/media-util.js
new file mode 100644
index 00000000000000..995802b0eebb44
--- /dev/null
+++ b/packages/editor/src/components/post-publish-panel/media-util.js
@@ -0,0 +1,87 @@
+/**
+ * External dependencies
+ */
+import { v4 as uuid } from 'uuid';
+
+/**
+ * WordPress dependencies
+ */
+import { getFilename } from '@wordpress/url';
+
+/**
+ * Generate a list of unique basenames given a list of URLs.
+ *
+ * We want all basenames to be unique, since sometimes the extension
+ * doesn't reflect the mime type, and may end up getting changed by
+ * the server, on upload.
+ *
+ * @param {string[]} urls The list of URLs
+ * @return {Record< string, string >} A URL => basename record.
+ */
+export function generateUniqueBasenames( urls ) {
+ const basenames = new Set();
+
+ return Object.fromEntries(
+ urls.map( ( url ) => {
+ // We prefer to match the remote filename, if possible.
+ const filename = getFilename( url );
+ let basename = '';
+
+ if ( filename ) {
+ const parts = filename.split( '.' );
+ if ( parts.length > 1 ) {
+ // Assume the last part is the extension.
+ parts.pop();
+ }
+ basename = parts.join( '.' );
+ }
+
+ if ( ! basename ) {
+ // It looks like we don't have a basename, so let's use a UUID.
+ basename = uuid();
+ }
+
+ if ( basenames.has( basename ) ) {
+ // Append a UUID to deduplicate the basename.
+ // The server will try to deduplicate on its own if we don't do this,
+ // but it may run into a race condition
+ // (see https://github.com/WordPress/gutenberg/issues/64899).
+ // Deduplicating the filenames before uploading is safer.
+ basename = `${ basename }-${ uuid() }`;
+ }
+
+ basenames.add( basename );
+
+ return [ url, basename ];
+ } )
+ );
+}
+
+/**
+ * Fetch a list of URLs, turning those into promises for files with
+ * unique filenames.
+ *
+ * @param {string[]} urls The list of URLs
+ * @return {Record< string, Promise< File > >} A URL => File promise record.
+ */
+export function fetchMedia( urls ) {
+ return Object.fromEntries(
+ Object.entries( generateUniqueBasenames( urls ) ).map(
+ ( [ url, basename ] ) => {
+ const filePromise = window
+ .fetch( url.includes( '?' ) ? url : url + '?' )
+ .then( ( response ) => response.blob() )
+ .then( ( blob ) => {
+ // The server will reject the upload if it doesn't have an extension,
+ // even though it'll rewrite the file name to match the mime type.
+ // Here we provide it with a safe extension to get it past that check.
+ return new File( [ blob ], `${ basename }.png`, {
+ type: blob.type,
+ } );
+ } );
+
+ return [ url, filePromise ];
+ }
+ )
+ );
+}
diff --git a/packages/editor/src/components/post-publish-panel/style.scss b/packages/editor/src/components/post-publish-panel/style.scss
index bcd7c798c7e9fb..9892cf5430f9a2 100644
--- a/packages/editor/src/components/post-publish-panel/style.scss
+++ b/packages/editor/src/components/post-publish-panel/style.scss
@@ -37,7 +37,7 @@
.components-site-icon {
border: none;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
margin-right: $grid-unit-15;
flex-shrink: 0;
diff --git a/packages/editor/src/components/post-publish-panel/test/media-util.js b/packages/editor/src/components/post-publish-panel/test/media-util.js
new file mode 100644
index 00000000000000..b91583cc3f17b3
--- /dev/null
+++ b/packages/editor/src/components/post-publish-panel/test/media-util.js
@@ -0,0 +1,118 @@
+/**
+ * Internal dependencies
+ */
+import { generateUniqueBasenames } from '../media-util';
+
+describe( 'generateUniqueBasenames', () => {
+ it( 'should prefer the original basenames', () => {
+ const urls = [
+ 'https://example.com/images/image1.jpg',
+ 'https://example.com/images/image2.jpg',
+ 'https://example.com/images/image3.jpg',
+ 'https://example.com/images/image4.jpg',
+ ];
+
+ expect( generateUniqueBasenames( urls ) ).toEqual( {
+ 'https://example.com/images/image1.jpg': 'image1',
+ 'https://example.com/images/image2.jpg': 'image2',
+ 'https://example.com/images/image3.jpg': 'image3',
+ 'https://example.com/images/image4.jpg': 'image4',
+ } );
+ } );
+
+ it( 'should handle filenames with no extensions', () => {
+ const urls = [
+ 'https://example.com/images/image1',
+ 'https://example.com/images/image2',
+ 'https://example.com/images/image3',
+ 'https://example.com/images/image4',
+ ];
+
+ expect( generateUniqueBasenames( urls ) ).toEqual( {
+ 'https://example.com/images/image1': 'image1',
+ 'https://example.com/images/image2': 'image2',
+ 'https://example.com/images/image3': 'image3',
+ 'https://example.com/images/image4': 'image4',
+ } );
+ } );
+
+ it( 'should handle query parameters correctly', () => {
+ const urls = [
+ 'https://example.com/images/image1.jpg?a=notafile.npg',
+ 'https://example.com/images/image2.jpg?a=notafile.npg',
+ 'https://example.com/images/image3.jpg?a=notafile.npg',
+ 'https://example.com/images/image4.jpg?a=notafile.npg',
+ ];
+
+ expect( generateUniqueBasenames( urls ) ).toEqual( {
+ 'https://example.com/images/image1.jpg?a=notafile.npg': 'image1',
+ 'https://example.com/images/image2.jpg?a=notafile.npg': 'image2',
+ 'https://example.com/images/image3.jpg?a=notafile.npg': 'image3',
+ 'https://example.com/images/image4.jpg?a=notafile.npg': 'image4',
+ } );
+ } );
+
+ it( 'should deduplicate identical filenames', () => {
+ const urls = [
+ 'https://example.com/image1/image.jpg',
+ 'https://example.com/image2/image.jpg',
+ 'https://example.com/image3/image.jpg',
+ 'https://example.com/image4/image.jpg',
+ ];
+
+ const results = generateUniqueBasenames( urls );
+ const resultLength = Object.entries( results ).length;
+ expect( resultLength ).toBe( urls.length );
+
+ const basenames = new Set( Object.values( results ) );
+ expect( basenames.size ).toBe( resultLength );
+ } );
+
+ it( 'should deduplicate identical basenames', () => {
+ const urls = [
+ 'https://example.com/images/image.jpg',
+ 'https://example.com/images/image.png',
+ 'https://example.com/images/image.webp',
+ 'https://example.com/images/image.avif',
+ ];
+
+ const results = generateUniqueBasenames( urls );
+ const resultLength = Object.entries( results ).length;
+ expect( resultLength ).toBe( urls.length );
+
+ const basenames = new Set( Object.values( results ) );
+ expect( basenames.size ).toBe( resultLength );
+ } );
+
+ it( 'should deduplicate filenames without extensions', () => {
+ const urls = [
+ 'https://example.com/image1/image',
+ 'https://example.com/image2/image',
+ 'https://example.com/image3/image',
+ 'https://example.com/image4/image',
+ ];
+
+ const results = generateUniqueBasenames( urls );
+ const resultLength = Object.entries( results ).length;
+ expect( resultLength ).toBe( urls.length );
+
+ const basenames = new Set( Object.values( results ) );
+ expect( basenames.size ).toBe( resultLength );
+ } );
+
+ it( 'should deduplicate paths with no filename', () => {
+ const urls = [
+ 'https://example.com/image1/dir/',
+ 'https://example.com/image2/dir/',
+ 'https://example.com/image3/dir/',
+ 'https://example.com/image4/dir/',
+ ];
+
+ const results = generateUniqueBasenames( urls );
+ const resultLength = Object.entries( results ).length;
+ expect( resultLength ).toBe( urls.length );
+
+ const basenames = new Set( Object.values( results ) );
+ expect( basenames.size ).toBe( resultLength );
+ } );
+} );
diff --git a/packages/editor/src/components/post-template/block-theme.js b/packages/editor/src/components/post-template/block-theme.js
index 7fcc5ead14f5b8..62de10a2c715ff 100644
--- a/packages/editor/src/components/post-template/block-theme.js
+++ b/packages/editor/src/components/post-template/block-theme.js
@@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n';
import { useEntityRecord, store as coreStore } from '@wordpress/core-data';
import { check } from '@wordpress/icons';
import { store as noticesStore } from '@wordpress/notices';
+import { store as preferencesStore } from '@wordpress/preferences';
/**
* Internal dependencies
@@ -43,6 +44,8 @@ export default function BlockThemeControl( { id } ) {
};
}, [] );
+ const { get: getPreference } = useSelect( preferencesStore );
+
const { editedRecord: template, hasResolved } = useEntityRecord(
'postType',
'wp_template',
@@ -75,6 +78,17 @@ export default function BlockThemeControl( { id } ) {
},
]
: undefined;
+
+ const mayShowTemplateEditNotice = () => {
+ if ( ! getPreference( 'core/edit-site', 'welcomeGuideTemplate' ) ) {
+ createSuccessNotice(
+ __(
+ 'Editing template. Changes made here affect all posts and pages that use the template.'
+ ),
+ { type: 'snackbar', actions: notificationAction }
+ );
+ }
+ };
return (
{ __( 'Edit template' ) }
diff --git a/packages/editor/src/components/post-trash/check.js b/packages/editor/src/components/post-trash/check.js
index 8f51df175c898c..7edc7c0f18c273 100644
--- a/packages/editor/src/components/post-trash/check.js
+++ b/packages/editor/src/components/post-trash/check.js
@@ -8,6 +8,7 @@ import { store as coreStore } from '@wordpress/core-data';
* Internal dependencies
*/
import { store as editorStore } from '../../store';
+import { GLOBAL_POST_TYPES } from '../../store/constants';
/**
* Wrapper component that renders its children only if the post can trashed.
@@ -34,10 +35,12 @@ export default function PostTrashCheck( { children } ) {
: false;
return {
- canTrashPost: ( ! isNew || postId ) && canUserDelete,
+ canTrashPost:
+ ( ! isNew || postId ) &&
+ canUserDelete &&
+ ! GLOBAL_POST_TYPES.includes( postType ),
};
}, [] );
-
if ( ! canTrashPost ) {
return null;
}
diff --git a/packages/editor/src/components/post-trash/index.js b/packages/editor/src/components/post-trash/index.js
index c29dfd66a2d8ca..743512e9efd7d4 100644
--- a/packages/editor/src/components/post-trash/index.js
+++ b/packages/editor/src/components/post-trash/index.js
@@ -1,31 +1,35 @@
/**
* WordPress dependencies
*/
-import { __ } from '@wordpress/i18n';
+import { __, sprintf } from '@wordpress/i18n';
import {
Button,
__experimentalConfirmDialog as ConfirmDialog,
} from '@wordpress/components';
-import { useSelect, useDispatch } from '@wordpress/data';
+import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import { store as editorStore } from '../../store';
+import PostTrashCheck from './check';
/**
* Displays the Post Trash Button and Confirm Dialog in the Editor.
*
+ * @param {?{onActionPerformed: Object}} An object containing the onActionPerformed function.
* @return {JSX.Element|null} The rendered PostTrash component.
*/
-export default function PostTrash() {
- const { isNew, isDeleting, postId } = useSelect( ( select ) => {
+export default function PostTrash( { onActionPerformed } ) {
+ const registry = useRegistry();
+ const { isNew, isDeleting, postId, title } = useSelect( ( select ) => {
const store = select( editorStore );
return {
isNew: store.isEditedPostNew(),
isDeleting: store.isDeletingPost(),
postId: store.getCurrentPostId(),
+ title: store.getCurrentPostAttribute( 'title' ),
};
}, [] );
const { trashPost } = useDispatch( editorStore );
@@ -35,13 +39,18 @@ export default function PostTrash() {
return null;
}
- const handleConfirm = () => {
+ const handleConfirm = async () => {
setShowConfirmDialog( false );
- trashPost();
+ await trashPost();
+ const item = await registry
+ .resolveSelect( editorStore )
+ .getCurrentPost();
+ // After the post is trashed, we want to trigger the onActionPerformed callback, so the user is redirect
+ // to the post view depending on if the user is on post editor or site editor.
+ onActionPerformed?.( 'move-to-trash', [ item ] );
};
-
return (
- <>
+
setShowConfirmDialog( false ) }
confirmButtonText={ __( 'Move to trash' ) }
- size="medium"
+ size="small"
>
- { __(
- 'Are you sure you want to move this post to the trash?'
+ { sprintf(
+ // translators: %s: The item's title.
+ __( 'Are you sure you want to move "%s" to the trash?' ),
+ title
) }
- >
+
);
}
diff --git a/packages/editor/src/components/preferences-modal/index.js b/packages/editor/src/components/preferences-modal/index.js
index f3378d76d994b2..a8cfd8245522cd 100644
--- a/packages/editor/src/components/preferences-modal/index.js
+++ b/packages/editor/src/components/preferences-modal/index.js
@@ -26,6 +26,7 @@ import PageAttributesCheck from '../page-attributes/check';
import PostTypeSupportCheck from '../post-type-support-check';
import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';
+import { useStartPatterns } from '../start-page-options';
const {
PreferencesModal,
@@ -57,214 +58,267 @@ export default function EditorPreferencesModal( { extraSections = {} } ) {
const { setIsListViewOpened, setIsInserterOpened } =
useDispatch( editorStore );
const { set: setPreference } = useDispatch( preferencesStore );
+ const hasStarterPatterns = !! useStartPatterns().length;
const sections = useMemo(
- () => [
- {
- name: 'general',
- tabLabel: __( 'General' ),
- content: (
- <>
-
-
- { showBlockBreadcrumbsOption && (
+ () =>
+ [
+ {
+ name: 'general',
+ tabLabel: __( 'General' ),
+ content: (
+ <>
+
- ) }
-
-
-
-
- (
-
) }
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
- { isLargeViewport && (
+ { hasStarterPatterns && (
+
+ ) }
+
-
+ (
+
) }
- label={ __( 'Enable pre-publish checks' ) }
/>
+
+
+
+
+
+
+
+
+
+
+
+
- ) }
- { extraSections?.general }
- >
- ),
- },
- {
- name: 'appearance',
- tabLabel: __( 'Appearance' ),
- content: (
-
-
- setPreference(
- 'core',
- 'distractionFree',
- false
- )
- }
- help={ __(
- 'Access all block and document tools in a single place.'
- ) }
- label={ __( 'Top toolbar' ) }
- />
- {
- setPreference( 'core', 'fixedToolbar', true );
- setIsInserterOpened( false );
- setIsListViewOpened( false );
- } }
- help={ __(
- 'Reduce visual distractions by hiding the toolbar and other elements to focus on writing.'
- ) }
- label={ __( 'Distraction free' ) }
- />
-
+
+
) }
- label={ __( 'Spotlight mode' ) }
- />
- { extraSections?.appearance }
-
- ),
- },
- {
- name: 'accessibility',
- tabLabel: __( 'Accessibility' ),
- content: (
- <>
+ { extraSections?.general }
+ >
+ ),
+ },
+ {
+ name: 'appearance',
+ tabLabel: __( 'Appearance' ),
+ content: (
+ setPreference(
+ 'core',
+ 'distractionFree',
+ false
+ )
+ }
help={ __(
- 'Keeps the text cursor within the block boundaries, aiding users with screen readers by preventing unintentional cursor movement outside the block.'
- ) }
- label={ __(
- 'Contain text cursor inside block'
+ 'Access all block and document tools in a single place.'
) }
+ label={ __( 'Top toolbar' ) }
/>
-
-
{
+ setPreference(
+ 'core',
+ 'fixedToolbar',
+ true
+ );
+ setIsInserterOpened( false );
+ setIsListViewOpened( false );
+ } }
help={ __(
- 'Show text instead of icons on buttons across the interface.'
+ 'Reduce visual distractions by hiding the toolbar and other elements to focus on writing.'
) }
+ label={ __( 'Distraction free' ) }
/>
-
- >
- ),
- },
- {
- name: 'blocks',
- tabLabel: __( 'Blocks' ),
- content: (
- <>
-
+ { extraSections?.appearance }
-
-
-
- >
- ),
- },
- ],
+ ),
+ },
+ {
+ name: 'accessibility',
+ tabLabel: __( 'Accessibility' ),
+ content: (
+ <>
+
+
+
+
+
+
+ >
+ ),
+ },
+ {
+ name: 'blocks',
+ tabLabel: __( 'Blocks' ),
+ content: (
+ <>
+
+
+
+
+
+
+ >
+ ),
+ },
+ window.__experimentalMediaProcessing && {
+ name: 'media',
+ tabLabel: __( 'Media' ),
+ content: (
+ <>
+
+
+
+
+ >
+ ),
+ },
+ ].filter( Boolean ),
[
showBlockBreadcrumbsOption,
extraSections,
@@ -272,6 +326,7 @@ export default function EditorPreferencesModal( { extraSections = {} } ) {
setIsListViewOpened,
setPreference,
isLargeViewport,
+ hasStarterPatterns,
]
);
diff --git a/packages/editor/src/components/preview-dropdown/index.js b/packages/editor/src/components/preview-dropdown/index.js
index 8b51bb79bc8873..5acaa351c9bb7e 100644
--- a/packages/editor/src/components/preview-dropdown/index.js
+++ b/packages/editor/src/components/preview-dropdown/index.js
@@ -112,22 +112,24 @@ export default function PreviewDropdown( { forceIsAutosaveable, disabled } ) {
label: __( 'Desktop' ),
icon: desktop,
},
- {
+ ];
+ if ( window.__experimentalEnableZoomOutExperiment ) {
+ choices.push( {
value: 'ZoomOut',
label: __( 'Desktop (50%)' ),
icon: desktop,
- },
- {
- value: 'Tablet',
- label: __( 'Tablet' ),
- icon: tablet,
- },
- {
- value: 'Mobile',
- label: __( 'Mobile' ),
- icon: mobile,
- },
- ];
+ } );
+ }
+ choices.push( {
+ value: 'Tablet',
+ label: __( 'Tablet' ),
+ icon: tablet,
+ } );
+ choices.push( {
+ value: 'Mobile',
+ label: __( 'Mobile' ),
+ icon: mobile,
+ } );
const previewValue = editorMode === 'zoom-out' ? 'ZoomOut' : deviceType;
diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js
index aaf25621d3324b..11b1478d58434a 100644
--- a/packages/editor/src/components/provider/index.js
+++ b/packages/editor/src/components/provider/index.js
@@ -4,7 +4,11 @@
import { useEffect, useLayoutEffect, useMemo } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
-import { EntityProvider, useEntityBlockEditor } from '@wordpress/core-data';
+import {
+ EntityProvider,
+ useEntityBlockEditor,
+ store as coreStore,
+} from '@wordpress/core-data';
import {
BlockEditorProvider,
BlockContextProvider,
@@ -48,7 +52,6 @@ const noop = () => {};
*/
const NON_CONTEXTUAL_POST_TYPES = [
'wp_block',
- 'wp_template',
'wp_navigation',
'wp_template_part',
];
@@ -161,31 +164,59 @@ export const ExperimentalEditorProvider = withRegistryProvider(
BlockEditorProviderComponent = ExperimentalBlockEditorProvider,
__unstableTemplate: template,
} ) => {
- const { editorSettings, selection, isReady, mode } = useSelect(
- ( select ) => {
+ const { editorSettings, selection, isReady, mode, postTypes } =
+ useSelect( ( select ) => {
const {
getEditorSettings,
getEditorSelection,
getRenderingMode,
__unstableIsEditorReady,
} = select( editorStore );
+ const { getPostTypes } = select( coreStore );
+
return {
editorSettings: getEditorSettings(),
isReady: __unstableIsEditorReady(),
mode: getRenderingMode(),
selection: getEditorSelection(),
+ postTypes: getPostTypes( { per_page: -1 } ),
};
- },
- []
- );
+ }, [] );
const shouldRenderTemplate = !! template && mode !== 'post-only';
const rootLevelPost = shouldRenderTemplate ? template : post;
const defaultBlockContext = useMemo( () => {
- const postContext =
+ const postContext = {};
+ // If it is a template, try to inherit the post type from the slug.
+ if ( post.type === 'wp_template' ) {
+ if ( ! post.is_custom ) {
+ const [ kind ] = post.slug.split( '-' );
+ switch ( kind ) {
+ case 'page':
+ postContext.postType = 'page';
+ break;
+ case 'single':
+ // Infer the post type from the slug.
+ const postTypesSlugs =
+ postTypes?.map( ( entity ) => entity.slug ) ||
+ [];
+ const match = post.slug.match(
+ `^single-(${ postTypesSlugs.join(
+ '|'
+ ) })(?:-.+)?$`
+ );
+ if ( match ) {
+ postContext.postType = match[ 1 ];
+ }
+ break;
+ }
+ }
+ } else if (
! NON_CONTEXTUAL_POST_TYPES.includes( rootLevelPost.type ) ||
shouldRenderTemplate
- ? { postId: post.id, postType: post.type }
- : {};
+ ) {
+ postContext.postId = post.id;
+ postContext.postType = post.type;
+ }
return {
...postContext,
@@ -200,6 +231,7 @@ export const ExperimentalEditorProvider = withRegistryProvider(
post.type,
rootLevelPost.type,
rootLevelPost.slug,
+ postTypes,
] );
const { id, type } = rootLevelPost;
const blockEditorSettings = useBlockEditorSettings(
diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js
index 69e544be99d0ee..12a67e44262b21 100644
--- a/packages/editor/src/components/provider/use-block-editor-settings.js
+++ b/packages/editor/src/components/provider/use-block-editor-settings.js
@@ -26,15 +26,20 @@ import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import { useGlobalStylesContext } from '../global-styles-provider';
-const EMPTY_BLOCKS_LIST = [];
const EMPTY_OBJECT = {};
function __experimentalReusableBlocksSelect( select ) {
- return (
- select( coreStore ).getEntityRecords( 'postType', 'wp_block', {
- per_page: -1,
- } ) ?? EMPTY_BLOCKS_LIST
- );
+ const { getEntityRecords, hasFinishedResolution } = select( coreStore );
+ const reusableBlocks = getEntityRecords( 'postType', 'wp_block', {
+ per_page: -1,
+ } );
+ return hasFinishedResolution( 'getEntityRecords', [
+ 'postType',
+ 'wp_block',
+ { per_page: -1 },
+ ] )
+ ? reusableBlocks
+ : undefined;
}
const BLOCK_EDITOR_SETTINGS = [
@@ -47,6 +52,7 @@ const BLOCK_EDITOR_SETTINGS = [
'allowedMimeTypes',
'bodyPlaceholder',
'canLockBlocks',
+ 'canUpdateBlockBindings',
'capabilities',
'clearBlockSelection',
'codeEditingEnabled',
diff --git a/packages/editor/src/components/resizable-editor/style.scss b/packages/editor/src/components/resizable-editor/style.scss
index 91ff1144c4179b..364fdf39ad3155 100644
--- a/packages/editor/src/components/resizable-editor/style.scss
+++ b/packages/editor/src/components/resizable-editor/style.scss
@@ -16,7 +16,7 @@
cursor: ew-resize;
outline: none;
background: none;
- border-radius: $radius-block-ui;
+ border-radius: $radius-full;
border: 0;
height: 100px;
@@ -29,7 +29,7 @@
content: "";
width: $grid-unit-05;
background-color: rgba($gray-700, 0.4);
- border-radius: $radius-block-ui;
+ border-radius: $radius-full;
}
&.is-left {
diff --git a/packages/editor/src/components/sidebar/post-summary.js b/packages/editor/src/components/sidebar/post-summary.js
index b19848f2247063..72f1080770fd9d 100644
--- a/packages/editor/src/components/sidebar/post-summary.js
+++ b/packages/editor/src/components/sidebar/post-summary.js
@@ -28,6 +28,7 @@ import PostsPerPage from '../posts-per-page';
import SiteDiscussion from '../site-discussion';
import { store as editorStore } from '../../store';
import { PrivatePostLastRevision } from '../post-last-revision';
+import PostTrash from '../post-trash';
/**
* Module Constants
@@ -87,6 +88,9 @@ export default function PostSummary( { onActionPerformed } ) {
+
{ fills }
) }
diff --git a/packages/editor/src/components/start-page-options/index.js b/packages/editor/src/components/start-page-options/index.js
index d888d1ccc4342d..07fee67fbed19b 100644
--- a/packages/editor/src/components/start-page-options/index.js
+++ b/packages/editor/src/components/start-page-options/index.js
@@ -3,7 +3,7 @@
*/
import { Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
-import { useState, useMemo, useEffect } from '@wordpress/element';
+import { useState, useMemo } from '@wordpress/element';
import {
store as blockEditorStore,
__experimentalBlockPatternsList as BlockPatternsList,
@@ -12,6 +12,8 @@ import { useSelect, useDispatch } from '@wordpress/data';
import { useAsyncList } from '@wordpress/compose';
import { store as coreStore } from '@wordpress/core-data';
import { __unstableSerializeAndClean } from '@wordpress/blocks';
+import { store as preferencesStore } from '@wordpress/preferences';
+import { store as interfaceStore } from '@wordpress/interface';
/**
* Internal dependencies
@@ -19,7 +21,7 @@ import { __unstableSerializeAndClean } from '@wordpress/blocks';
import { store as editorStore } from '../../store';
import { TEMPLATE_POST_TYPE } from '../../store/constants';
-function useStartPatterns() {
+export function useStartPatterns() {
// A pattern is a start pattern if it includes 'core/post-content' in its blockTypes,
// and it has no postTypes declared and the current post type is page or if
// the current post type is part of the postTypes declared.
@@ -45,8 +47,14 @@ function useStartPatterns() {
);
return useMemo( () => {
- // filter patterns without postTypes declared if the current postType is page
- // or patterns that declare the current postType in its post type array.
+ if ( ! blockPatternsWithPostContentBlockType?.length ) {
+ return [];
+ }
+
+ /*
+ * Filter patterns without postTypes declared if the current postType is page
+ * or patterns that declare the current postType in its post type array.
+ */
return blockPatternsWithPostContentBlockType.filter( ( pattern ) => {
return (
( postType === 'page' && ! pattern.postTypes ) ||
@@ -110,30 +118,24 @@ function StartPageOptionsModal( { onClose } ) {
export default function StartPageOptions() {
const [ isClosed, setIsClosed ] = useState( false );
- const { shouldEnableModal, postType, postId } = useSelect( ( select ) => {
- const {
- isEditedPostDirty,
- isEditedPostEmpty,
- getCurrentPostType,
- getCurrentPostId,
- } = select( editorStore );
- const _postType = getCurrentPostType();
-
- return {
- shouldEnableModal:
- ! isEditedPostDirty() &&
- isEditedPostEmpty() &&
- TEMPLATE_POST_TYPE !== _postType,
- postType: _postType,
- postId: getCurrentPostId(),
- };
+ const shouldEnableModal = useSelect( ( select ) => {
+ const { isEditedPostDirty, isEditedPostEmpty, getCurrentPostType } =
+ select( editorStore );
+ const preferencesModalActive =
+ select( interfaceStore ).isModalActive( 'editor/preferences' );
+ const choosePatternModalEnabled = select( preferencesStore ).get(
+ 'core',
+ 'enableChoosePatternModal'
+ );
+ return (
+ choosePatternModalEnabled &&
+ ! preferencesModalActive &&
+ ! isEditedPostDirty() &&
+ isEditedPostEmpty() &&
+ TEMPLATE_POST_TYPE !== getCurrentPostType()
+ );
}, [] );
- useEffect( () => {
- // Should reset the modal state when navigating to a new page/post.
- setIsClosed( false );
- }, [ postType, postId ] );
-
if ( ! shouldEnableModal || isClosed ) {
return null;
}
diff --git a/packages/editor/src/components/visual-editor/edit-template-blocks-notification.js b/packages/editor/src/components/visual-editor/edit-template-blocks-notification.js
index 449834e3ab61cf..3ccbe79127c013 100644
--- a/packages/editor/src/components/visual-editor/edit-template-blocks-notification.js
+++ b/packages/editor/src/components/visual-editor/edit-template-blocks-notification.js
@@ -55,7 +55,10 @@ export default function EditTemplateBlocksNotification( { contentRef } ) {
return;
}
- if ( ! event.target.classList.contains( 'is-root-container' ) ) {
+ if (
+ ! event.target.classList.contains( 'is-root-container' ) ||
+ event.target.dataset?.type === 'core/template-part'
+ ) {
return;
}
setIsDialogOpen( true );
diff --git a/packages/editor/src/dataviews/actions/view-post.tsx b/packages/editor/src/dataviews/actions/view-post.tsx
index 47eb1a66d019ad..187faffafb5d3c 100644
--- a/packages/editor/src/dataviews/actions/view-post.tsx
+++ b/packages/editor/src/dataviews/actions/view-post.tsx
@@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { external } from '@wordpress/icons';
-import { __ } from '@wordpress/i18n';
+import { _x } from '@wordpress/i18n';
import type { Action } from '@wordpress/dataviews';
/**
@@ -12,7 +12,7 @@ import type { BasePost } from '../types';
const viewPost: Action< BasePost > = {
id: 'view-post',
- label: __( 'View' ),
+ label: _x( 'View', 'verb' ),
isPrimary: true,
icon: external,
isEligible( post ) {
diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js
index 8b0dfd4b370c41..f890dcac397326 100644
--- a/packages/editor/src/store/selectors.js
+++ b/packages/editor/src/store/selectors.js
@@ -1276,9 +1276,15 @@ export function getRenderingMode( state ) {
*
* @return {string} Device type.
*/
-export function getDeviceType( state ) {
- return state.deviceType;
-}
+export const getDeviceType = createRegistrySelector(
+ ( select ) => ( state ) => {
+ const editorMode = select( blockEditorStore ).__unstableGetEditorMode();
+ if ( editorMode === 'zoom-out' ) {
+ return 'Desktop';
+ }
+ return state.deviceType;
+ }
+);
/**
* Returns true if the list view is opened.
diff --git a/packages/editor/tsconfig.json b/packages/editor/tsconfig.json
index 8dfdf2ba7f2d5c..55d79babd1dacc 100644
--- a/packages/editor/tsconfig.json
+++ b/packages/editor/tsconfig.json
@@ -25,6 +25,7 @@
{ "path": "../i18n" },
{ "path": "../icons" },
{ "path": "../keycodes" },
+ { "path": "../media-utils" },
{ "path": "../notices" },
{ "path": "../plugins" },
{ "path": "../private-apis" },
diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md
index 4c0d695bef6a37..ccfa6a6021d71f 100644
--- a/packages/element/CHANGELOG.md
+++ b/packages/element/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/element/package.json b/packages/element/package.json
index b1ed7cef47033b..d6062abe6ecf61 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/element",
- "version": "6.6.0",
+ "version": "6.7.0",
"description": "Element React module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md
index 836084cd437fe7..b3dcf350596a0f 100644
--- a/packages/env/CHANGELOG.md
+++ b/packages/env/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 10.7.0 (2024-09-05)
+
## 10.6.0 (2024-08-21)
## 10.5.0 (2024-08-07)
diff --git a/packages/env/package.json b/packages/env/package.json
index f7b8b2fb4a64d1..7f3b15b1e15622 100644
--- a/packages/env/package.json
+++ b/packages/env/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/env",
- "version": "10.6.0",
+ "version": "10.7.0",
"description": "A zero-config, self contained local WordPress environment for development and testing.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/escape-html/CHANGELOG.md b/packages/escape-html/CHANGELOG.md
index d3a505806d4575..490cbdb184f5b1 100644
--- a/packages/escape-html/CHANGELOG.md
+++ b/packages/escape-html/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 3.7.0 (2024-09-05)
+
## 3.6.0 (2024-08-21)
## 3.5.0 (2024-08-07)
diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json
index 3b5572ffcfc2bf..8dd1a66cf2d5e9 100644
--- a/packages/escape-html/package.json
+++ b/packages/escape-html/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/escape-html",
- "version": "3.6.0",
+ "version": "3.7.0",
"description": "Escape HTML utils.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index b61a4ecb9f3bb7..14d44d21acda9f 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 21.0.0 (2024-09-05)
+
### Breaking Changes
- Add [`@wordpress/i18n-no-flanking-whitespace`](https://github.com/WordPress/gutenberg/blob/HEAD/packages/eslint-plugin/docs/rules/i18n-no-flanking-whitespace.md) to the recommended i18n ruleset ([#64710](https://github.com/WordPress/gutenberg/pull/64710).
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 7edb4033f32510..73996275c9154f 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/eslint-plugin",
- "version": "20.3.0",
+ "version": "21.0.0",
"description": "ESLint plugin for WordPress development.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/fields/.npmrc b/packages/fields/.npmrc
new file mode 100644
index 00000000000000..43c97e719a5a82
--- /dev/null
+++ b/packages/fields/.npmrc
@@ -0,0 +1 @@
+package-lock=false
diff --git a/packages/fields/CHANGELOG.md b/packages/fields/CHANGELOG.md
new file mode 100644
index 00000000000000..e04ce921cdfdc4
--- /dev/null
+++ b/packages/fields/CHANGELOG.md
@@ -0,0 +1,5 @@
+
+
+## Unreleased
+
+Initial release.
diff --git a/packages/fields/README.md b/packages/fields/README.md
new file mode 100644
index 00000000000000..c5044d1e9fdd2f
--- /dev/null
+++ b/packages/fields/README.md
@@ -0,0 +1,27 @@
+# Fields
+
+This package provides core elements for the DataView library, designed to simplify the creation and management of data display elements in WordPress.
+
+## Installation
+
+Install the module
+
+```bash
+npm install @wordpress/fields --save
+```
+
+## Usage
+
+
+
+Nothing to document.
+
+
+
+## Contributing to this package
+
+This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects.
+
+To find out more about contributing to this package or Gutenberg as a whole, please read the project's main [contributor guide](https://github.com/WordPress/gutenberg/tree/HEAD/CONTRIBUTING.md).
+
+
diff --git a/packages/fields/package.json b/packages/fields/package.json
new file mode 100644
index 00000000000000..4d5d33a10e5ed5
--- /dev/null
+++ b/packages/fields/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@wordpress/fields",
+ "version": "0.0.1",
+ "description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).",
+ "author": "The WordPress Contributors",
+ "license": "GPL-2.0-or-later",
+ "keywords": [
+ "wordpress",
+ "gutenberg",
+ "dataviews"
+ ],
+ "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/packages/fields/README.md",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/fields"
+ },
+ "bugs": {
+ "url": "https://github.com/WordPress/gutenberg/issues"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "main": "build/index.js",
+ "module": "build-module/index.js",
+ "types": "build-types",
+ "sideEffects": [
+ "build-style/**",
+ "src/**/*.scss"
+ ],
+ "dependencies": {
+ "@babel/runtime": "^7.16.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/fields/src/index.ts b/packages/fields/src/index.ts
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/packages/fields/tsconfig.json b/packages/fields/tsconfig.json
new file mode 100644
index 00000000000000..79aa09d0ad56e3
--- /dev/null
+++ b/packages/fields/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig.json",
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "declarationDir": "build-types",
+ "checkJs": false
+ },
+ "references": [],
+ "include": [ "src" ]
+}
diff --git a/packages/format-library/CHANGELOG.md b/packages/format-library/CHANGELOG.md
index ff23fff3e0487f..c003ac587fcdfa 100644
--- a/packages/format-library/CHANGELOG.md
+++ b/packages/format-library/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/format-library/package.json b/packages/format-library/package.json
index 531ec71224ce76..780f513bdb0374 100644
--- a/packages/format-library/package.json
+++ b/packages/format-library/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/format-library",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Format library for the WordPress editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/format-library/src/link/index.js b/packages/format-library/src/link/index.js
index ce1af0a36268ec..ef7008451698ae 100644
--- a/packages/format-library/src/link/index.js
+++ b/packages/format-library/src/link/index.js
@@ -12,7 +12,7 @@ import {
insert,
create,
} from '@wordpress/rich-text';
-import { isURL, isEmail } from '@wordpress/url';
+import { isURL, isEmail, isPhoneNumber } from '@wordpress/url';
import {
RichTextToolbarButton,
RichTextShortcut,
@@ -104,6 +104,13 @@ function Edit( {
attributes: { url: `mailto:${ text }` },
} )
);
+ } else if ( ! isActive && text && isPhoneNumber( text ) ) {
+ onChange(
+ applyFormat( value, {
+ type: name,
+ attributes: { url: `tel:${ text.replace( /\D/g, '' ) }` },
+ } )
+ );
} else {
if ( target ) {
setOpenedBy( {
diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md
index f23ab552ca7711..de9e11c38fe905 100644
--- a/packages/hooks/CHANGELOG.md
+++ b/packages/hooks/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/hooks/package.json b/packages/hooks/package.json
index 007404ec4a371d..51074af0d239b0 100644
--- a/packages/hooks/package.json
+++ b/packages/hooks/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/hooks",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "WordPress hooks library.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/html-entities/CHANGELOG.md b/packages/html-entities/CHANGELOG.md
index 03e163b070bd86..6887a70b391f98 100644
--- a/packages/html-entities/CHANGELOG.md
+++ b/packages/html-entities/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json
index 690bd5d6762215..30e3c1728307d7 100644
--- a/packages/html-entities/package.json
+++ b/packages/html-entities/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/html-entities",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "HTML entity utilities for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md
index 40f11787b9779f..d2bf7ad7ea7eb3 100644
--- a/packages/i18n/CHANGELOG.md
+++ b/packages/i18n/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index ef53c5a52900fb..fb3c488c1a4dea 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/i18n",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "WordPress internationalization (i18n) library.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/icons/CHANGELOG.md b/packages/icons/CHANGELOG.md
index f5d33c3a0dea01..32bbd88181e369 100644
--- a/packages/icons/CHANGELOG.md
+++ b/packages/icons/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 10.7.0 (2024-09-05)
+
## 10.6.0 (2024-08-21)
## 10.5.0 (2024-08-07)
diff --git a/packages/icons/package.json b/packages/icons/package.json
index 576160fdb52738..ab23fef8eff667 100644
--- a/packages/icons/package.json
+++ b/packages/icons/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/icons",
- "version": "10.6.0",
+ "version": "10.7.0",
"description": "WordPress Icons package, based on dashicon.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/interactivity-router/CHANGELOG.md b/packages/interactivity-router/CHANGELOG.md
index 472b351682bbce..00c47f3ed074b6 100644
--- a/packages/interactivity-router/CHANGELOG.md
+++ b/packages/interactivity-router/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 2.7.0 (2024-09-05)
+
## 2.6.0 (2024-08-21)
## 2.5.0 (2024-08-07)
diff --git a/packages/interactivity-router/package.json b/packages/interactivity-router/package.json
index 29a3f53a6109f0..db85c0d8bdba3b 100644
--- a/packages/interactivity-router/package.json
+++ b/packages/interactivity-router/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/interactivity-router",
- "version": "2.6.0",
+ "version": "2.7.0",
"description": "Package that exposes state and actions from the `core/router` store, part of the Interactivity API.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
@@ -26,6 +26,7 @@
"module": "build-module/index.js",
"react-native": "src/index",
"types": "build-types",
+ "wpScriptModuleExports": "./build-module/index.js",
"dependencies": {
"@wordpress/interactivity": "file:../interactivity"
},
diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md
index 1d72e85f3c34e2..9ea269fa269ad0 100644
--- a/packages/interactivity/CHANGELOG.md
+++ b/packages/interactivity/CHANGELOG.md
@@ -2,6 +2,13 @@
## Unreleased
+### Bug Fixes
+
+- Prevent calling `proxifyContext` over an already-proxified context inside `wp-context` ([#65090](https://github.com/WordPress/gutenberg/pull/65090)).
+- Update iterable signals when `deepMerge()` adds new properties ([#65135](https://github.com/WordPress/gutenberg/pull/65135)).
+
+## 6.7.0 (2024-09-05)
+
### Enhancements
- Improve internal `deepMerge` function ([#64879](https://github.com/WordPress/gutenberg/pull/64879)).
diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json
index fca8a03a7e45a7..6be9f3c4a0d7d8 100644
--- a/packages/interactivity/package.json
+++ b/packages/interactivity/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/interactivity",
- "version": "6.6.0",
+ "version": "6.7.0",
"description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
@@ -26,6 +26,10 @@
"module": "build-module/index.js",
"react-native": "src/index",
"types": "build-types",
+ "wpScriptModuleExports": {
+ ".": "./build-module/index.js",
+ "./debug": "./build-module/debug.js"
+ },
"dependencies": {
"@preact/signals": "^1.2.2",
"preact": "^10.19.3"
diff --git a/packages/interactivity/src/directives.tsx b/packages/interactivity/src/directives.tsx
index 715bc2ea7b16b6..b41d50722e3765 100644
--- a/packages/interactivity/src/directives.tsx
+++ b/packages/interactivity/src/directives.tsx
@@ -290,11 +290,10 @@ export default () => {
currentValue.current,
deepClone( value ) as object
);
- currentValue.current = proxifyContext(
+ result[ namespace ] = proxifyContext(
currentValue.current,
inheritedValue[ namespace ]
);
- result[ namespace ] = currentValue.current;
}
return result;
}, [ defaultEntry, inheritedValue ] );
diff --git a/packages/interactivity/src/proxies/state.ts b/packages/interactivity/src/proxies/state.ts
index 323cd306bf9acf..ec49c4b27c4adb 100644
--- a/packages/interactivity/src/proxies/state.ts
+++ b/packages/interactivity/src/proxies/state.ts
@@ -276,13 +276,17 @@ const deepMergeRecursive = (
override: boolean = true
) => {
if ( isPlainObject( target ) && isPlainObject( source ) ) {
+ let hasNewKeys = false;
for ( const key in source ) {
+ const isNew = ! ( key in target );
+ hasNewKeys = hasNewKeys || isNew;
+
const desc = Object.getOwnPropertyDescriptor( source, key );
if (
typeof desc?.get === 'function' ||
typeof desc?.set === 'function'
) {
- if ( override || ! ( key in target ) ) {
+ if ( override || isNew ) {
Object.defineProperty( target, key, {
...desc,
configurable: true,
@@ -296,12 +300,12 @@ const deepMergeRecursive = (
}
}
} else if ( isPlainObject( source[ key ] ) ) {
- if ( ! ( key in target ) ) {
+ if ( isNew ) {
target[ key ] = {};
}
deepMergeRecursive( target[ key ], source[ key ], override );
- } else if ( override || ! ( key in target ) ) {
+ } else if ( override || isNew ) {
Object.defineProperty( target, key, desc! );
const proxy = getProxyFromObject( target );
@@ -311,6 +315,10 @@ const deepMergeRecursive = (
}
}
}
+
+ if ( hasNewKeys && objToIterable.has( target ) ) {
+ objToIterable.get( target )!.value++;
+ }
}
};
diff --git a/packages/interactivity/src/proxies/test/deep-merge.ts b/packages/interactivity/src/proxies/test/deep-merge.ts
index b24d9b6781cfe2..bf31c4b552133c 100644
--- a/packages/interactivity/src/proxies/test/deep-merge.ts
+++ b/packages/interactivity/src/proxies/test/deep-merge.ts
@@ -374,5 +374,20 @@ describe( 'Interactivity API', () => {
expect( spy ).toHaveBeenCalledTimes( 3 );
} );
+
+ it( 'should update iterable signals when new keys are added', () => {
+ const target = proxifyState< any >( 'test', { a: 1, b: 2 } );
+ const source = { a: 1, b: 2, c: 3 };
+
+ const spy = jest.fn( () => Object.keys( target ) );
+ effect( spy );
+
+ expect( spy ).toHaveBeenCalledTimes( 1 );
+
+ deepMerge( target, source, false );
+
+ expect( spy ).toHaveBeenCalledTimes( 2 );
+ expect( spy ).toHaveLastReturnedWith( [ 'a', 'b', 'c' ] );
+ } );
} );
} );
diff --git a/packages/interface/CHANGELOG.md b/packages/interface/CHANGELOG.md
index 60a339f019bf50..64f8c370bb6ece 100644
--- a/packages/interface/CHANGELOG.md
+++ b/packages/interface/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/interface/package.json b/packages/interface/package.json
index 6e017bfd349256..d15d648eaef446 100644
--- a/packages/interface/package.json
+++ b/packages/interface/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/interface",
- "version": "6.6.0",
+ "version": "6.7.0",
"description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/is-shallow-equal/CHANGELOG.md b/packages/is-shallow-equal/CHANGELOG.md
index 23e1a6e0098484..81f2c4b164a3ab 100644
--- a/packages/is-shallow-equal/CHANGELOG.md
+++ b/packages/is-shallow-equal/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json
index baa1dd7b3ebea9..553c3182bb5668 100644
--- a/packages/is-shallow-equal/package.json
+++ b/packages/is-shallow-equal/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/is-shallow-equal",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Test for shallow equality between two objects or arrays.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/jest-console/CHANGELOG.md b/packages/jest-console/CHANGELOG.md
index ff8c00c83fc8ee..7f7f80b815a850 100644
--- a/packages/jest-console/CHANGELOG.md
+++ b/packages/jest-console/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 8.7.0 (2024-09-05)
+
## 8.6.0 (2024-08-21)
## 8.5.0 (2024-08-07)
diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json
index 4e0d804088f893..4381a0f287235f 100644
--- a/packages/jest-console/package.json
+++ b/packages/jest-console/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/jest-console",
- "version": "8.6.0",
+ "version": "8.7.0",
"description": "Custom Jest matchers for the Console object.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/jest-preset-default/CHANGELOG.md b/packages/jest-preset-default/CHANGELOG.md
index 04acc6e69ef606..e48d25dc1e6424 100644
--- a/packages/jest-preset-default/CHANGELOG.md
+++ b/packages/jest-preset-default/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 12.7.0 (2024-09-05)
+
## 12.6.0 (2024-08-21)
## 12.5.0 (2024-08-07)
diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json
index d09e89e88a63f1..27638c711d4d1e 100644
--- a/packages/jest-preset-default/package.json
+++ b/packages/jest-preset-default/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/jest-preset-default",
- "version": "12.6.0",
+ "version": "12.7.0",
"description": "Default Jest preset for WordPress development.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/jest-puppeteer-axe/CHANGELOG.md b/packages/jest-puppeteer-axe/CHANGELOG.md
index 984fdf249f504e..572b4aa137a141 100644
--- a/packages/jest-puppeteer-axe/CHANGELOG.md
+++ b/packages/jest-puppeteer-axe/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 7.7.0 (2024-09-05)
+
## 7.6.0 (2024-08-21)
## 7.5.0 (2024-08-07)
diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json
index a512a018a9760c..b8ccbfcd36beff 100644
--- a/packages/jest-puppeteer-axe/package.json
+++ b/packages/jest-puppeteer-axe/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/jest-puppeteer-axe",
- "version": "7.6.0",
+ "version": "7.7.0",
"description": "Axe API integration with Jest and Puppeteer.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/keyboard-shortcuts/CHANGELOG.md b/packages/keyboard-shortcuts/CHANGELOG.md
index 20396b4b935ac6..fe3506ea4a5f19 100644
--- a/packages/keyboard-shortcuts/CHANGELOG.md
+++ b/packages/keyboard-shortcuts/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json
index 52070a0904e101..dd560424b66ff7 100644
--- a/packages/keyboard-shortcuts/package.json
+++ b/packages/keyboard-shortcuts/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/keyboard-shortcuts",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Handling keyboard shortcuts.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/keycodes/CHANGELOG.md b/packages/keycodes/CHANGELOG.md
index 745412efcb990b..51050728f2f5a3 100644
--- a/packages/keycodes/CHANGELOG.md
+++ b/packages/keycodes/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json
index 4206b93430cfa5..5d8ec0545d7e3f 100644
--- a/packages/keycodes/package.json
+++ b/packages/keycodes/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/keycodes",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/lazy-import/CHANGELOG.md b/packages/lazy-import/CHANGELOG.md
index 944cb6207fb962..daea6da3404d3f 100644
--- a/packages/lazy-import/CHANGELOG.md
+++ b/packages/lazy-import/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 2.7.0 (2024-09-05)
+
## 2.6.0 (2024-08-21)
## 2.5.0 (2024-08-07)
diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json
index 54ea606faefac5..62aab79635e9c4 100644
--- a/packages/lazy-import/package.json
+++ b/packages/lazy-import/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/lazy-import",
- "version": "2.6.0",
+ "version": "2.7.0",
"description": "Lazily import a module, installing it automatically if missing.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md
index 1338124bf1a7cd..9f62523fd94e11 100644
--- a/packages/list-reusable-blocks/CHANGELOG.md
+++ b/packages/list-reusable-blocks/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json
index 8e5fb404607c6e..839532327faa24 100644
--- a/packages/list-reusable-blocks/package.json
+++ b/packages/list-reusable-blocks/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/list-reusable-blocks",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "Adding Export/Import support to the reusable blocks listing.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/media-utils/CHANGELOG.md b/packages/media-utils/CHANGELOG.md
index c155b906ab6dca..26282b6b2bda21 100644
--- a/packages/media-utils/CHANGELOG.md
+++ b/packages/media-utils/CHANGELOG.md
@@ -2,6 +2,12 @@
## Unreleased
+### New Features
+
+- Rewrite in TypeScript, exporting all the individual utility functions.
+
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/media-utils/README.md b/packages/media-utils/README.md
index 6c2384061a5f6f..1adc15186078a5 100644
--- a/packages/media-utils/README.md
+++ b/packages/media-utils/README.md
@@ -13,6 +13,75 @@ npm install @wordpress/media-utils --save
_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill) in your code._
+## API
+
+
+
+### Attachment
+
+Undocumented declaration.
+
+### MediaUpload
+
+Undocumented declaration.
+
+### RestAttachment
+
+Undocumented declaration.
+
+### transformAttachment
+
+Transforms an attachment object from the REST API shape into the shape expected by the block editor and other consumers.
+
+_Parameters_
+
+- _attachment_ `RestAttachment`: REST API attachment object.
+
+### uploadMedia
+
+Upload a media file when the file upload button is activated or when adding a file to the editor via drag & drop.
+
+_Parameters_
+
+- _$0_ `UploadMediaArgs`: Parameters object passed to the function.
+- _$0.allowedTypes_ `UploadMediaArgs[ 'allowedTypes' ]`: Array with the types of media that can be uploaded, if unset all types are allowed.
+- _$0.additionalData_ `UploadMediaArgs[ 'additionalData' ]`: Additional data to include in the request.
+- _$0.filesList_ `UploadMediaArgs[ 'filesList' ]`: List of files.
+- _$0.maxUploadFileSize_ `UploadMediaArgs[ 'maxUploadFileSize' ]`: Maximum upload size in bytes allowed for the site.
+- _$0.onError_ `UploadMediaArgs[ 'onError' ]`: Function called when an error happens.
+- _$0.onFileChange_ `UploadMediaArgs[ 'onFileChange' ]`: Function called each time a file or a temporary representation of the file is available.
+- _$0.wpAllowedMimeTypes_ `UploadMediaArgs[ 'wpAllowedMimeTypes' ]`: List of allowed mime types and file extensions.
+- _$0.signal_ `UploadMediaArgs[ 'signal' ]`: Abort signal.
+
+### validateFileSize
+
+Verifies whether the file is within the file upload size limits for the site.
+
+_Parameters_
+
+- _file_ `File`: File object.
+- _maxUploadFileSize_ `number`: Maximum upload size in bytes allowed for the site.
+
+### validateMimeType
+
+Verifies if the caller (e.g. a block) supports this mime type.
+
+_Parameters_
+
+- _file_ `File`: File object.
+- _allowedTypes_ `string[]`: List of allowed mime types.
+
+### validateMimeTypeForUser
+
+Verifies if the user is allowed to upload this mime type.
+
+_Parameters_
+
+- _file_ `File`: File object.
+- _wpAllowedMimeTypes_ `Record< string, string > | null`: List of allowed mime types and file extensions.
+
+
+
## Usage
### uploadMedia
@@ -43,7 +112,7 @@ Beware that first onFileChange is called with temporary blob URLs and then with
### MediaUpload
Media upload component provides a UI button that allows users to open the WordPress media library. It is normally used in conjunction with the filter `editor.MediaUpload`.
-The component follows the interface specified in [https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-upload/README.md](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-upload/README.md), and more details regarding its usage can be checked there.
+The component follows the interface specified in , and more details regarding its usage can be checked there.
## Contributing to this package
diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json
index 0cbf38592a1fd6..1cfe019e89d0a6 100644
--- a/packages/media-utils/package.json
+++ b/packages/media-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/media-utils",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "WordPress Media Upload Utils.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
@@ -14,7 +14,7 @@
"repository": {
"type": "git",
"url": "https://github.com/WordPress/gutenberg.git",
- "directory": "packages/url"
+ "directory": "packages/media-utils"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
@@ -25,6 +25,7 @@
},
"main": "build/index.js",
"module": "build-module/index.js",
+ "types": "build-types",
"dependencies": {
"@babel/runtime": "^7.16.0",
"@wordpress/api-fetch": "file:../api-fetch",
diff --git a/packages/media-utils/src/index.js b/packages/media-utils/src/index.js
deleted file mode 100644
index 590a7f4c9d188d..00000000000000
--- a/packages/media-utils/src/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './components';
-export * from './utils';
diff --git a/packages/media-utils/src/index.ts b/packages/media-utils/src/index.ts
new file mode 100644
index 00000000000000..c37814afe7c48f
--- /dev/null
+++ b/packages/media-utils/src/index.ts
@@ -0,0 +1,9 @@
+export * from './components';
+
+export { uploadMedia } from './utils/upload-media';
+export { transformAttachment } from './utils/transform-attachment';
+export { validateFileSize } from './utils/validate-file-size';
+export { validateMimeType } from './utils/validate-mime-type';
+export { validateMimeTypeForUser } from './utils/validate-mime-type-for-user';
+
+export type { Attachment, RestAttachment } from './utils/types';
diff --git a/packages/media-utils/src/utils/flatten-form-data.ts b/packages/media-utils/src/utils/flatten-form-data.ts
new file mode 100644
index 00000000000000..594f172ef7eb2c
--- /dev/null
+++ b/packages/media-utils/src/utils/flatten-form-data.ts
@@ -0,0 +1,33 @@
+/**
+ * Determines whether the passed argument appears to be a plain object.
+ *
+ * @param data The object to inspect.
+ */
+function isPlainObject( data: unknown ): data is Record< string, unknown > {
+ return (
+ data !== null &&
+ typeof data === 'object' &&
+ Object.getPrototypeOf( data ) === Object.prototype
+ );
+}
+
+/**
+ * Recursively flatten data passed to form data, to allow using multi-level objects.
+ *
+ * @param {FormData} formData Form data object.
+ * @param {string} key Key to amend to form data object
+ * @param {string|Object} data Data to be amended to form data.
+ */
+export function flattenFormData(
+ formData: FormData,
+ key: string,
+ data: string | undefined | Record< string, string >
+) {
+ if ( isPlainObject( data ) ) {
+ for ( const [ name, value ] of Object.entries( data ) ) {
+ flattenFormData( formData, `${ key }[${ name }]`, value );
+ }
+ } else if ( data !== undefined ) {
+ formData.append( key, String( data ) );
+ }
+}
diff --git a/packages/media-utils/src/utils/get-mime-types-array.ts b/packages/media-utils/src/utils/get-mime-types-array.ts
new file mode 100644
index 00000000000000..d4940d36cd6ae5
--- /dev/null
+++ b/packages/media-utils/src/utils/get-mime-types-array.ts
@@ -0,0 +1,29 @@
+/**
+ * Browsers may use unexpected mime types, and they differ from browser to browser.
+ * This function computes a flexible array of mime types from the mime type structured provided by the server.
+ * Converts { jpg|jpeg|jpe: "image/jpeg" } into [ "image/jpeg", "image/jpg", "image/jpeg", "image/jpe" ]
+ *
+ * @param {?Object} wpMimeTypesObject Mime type object received from the server.
+ * Extensions are keys separated by '|' and values are mime types associated with an extension.
+ *
+ * @return An array of mime types or null
+ */
+export function getMimeTypesArray(
+ wpMimeTypesObject?: Record< string, string > | null
+) {
+ if ( ! wpMimeTypesObject ) {
+ return null;
+ }
+ return Object.entries( wpMimeTypesObject ).flatMap(
+ ( [ extensionsString, mime ] ) => {
+ const [ type ] = mime.split( '/' );
+ const extensions = extensionsString.split( '|' );
+ return [
+ mime,
+ ...extensions.map(
+ ( extension ) => `${ type }/${ extension }`
+ ),
+ ];
+ }
+ );
+}
diff --git a/packages/media-utils/src/utils/index.js b/packages/media-utils/src/utils/index.js
deleted file mode 100644
index 509b62f1e88648..00000000000000
--- a/packages/media-utils/src/utils/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { uploadMedia } from './upload-media';
diff --git a/packages/media-utils/src/utils/test/flatten-form-data.ts b/packages/media-utils/src/utils/test/flatten-form-data.ts
new file mode 100644
index 00000000000000..458dfd26c8f6ed
--- /dev/null
+++ b/packages/media-utils/src/utils/test/flatten-form-data.ts
@@ -0,0 +1,49 @@
+/**
+ * Internal dependencies
+ */
+import { flattenFormData } from '../flatten-form-data';
+
+describe( 'flattenFormData', () => {
+ it( 'should flatten nested data structure', () => {
+ const data = new FormData();
+
+ class RichTextData {
+ toString() {
+ return 'i am rich text';
+ }
+ }
+
+ const additionalData = {
+ foo: null,
+ bar: 1234,
+ meta: {
+ nested: 'foo',
+ dothis: true,
+ dothat: false,
+ supermeta: {
+ nested: 'baz',
+ },
+ },
+ customClass: new RichTextData(),
+ };
+
+ for ( const [ key, value ] of Object.entries( additionalData ) ) {
+ flattenFormData(
+ data,
+ key,
+ value as Parameters< typeof flattenFormData >[ 2 ]
+ );
+ }
+
+ const actual = Object.fromEntries( data.entries() );
+ expect( actual ).toStrictEqual( {
+ bar: '1234',
+ foo: 'null',
+ 'meta[dothat]': 'false',
+ 'meta[dothis]': 'true',
+ 'meta[nested]': 'foo',
+ 'meta[supermeta][nested]': 'baz',
+ customClass: 'i am rich text',
+ } );
+ } );
+} );
diff --git a/packages/media-utils/src/utils/test/get-mime-types-array.ts b/packages/media-utils/src/utils/test/get-mime-types-array.ts
new file mode 100644
index 00000000000000..156955373bd0da
--- /dev/null
+++ b/packages/media-utils/src/utils/test/get-mime-types-array.ts
@@ -0,0 +1,47 @@
+/**
+ * Internal dependencies
+ */
+import { getMimeTypesArray } from '../get-mime-types-array';
+
+describe( 'getMimeTypesArray', () => {
+ it( 'should return null if it is "falsy" e.g: undefined or null', () => {
+ expect( getMimeTypesArray( null ) ).toEqual( null );
+ expect( getMimeTypesArray( undefined ) ).toEqual( null );
+ } );
+
+ it( 'should return an empty array if an empty object is passed', () => {
+ expect( getMimeTypesArray( {} ) ).toEqual( [] );
+ } );
+
+ it( 'should return the type plus a new mime type with type and subtype with the extension if a type is passed', () => {
+ expect( getMimeTypesArray( { ext: 'chicken' } ) ).toEqual( [
+ 'chicken',
+ 'chicken/ext',
+ ] );
+ } );
+
+ it( 'should return the mime type passed and a new mime type with type and the extension as subtype', () => {
+ expect( getMimeTypesArray( { ext: 'chicken/ribs' } ) ).toEqual( [
+ 'chicken/ribs',
+ 'chicken/ext',
+ ] );
+ } );
+
+ it( 'should return the mime type passed and an additional mime type per extension supported', () => {
+ expect( getMimeTypesArray( { 'jpg|jpeg|jpe': 'image/jpeg' } ) ).toEqual(
+ [ 'image/jpeg', 'image/jpg', 'image/jpeg', 'image/jpe' ]
+ );
+ } );
+
+ it( 'should handle multiple mime types', () => {
+ expect(
+ getMimeTypesArray( { 'ext|aaa': 'chicken/ribs', aaa: 'bbb' } )
+ ).toEqual( [
+ 'chicken/ribs',
+ 'chicken/ext',
+ 'chicken/aaa',
+ 'bbb',
+ 'bbb/aaa',
+ ] );
+ } );
+} );
diff --git a/packages/media-utils/src/utils/test/upload-error.ts b/packages/media-utils/src/utils/test/upload-error.ts
new file mode 100644
index 00000000000000..4d5f025ed8cf39
--- /dev/null
+++ b/packages/media-utils/src/utils/test/upload-error.ts
@@ -0,0 +1,24 @@
+/**
+ * Internal dependencies
+ */
+import { UploadError } from '../upload-error';
+
+describe( 'UploadError', () => {
+ it( 'holds error code and file name', () => {
+ const file = new File( [], 'example.jpg', {
+ lastModified: 1234567891,
+ type: 'image/jpeg',
+ } );
+
+ const error = new UploadError( {
+ code: 'some_error',
+ message: 'An error occurred',
+ file,
+ } );
+
+ expect( error ).toStrictEqual( expect.any( Error ) );
+ expect( error.code ).toBe( 'some_error' );
+ expect( error.message ).toBe( 'An error occurred' );
+ expect( error.file ).toBe( file );
+ } );
+} );
diff --git a/packages/media-utils/src/utils/test/upload-media.test.js b/packages/media-utils/src/utils/test/upload-media.ts
similarity index 58%
rename from packages/media-utils/src/utils/test/upload-media.test.js
rename to packages/media-utils/src/utils/test/upload-media.ts
index fa9adc9f408815..b5075255ad4c81 100644
--- a/packages/media-utils/src/utils/test/upload-media.test.js
+++ b/packages/media-utils/src/utils/test/upload-media.ts
@@ -1,19 +1,18 @@
-/**
- * WordPress dependencies
- */
-import { createBlobURL } from '@wordpress/blob';
-import apiFetch from '@wordpress/api-fetch';
-
/**
* Internal dependencies
*/
-import { uploadMedia, getMimeTypesArray } from '../upload-media';
+import { uploadMedia } from '../upload-media';
+import { UploadError } from '../upload-error';
+import { uploadToServer } from '../upload-to-server';
+
+jest.mock( '../upload-to-server', () => ( {
+ uploadToServer: jest.fn(),
+} ) );
jest.mock( '@wordpress/blob', () => ( {
createBlobURL: jest.fn(),
revokeBlobURL: jest.fn(),
} ) );
-jest.mock( '@wordpress/api-fetch', () => jest.fn() );
const xmlFile = new window.File( [ 'fake_file' ], 'test.xml', {
type: 'text/xml',
@@ -23,6 +22,10 @@ const imageFile = new window.File( [ 'fake_file' ], 'test.jpeg', {
} );
describe( 'uploadMedia', () => {
+ afterEach( () => {
+ jest.clearAllMocks();
+ } );
+
it( 'should do nothing on no files', async () => {
const onError = jest.fn();
const onFileChange = jest.fn();
@@ -33,7 +36,7 @@ describe( 'uploadMedia', () => {
} );
expect( onError ).not.toHaveBeenCalled();
- expect( onFileChange ).not.toHaveBeenCalled();
+ expect( uploadToServer ).not.toHaveBeenCalled();
} );
it( 'should error if allowedTypes contains a partial mime type and the validation fails', async () => {
@@ -47,11 +50,14 @@ describe( 'uploadMedia', () => {
} );
expect( onError ).toHaveBeenCalledWith(
- expect.objectContaining( {
+ new UploadError( {
code: 'MIME_TYPE_NOT_SUPPORTED',
+ message:
+ 'test.xml: Sorry, this file type is not supported here.',
+ file: xmlFile,
} )
);
- expect( onFileChange ).not.toHaveBeenCalled();
+ expect( uploadToServer ).not.toHaveBeenCalled();
} );
it( 'should error if allowedTypes contains a complete mime type and the validation fails', async () => {
@@ -65,17 +71,17 @@ describe( 'uploadMedia', () => {
} );
expect( onError ).toHaveBeenCalledWith(
- expect.objectContaining( {
+ new UploadError( {
code: 'MIME_TYPE_NOT_SUPPORTED',
+ message:
+ 'test.jpeg: Sorry, this file type is not supported here.',
+ file: xmlFile,
} )
);
- expect( onFileChange ).not.toHaveBeenCalled();
+ expect( uploadToServer ).not.toHaveBeenCalled();
} );
it( 'should work if allowedTypes contains a complete mime type and the validation succeeds', async () => {
- createBlobURL.mockReturnValue( 'blob:fake_blob' );
- apiFetch.mockResolvedValue( { title: { raw: 'Test' } } );
-
const onError = jest.fn();
const onFileChange = jest.fn();
await uploadMedia( {
@@ -83,10 +89,11 @@ describe( 'uploadMedia', () => {
filesList: [ imageFile ],
onError,
onFileChange,
+ wpAllowedMimeTypes: { jpeg: 'image/jpeg' },
} );
expect( onError ).not.toHaveBeenCalled();
- expect( onFileChange ).toHaveBeenCalledTimes( 2 );
+ expect( uploadToServer ).toHaveBeenCalled();
} );
it( 'should error if allowedTypes contains multiple types and the validation fails', async () => {
@@ -100,17 +107,17 @@ describe( 'uploadMedia', () => {
} );
expect( onError ).toHaveBeenCalledWith(
- expect.objectContaining( {
+ new UploadError( {
code: 'MIME_TYPE_NOT_SUPPORTED',
+ message:
+ 'test.xml: Sorry, this file type is not supported here.',
+ file: xmlFile,
} )
);
- expect( onFileChange ).not.toHaveBeenCalled();
+ expect( uploadToServer ).not.toHaveBeenCalled();
} );
it( 'should work if allowedTypes contains multiple types and the validation succeeds', async () => {
- createBlobURL.mockReturnValue( 'blob:fake_blob' );
- apiFetch.mockResolvedValue( { title: { raw: 'Test' } } );
-
const onError = jest.fn();
const onFileChange = jest.fn();
await uploadMedia( {
@@ -118,16 +125,14 @@ describe( 'uploadMedia', () => {
filesList: [ imageFile ],
onError,
onFileChange,
+ wpAllowedMimeTypes: { jpeg: 'image/jpeg', mp4: 'video/mp4' },
} );
expect( onError ).not.toHaveBeenCalled();
- expect( onFileChange ).toHaveBeenCalledTimes( 2 );
+ expect( uploadToServer ).toHaveBeenCalled();
} );
it( 'should only fail the invalid file and still allow others to succeed when uploading multiple files', async () => {
- createBlobURL.mockReturnValue( 'blob:fake_blob' );
- apiFetch.mockResolvedValue( { title: { raw: 'Test' } } );
-
const onError = jest.fn();
const onFileChange = jest.fn();
await uploadMedia( {
@@ -135,15 +140,18 @@ describe( 'uploadMedia', () => {
filesList: [ imageFile, xmlFile ],
onError,
onFileChange,
+ wpAllowedMimeTypes: { jpeg: 'image/jpeg' },
} );
expect( onError ).toHaveBeenCalledWith(
- expect.objectContaining( {
+ new UploadError( {
code: 'MIME_TYPE_NOT_SUPPORTED',
+ message:
+ 'test.xml: Sorry, you are not allowed to upload this file type.',
file: xmlFile,
} )
);
- expect( onFileChange ).toHaveBeenCalledTimes( 2 );
+ expect( uploadToServer ).toHaveBeenCalledTimes( 1 );
} );
it( 'should error if the file size is greater than the maximum', async () => {
@@ -155,19 +163,22 @@ describe( 'uploadMedia', () => {
maxUploadFileSize: 1,
onError,
onFileChange,
+ wpAllowedMimeTypes: { jpeg: 'image/jpeg' },
} );
expect( onError ).toHaveBeenCalledWith(
- expect.objectContaining( {
+ new UploadError( {
code: 'SIZE_ABOVE_LIMIT',
+ message:
+ 'test.jpeg: This file exceeds the maximum upload size for this site.',
+ file: imageFile,
} )
);
- expect( onFileChange ).not.toHaveBeenCalled();
+ expect( uploadToServer ).not.toHaveBeenCalled();
} );
it( 'should call error handler with the correct error object if file type is not allowed for user', async () => {
const onError = jest.fn();
- const onFileChange = jest.fn();
await uploadMedia( {
allowedTypes: [ 'image' ],
filesList: [ imageFile ],
@@ -176,53 +187,13 @@ describe( 'uploadMedia', () => {
} );
expect( onError ).toHaveBeenCalledWith(
- expect.objectContaining( {
+ new UploadError( {
code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER',
+ message:
+ 'test.jpeg: Sorry, you are not allowed to upload this file type.',
+ file: imageFile,
} )
);
- expect( onFileChange ).not.toHaveBeenCalled();
- } );
-} );
-
-describe( 'getMimeTypesArray', () => {
- it( 'should return the parameter passed if it is "falsy" e.g: undefined or null', () => {
- expect( getMimeTypesArray( null ) ).toEqual( null );
- expect( getMimeTypesArray( undefined ) ).toEqual( undefined );
- } );
-
- it( 'should return an empty array if an empty object is passed', () => {
- expect( getMimeTypesArray( {} ) ).toEqual( [] );
- } );
-
- it( 'should return the type plus a new mime type with type and subtype with the extension if a type is passed', () => {
- expect( getMimeTypesArray( { ext: 'chicken' } ) ).toEqual( [
- 'chicken',
- 'chicken/ext',
- ] );
- } );
-
- it( 'should return the mime type passed and a new mime type with type and the extension as subtype', () => {
- expect( getMimeTypesArray( { ext: 'chicken/ribs' } ) ).toEqual( [
- 'chicken/ribs',
- 'chicken/ext',
- ] );
- } );
-
- it( 'should return the mime type passed and an additional mime type per extension supported', () => {
- expect( getMimeTypesArray( { 'jpg|jpeg|jpe': 'image/jpeg' } ) ).toEqual(
- [ 'image/jpeg', 'image/jpg', 'image/jpeg', 'image/jpe' ]
- );
- } );
-
- it( 'should handle multiple mime types', () => {
- expect(
- getMimeTypesArray( { 'ext|aaa': 'chicken/ribs', aaa: 'bbb' } )
- ).toEqual( [
- 'chicken/ribs',
- 'chicken/ext',
- 'chicken/aaa',
- 'bbb',
- 'bbb/aaa',
- ] );
+ expect( uploadToServer ).not.toHaveBeenCalled();
} );
} );
diff --git a/packages/media-utils/src/utils/test/validate-file-size.ts b/packages/media-utils/src/utils/test/validate-file-size.ts
new file mode 100644
index 00000000000000..31d6af0e7e4a55
--- /dev/null
+++ b/packages/media-utils/src/utils/test/validate-file-size.ts
@@ -0,0 +1,70 @@
+/**
+ * Internal dependencies
+ */
+import { validateFileSize } from '../validate-file-size';
+import { UploadError } from '../upload-error';
+
+const imageFile = new window.File( [ 'fake_file' ], 'test.jpeg', {
+ type: 'image/jpeg',
+} );
+
+const emptyFile = new window.File( [], 'test.jpeg', {
+ type: 'image/jpeg',
+} );
+
+describe( 'validateFileSize', () => {
+ afterEach( () => {
+ jest.clearAllMocks();
+ } );
+
+ it( 'should error if the file is empty', () => {
+ expect( () => {
+ validateFileSize( emptyFile );
+ } ).toThrow(
+ new UploadError( {
+ code: 'EMPTY_FILE',
+ message: 'test.jpeg: This file is empty.',
+ file: imageFile,
+ } )
+ );
+ } );
+
+ it( 'should error if the file is is greater than the maximum', () => {
+ expect( () => {
+ validateFileSize( imageFile, 2 );
+ } ).toThrow(
+ new UploadError( {
+ code: 'SIZE_ABOVE_LIMIT',
+ message:
+ 'test.jpeg: This file exceeds the maximum upload size for this site.',
+ file: imageFile,
+ } )
+ );
+ } );
+
+ it( 'should not error if the file is below the limit', () => {
+ expect( () => {
+ validateFileSize( imageFile, 100 );
+ } ).not.toThrow(
+ new UploadError( {
+ code: 'SIZE_ABOVE_LIMIT',
+ message:
+ 'test.jpeg: This file exceeds the maximum upload size for this site.',
+ file: imageFile,
+ } )
+ );
+ } );
+
+ it( 'should not error if there is no limit', () => {
+ expect( () => {
+ validateFileSize( imageFile );
+ } ).not.toThrow(
+ new UploadError( {
+ code: 'SIZE_ABOVE_LIMIT',
+ message:
+ 'test.jpeg: This file exceeds the maximum upload size for this site.',
+ file: imageFile,
+ } )
+ );
+ } );
+} );
diff --git a/packages/media-utils/src/utils/test/validate-mime-type-for-user.ts b/packages/media-utils/src/utils/test/validate-mime-type-for-user.ts
new file mode 100644
index 00000000000000..d2566566862142
--- /dev/null
+++ b/packages/media-utils/src/utils/test/validate-mime-type-for-user.ts
@@ -0,0 +1,37 @@
+/**
+ * Internal dependencies
+ */
+import { validateMimeTypeForUser } from '../validate-mime-type-for-user';
+import { UploadError } from '../upload-error';
+
+const imageFile = new window.File( [ 'fake_file' ], 'test.jpeg', {
+ type: 'image/jpeg',
+} );
+
+describe( 'validateMimeTypeForUser', () => {
+ afterEach( () => {
+ jest.clearAllMocks();
+ } );
+
+ it( 'should not error if wpAllowedMimeTypes is null or missing', async () => {
+ expect( () => {
+ validateMimeTypeForUser( imageFile );
+ } ).not.toThrow();
+ expect( () => {
+ validateMimeTypeForUser( imageFile, null );
+ } ).not.toThrow();
+ } );
+
+ it( 'should error if file type is not allowed for user', async () => {
+ expect( () => {
+ validateMimeTypeForUser( imageFile, { aac: 'audio/aac' } );
+ } ).toThrow(
+ new UploadError( {
+ code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER',
+ message:
+ 'test.jpeg: Sorry, you are not allowed to upload this file type.',
+ file: imageFile,
+ } )
+ );
+ } );
+} );
diff --git a/packages/media-utils/src/utils/test/validate-mime-type.ts b/packages/media-utils/src/utils/test/validate-mime-type.ts
new file mode 100644
index 00000000000000..a83cdcefe5f99a
--- /dev/null
+++ b/packages/media-utils/src/utils/test/validate-mime-type.ts
@@ -0,0 +1,57 @@
+/**
+ * Internal dependencies
+ */
+import { validateMimeType } from '../validate-mime-type';
+import { UploadError } from '../upload-error';
+
+const xmlFile = new window.File( [ 'fake_file' ], 'test.xml', {
+ type: 'text/xml',
+} );
+const imageFile = new window.File( [ 'fake_file' ], 'test.jpeg', {
+ type: 'image/jpeg',
+} );
+
+describe( 'validateMimeType', () => {
+ afterEach( () => {
+ jest.clearAllMocks();
+ } );
+
+ it( 'should error if allowedTypes contains a partial mime type and the validation fails', async () => {
+ expect( () => {
+ validateMimeType( xmlFile, [ 'image' ] );
+ } ).toThrow(
+ new UploadError( {
+ code: 'MIME_TYPE_NOT_SUPPORTED',
+ message:
+ 'test.xml: Sorry, this file type is not supported here.',
+ file: xmlFile,
+ } )
+ );
+ } );
+
+ it( 'should error if allowedTypes contains a complete mime type and the validation fails', async () => {
+ expect( () => {
+ validateMimeType( imageFile, [ 'image/gif' ] );
+ } ).toThrow(
+ new UploadError( {
+ code: 'MIME_TYPE_NOT_SUPPORTED',
+ message:
+ 'test.jpeg: Sorry, this file type is not supported here.',
+ file: xmlFile,
+ } )
+ );
+ } );
+
+ it( 'should error if allowedTypes contains multiple types and the validation fails', async () => {
+ expect( () => {
+ validateMimeType( xmlFile, [ 'video', 'image' ] );
+ } ).toThrow(
+ new UploadError( {
+ code: 'MIME_TYPE_NOT_SUPPORTED',
+ message:
+ 'test.xml: Sorry, this file type is not supported here.',
+ file: xmlFile,
+ } )
+ );
+ } );
+} );
diff --git a/packages/media-utils/src/utils/transform-attachment.ts b/packages/media-utils/src/utils/transform-attachment.ts
new file mode 100644
index 00000000000000..a97287872f24af
--- /dev/null
+++ b/packages/media-utils/src/utils/transform-attachment.ts
@@ -0,0 +1,24 @@
+/**
+ * Internal dependencies
+ */
+import type { Attachment, RestAttachment } from './types';
+
+/**
+ * Transforms an attachment object from the REST API shape into the shape expected by the block editor and other consumers.
+ *
+ * @param attachment REST API attachment object.
+ */
+export function transformAttachment( attachment: RestAttachment ): Attachment {
+ // eslint-disable-next-line camelcase
+ const { alt_text, source_url, ...savedMediaProps } = attachment;
+ return {
+ ...savedMediaProps,
+ alt: attachment.alt_text,
+ caption: attachment.caption?.raw ?? '',
+ title: attachment.title.raw,
+ url: attachment.source_url,
+ poster:
+ attachment._embedded?.[ 'wp:featuredmedia' ]?.[ 0 ]?.source_url ||
+ undefined,
+ };
+}
diff --git a/packages/media-utils/src/utils/types.ts b/packages/media-utils/src/utils/types.ts
new file mode 100644
index 00000000000000..e05536400a7604
--- /dev/null
+++ b/packages/media-utils/src/utils/types.ts
@@ -0,0 +1,207 @@
+/**
+ * A media attachment object in a REST API context.
+ *
+ * Simplified version of what's defined in the wp-types package.
+ *
+ * @see https://www.npmjs.com/package/wp-types
+ */
+interface WP_REST_API_Attachment {
+ /**
+ * Unique identifier for the attachment.
+ */
+ id: number;
+ /**
+ * The ID of the featured media for the post.
+ */
+ featured_media: number;
+ /**
+ * URL to the attachment.
+ */
+ link: string;
+ /**
+ * The date the attachment was published, in the site's timezone.
+ */
+ date: string;
+ /**
+ * The date the attachment was published, as GMT.
+ */
+ date_gmt: string;
+ /**
+ * The date the attachment was last modified, in the site's timezone.
+ */
+ modified: string;
+ /**
+ * The date the attachment was last modified, as GMT.
+ */
+ modified_gmt: string;
+ /**
+ * An alphanumeric identifier for the attachment unique to its type.
+ */
+ slug: string;
+ /**
+ * A named status for the attachment.
+ */
+ status: string;
+ /**
+ * Type of Post for the attachment.
+ */
+ type: 'attachment';
+ /**
+ * Alternative text to display when attachment is not displayed.
+ */
+ alt_text: string;
+ /**
+ * The attachment caption.
+ */
+ caption: {
+ /**
+ * Caption for the attachment, as it exists in the database. Only present when using the 'edit' context.
+ */
+ raw?: string;
+ /**
+ * HTML caption for the attachment, transformed for display.
+ */
+ rendered: string;
+ };
+ /**
+ * The attachment description.
+ */
+ description: {
+ /**
+ * Description for the attachment, as it exists in the database. Only present when using the 'edit' context.
+ */
+ raw?: string;
+ /**
+ * HTML description for the attachment, transformed for display.
+ */
+ rendered: string;
+ };
+ /**
+ * Attachment type.
+ */
+ media_type: 'image' | 'file';
+ /**
+ * The attachment MIME type.
+ */
+ mime_type: string;
+ /**
+ * Details about the media file, specific to its type.
+ */
+ media_details: {
+ [ k: string ]: unknown;
+ };
+ /**
+ * The ID for the associated post of the attachment.
+ */
+ post: number | null;
+ /**
+ * URL to the original attachment file.
+ */
+ source_url: string;
+ /**
+ * List of the missing image sizes of the attachment. Only present when using the 'edit' context.
+ */
+ missing_image_sizes?: string[];
+ /**
+ * Permalink template for the attachment. Only present when using the 'edit' context and the post type is public.
+ */
+ permalink_template?: string;
+ /**
+ * Slug automatically generated from the attachment title. Only present when using the 'edit' context and the post type is public.
+ */
+ generated_slug?: string;
+ /**
+ * An array of the class names for the post container element.
+ */
+ class_list: string[];
+ /**
+ * The title for the attachment.
+ */
+ title: {
+ /**
+ * Title for the attachment, as it exists in the database. Only present when using the 'edit' context.
+ */
+ raw?: string;
+ /**
+ * HTML title for the attachment, transformed for display.
+ */
+ rendered: string;
+ };
+ /**
+ * The ID for the author of the attachment.
+ */
+ author: number;
+ /**
+ * Whether or not comments are open on the attachment.
+ */
+ comment_status: string;
+ /**
+ * Whether or not the attachment can be pinged.
+ */
+ ping_status: string;
+ /**
+ * Meta fields.
+ */
+ meta:
+ | []
+ | {
+ [ k: string ]: unknown;
+ };
+ /**
+ * The theme file to use to display the attachment.
+ */
+ template: string;
+ _links: {
+ [ k: string ]: {
+ href: string;
+ embeddable?: boolean;
+ [ k: string ]: unknown;
+ }[];
+ };
+ /**
+ * The embedded representation of relations. Only present when the '_embed' query parameter is set.
+ */
+ _embedded?: {
+ /**
+ * The author of the post.
+ */
+ author: unknown[];
+ /**
+ * The featured image post.
+ */
+ 'wp:featuredmedia'?: WP_REST_API_Attachment[];
+ [ k: string ]: unknown;
+ };
+ [ k: string ]: unknown;
+}
+
+/**
+ * REST API attachment object with additional fields added by this project.
+ */
+export interface RestAttachment extends WP_REST_API_Attachment {}
+
+type BetterOmit< T, K extends PropertyKey > = {
+ [ P in keyof T as P extends K ? never : P ]: T[ P ];
+};
+
+/**
+ * Transformed attachment object.
+ */
+export type Attachment = BetterOmit<
+ RestAttachment,
+ 'alt_text' | 'source_url' | 'caption' | 'title'
+> & {
+ alt: WP_REST_API_Attachment[ 'alt_text' ];
+ caption: WP_REST_API_Attachment[ 'caption' ][ 'raw' ] & string;
+ title: WP_REST_API_Attachment[ 'title' ][ 'raw' ];
+ url: WP_REST_API_Attachment[ 'source_url' ];
+ poster?: WP_REST_API_Attachment[ 'source_url' ];
+};
+
+export type OnChangeHandler = ( attachments: Partial< Attachment >[] ) => void;
+export type OnSuccessHandler = ( attachments: Partial< Attachment >[] ) => void;
+export type OnErrorHandler = ( error: Error ) => void;
+
+export type CreateRestAttachment = Partial< RestAttachment >;
+
+export type AdditionalData = BetterOmit< CreateRestAttachment, 'meta' >;
diff --git a/packages/media-utils/src/utils/upload-error.ts b/packages/media-utils/src/utils/upload-error.ts
new file mode 100644
index 00000000000000..d712e9dcdb6966
--- /dev/null
+++ b/packages/media-utils/src/utils/upload-error.ts
@@ -0,0 +1,26 @@
+interface UploadErrorArgs {
+ code: string;
+ message: string;
+ file: File;
+ cause?: Error;
+}
+
+/**
+ * MediaError class.
+ *
+ * Small wrapper around the `Error` class
+ * to hold an error code and a reference to a file object.
+ */
+export class UploadError extends Error {
+ code: string;
+ file: File;
+
+ constructor( { code, message, file, cause }: UploadErrorArgs ) {
+ super( message, { cause } );
+
+ Object.setPrototypeOf( this, new.target.prototype );
+
+ this.code = code;
+ this.file = file;
+ }
+}
diff --git a/packages/media-utils/src/utils/upload-media.js b/packages/media-utils/src/utils/upload-media.js
deleted file mode 100644
index e3c9b95d5c25cd..00000000000000
--- a/packages/media-utils/src/utils/upload-media.js
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * WordPress dependencies
- */
-import apiFetch from '@wordpress/api-fetch';
-import { createBlobURL, revokeBlobURL } from '@wordpress/blob';
-import { __, sprintf } from '@wordpress/i18n';
-
-const noop = () => {};
-
-/**
- * Browsers may use unexpected mime types, and they differ from browser to browser.
- * This function computes a flexible array of mime types from the mime type structured provided by the server.
- * Converts { jpg|jpeg|jpe: "image/jpeg" } into [ "image/jpeg", "image/jpg", "image/jpeg", "image/jpe" ]
- * The computation of this array instead of directly using the object,
- * solves the problem in chrome where mp3 files have audio/mp3 as mime type instead of audio/mpeg.
- * https://bugs.chromium.org/p/chromium/issues/detail?id=227004
- *
- * @param {?Object} wpMimeTypesObject Mime type object received from the server.
- * Extensions are keys separated by '|' and values are mime types associated with an extension.
- *
- * @return {?Array} An array of mime types or the parameter passed if it was "falsy".
- */
-export function getMimeTypesArray( wpMimeTypesObject ) {
- if ( ! wpMimeTypesObject ) {
- return wpMimeTypesObject;
- }
- return Object.entries( wpMimeTypesObject )
- .map( ( [ extensionsString, mime ] ) => {
- const [ type ] = mime.split( '/' );
- const extensions = extensionsString.split( '|' );
- return [
- mime,
- ...extensions.map(
- ( extension ) => `${ type }/${ extension }`
- ),
- ];
- } )
- .flat();
-}
-
-/**
- * Media Upload is used by audio, image, gallery, video, and file blocks to
- * handle uploading a media file when a file upload button is activated.
- *
- * TODO: future enhancement to add an upload indicator.
- *
- * @param {Object} $0 Parameters object passed to the function.
- * @param {?Array} $0.allowedTypes Array with the types of media that can be uploaded, if unset all types are allowed.
- * @param {?Object} $0.additionalData Additional data to include in the request.
- * @param {Array} $0.filesList List of files.
- * @param {?number} $0.maxUploadFileSize Maximum upload size in bytes allowed for the site.
- * @param {Function} $0.onError Function called when an error happens.
- * @param {Function} $0.onFileChange Function called each time a file or a temporary representation of the file is available.
- * @param {?Object} $0.wpAllowedMimeTypes List of allowed mime types and file extensions.
- */
-export async function uploadMedia( {
- allowedTypes,
- additionalData = {},
- filesList,
- maxUploadFileSize,
- onError = noop,
- onFileChange,
- wpAllowedMimeTypes = null,
-} ) {
- // Cast filesList to array.
- const files = [ ...filesList ];
-
- const filesSet = [];
- const setAndUpdateFiles = ( idx, value ) => {
- revokeBlobURL( filesSet[ idx ]?.url );
- filesSet[ idx ] = value;
- onFileChange( filesSet.filter( Boolean ) );
- };
-
- // Allowed type specified by consumer.
- const isAllowedType = ( fileType ) => {
- if ( ! allowedTypes ) {
- return true;
- }
- return allowedTypes.some( ( allowedType ) => {
- // If a complete mimetype is specified verify if it matches exactly the mime type of the file.
- if ( allowedType.includes( '/' ) ) {
- return allowedType === fileType;
- }
- // Otherwise a general mime type is used and we should verify if the file mimetype starts with it.
- return fileType.startsWith( `${ allowedType }/` );
- } );
- };
-
- // Allowed types for the current WP_User.
- const allowedMimeTypesForUser = getMimeTypesArray( wpAllowedMimeTypes );
- const isAllowedMimeTypeForUser = ( fileType ) => {
- return allowedMimeTypesForUser.includes( fileType );
- };
-
- const validFiles = [];
-
- for ( const mediaFile of files ) {
- // Verify if user is allowed to upload this mime type.
- // Defer to the server when type not detected.
- if (
- allowedMimeTypesForUser &&
- mediaFile.type &&
- ! isAllowedMimeTypeForUser( mediaFile.type )
- ) {
- onError( {
- code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER',
- message: sprintf(
- // translators: %s: file name.
- __(
- '%s: Sorry, you are not allowed to upload this file type.'
- ),
- mediaFile.name
- ),
- file: mediaFile,
- } );
- continue;
- }
-
- // Check if the block supports this mime type.
- // Defer to the server when type not detected.
- if ( mediaFile.type && ! isAllowedType( mediaFile.type ) ) {
- onError( {
- code: 'MIME_TYPE_NOT_SUPPORTED',
- message: sprintf(
- // translators: %s: file name.
- __( '%s: Sorry, this file type is not supported here.' ),
- mediaFile.name
- ),
- file: mediaFile,
- } );
- continue;
- }
-
- // Verify if file is greater than the maximum file upload size allowed for the site.
- if ( maxUploadFileSize && mediaFile.size > maxUploadFileSize ) {
- onError( {
- code: 'SIZE_ABOVE_LIMIT',
- message: sprintf(
- // translators: %s: file name.
- __(
- '%s: This file exceeds the maximum upload size for this site.'
- ),
- mediaFile.name
- ),
- file: mediaFile,
- } );
- continue;
- }
-
- // Don't allow empty files to be uploaded.
- if ( mediaFile.size <= 0 ) {
- onError( {
- code: 'EMPTY_FILE',
- message: sprintf(
- // translators: %s: file name.
- __( '%s: This file is empty.' ),
- mediaFile.name
- ),
- file: mediaFile,
- } );
- continue;
- }
-
- validFiles.push( mediaFile );
-
- // Set temporary URL to create placeholder media file, this is replaced
- // with final file from media gallery when upload is `done` below.
- filesSet.push( { url: createBlobURL( mediaFile ) } );
- onFileChange( filesSet );
- }
-
- for ( let idx = 0; idx < validFiles.length; ++idx ) {
- const mediaFile = validFiles[ idx ];
- try {
- const savedMedia = await createMediaFromFile(
- mediaFile,
- additionalData
- );
- // eslint-disable-next-line camelcase
- const { alt_text, source_url, ...savedMediaProps } = savedMedia;
- const mediaObject = {
- ...savedMediaProps,
- alt: savedMedia.alt_text,
- caption: savedMedia.caption?.raw ?? '',
- title: savedMedia.title.raw,
- url: savedMedia.source_url,
- };
- setAndUpdateFiles( idx, mediaObject );
- } catch ( error ) {
- // Reset to empty on failure.
- setAndUpdateFiles( idx, null );
- let message;
- if ( error.message ) {
- message = error.message;
- } else {
- message = sprintf(
- // translators: %s: file name
- __( 'Error while uploading file %s to the media library.' ),
- mediaFile.name
- );
- }
- onError( {
- code: 'GENERAL',
- message,
- file: mediaFile,
- } );
- }
- }
-}
-
-/**
- * @param {File} file Media File to Save.
- * @param {?Object} additionalData Additional data to include in the request.
- *
- * @return {Promise} Media Object Promise.
- */
-function createMediaFromFile( file, additionalData ) {
- // Create upload payload.
- const data = new window.FormData();
- data.append( 'file', file, file.name || file.type.replace( '/', '.' ) );
- if ( additionalData ) {
- Object.entries( additionalData ).forEach( ( [ key, value ] ) =>
- data.append( key, value )
- );
- }
- return apiFetch( {
- path: '/wp/v2/media',
- body: data,
- method: 'POST',
- } );
-}
diff --git a/packages/media-utils/src/utils/upload-media.ts b/packages/media-utils/src/utils/upload-media.ts
new file mode 100644
index 00000000000000..1bc861cfb3b607
--- /dev/null
+++ b/packages/media-utils/src/utils/upload-media.ts
@@ -0,0 +1,149 @@
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+import { createBlobURL, revokeBlobURL } from '@wordpress/blob';
+
+/**
+ * Internal dependencies
+ */
+import type {
+ AdditionalData,
+ Attachment,
+ OnChangeHandler,
+ OnErrorHandler,
+ OnSuccessHandler,
+} from './types';
+import { uploadToServer } from './upload-to-server';
+import { validateMimeType } from './validate-mime-type';
+import { validateMimeTypeForUser } from './validate-mime-type-for-user';
+import { validateFileSize } from './validate-file-size';
+import { UploadError } from './upload-error';
+
+interface UploadMediaArgs {
+ // Additional data to include in the request.
+ additionalData?: AdditionalData;
+ // Array with the types of media that can be uploaded, if unset all types are allowed.
+ allowedTypes?: string[];
+ // List of files.
+ filesList: File[];
+ // Maximum upload size in bytes allowed for the site.
+ maxUploadFileSize?: number;
+ // Function called when an error happens.
+ onError?: OnErrorHandler;
+ // Function called each time a file or a temporary representation of the file is available.
+ onFileChange?: OnChangeHandler;
+ // Function called once a file has completely finished uploading, including thumbnails.
+ onSuccess?: OnSuccessHandler;
+ // List of allowed mime types and file extensions.
+ wpAllowedMimeTypes?: Record< string, string > | null;
+ // Abort signal.
+ signal?: AbortSignal;
+}
+
+/**
+ * Upload a media file when the file upload button is activated
+ * or when adding a file to the editor via drag & drop.
+ *
+ * @param $0 Parameters object passed to the function.
+ * @param $0.allowedTypes Array with the types of media that can be uploaded, if unset all types are allowed.
+ * @param $0.additionalData Additional data to include in the request.
+ * @param $0.filesList List of files.
+ * @param $0.maxUploadFileSize Maximum upload size in bytes allowed for the site.
+ * @param $0.onError Function called when an error happens.
+ * @param $0.onFileChange Function called each time a file or a temporary representation of the file is available.
+ * @param $0.wpAllowedMimeTypes List of allowed mime types and file extensions.
+ * @param $0.signal Abort signal.
+ */
+export function uploadMedia( {
+ wpAllowedMimeTypes,
+ allowedTypes,
+ additionalData = {},
+ filesList,
+ maxUploadFileSize,
+ onError,
+ onFileChange,
+ signal,
+}: UploadMediaArgs ) {
+ const validFiles = [];
+
+ const filesSet: Array< Partial< Attachment > | null > = [];
+ const setAndUpdateFiles = ( index: number, value: Attachment | null ) => {
+ if ( filesSet[ index ]?.url ) {
+ revokeBlobURL( filesSet[ index ].url );
+ }
+ filesSet[ index ] = value;
+ onFileChange?.(
+ filesSet.filter( ( attachment ) => attachment !== null )
+ );
+ };
+
+ for ( const mediaFile of filesList ) {
+ // Verify if user is allowed to upload this mime type.
+ // Defer to the server when type not detected.
+ try {
+ validateMimeTypeForUser( mediaFile, wpAllowedMimeTypes );
+ } catch ( error: unknown ) {
+ onError?.( error as Error );
+ continue;
+ }
+
+ // Check if the caller (e.g. a block) supports this mime type.
+ // Defer to the server when type not detected.
+ try {
+ validateMimeType( mediaFile, allowedTypes );
+ } catch ( error: unknown ) {
+ onError?.( error as Error );
+ continue;
+ }
+
+ // Verify if file is greater than the maximum file upload size allowed for the site.
+ try {
+ validateFileSize( mediaFile, maxUploadFileSize );
+ } catch ( error: unknown ) {
+ onError?.( error as Error );
+ continue;
+ }
+
+ validFiles.push( mediaFile );
+
+ // Set temporary URL to create placeholder media file, this is replaced
+ // with final file from media gallery when upload is `done` below.
+ filesSet.push( { url: createBlobURL( mediaFile ) } );
+ onFileChange?.( filesSet as Array< Partial< Attachment > > );
+ }
+
+ validFiles.map( async ( file, index ) => {
+ try {
+ const attachment = await uploadToServer(
+ file,
+ additionalData,
+ signal
+ );
+ setAndUpdateFiles( index, attachment );
+ } catch ( error ) {
+ // Reset to empty on failure.
+ setAndUpdateFiles( index, null );
+
+ let message;
+ if ( error instanceof Error ) {
+ message = error.message;
+ } else {
+ message = sprintf(
+ // translators: %s: file name
+ __( 'Error while uploading file %s to the media library.' ),
+ file.name
+ );
+ }
+
+ onError?.(
+ new UploadError( {
+ code: 'GENERAL',
+ message,
+ file,
+ cause: error instanceof Error ? error : undefined,
+ } )
+ );
+ }
+ } );
+}
diff --git a/packages/media-utils/src/utils/upload-to-server.ts b/packages/media-utils/src/utils/upload-to-server.ts
new file mode 100644
index 00000000000000..7aa4243d5ccfd4
--- /dev/null
+++ b/packages/media-utils/src/utils/upload-to-server.ts
@@ -0,0 +1,38 @@
+/**
+ * WordPress dependencies
+ */
+import apiFetch from '@wordpress/api-fetch';
+
+/**
+ * Internal dependencies
+ */
+import { flattenFormData } from './flatten-form-data';
+import { transformAttachment } from './transform-attachment';
+import type { CreateRestAttachment, RestAttachment } from './types';
+
+export async function uploadToServer(
+ file: File,
+ additionalData: CreateRestAttachment = {},
+ signal?: AbortSignal
+) {
+ // Create upload payload.
+ const data = new FormData();
+ data.append( 'file', file, file.name || file.type.replace( '/', '.' ) );
+ for ( const [ key, value ] of Object.entries( additionalData ) ) {
+ flattenFormData(
+ data,
+ key,
+ value as string | Record< string, string > | undefined
+ );
+ }
+
+ return transformAttachment(
+ await apiFetch< RestAttachment >( {
+ // This allows the video block to directly get a video's poster image.
+ path: '/wp/v2/media?_embed=wp:featuredmedia',
+ body: data,
+ method: 'POST',
+ signal,
+ } )
+ );
+}
diff --git a/packages/media-utils/src/utils/validate-file-size.ts b/packages/media-utils/src/utils/validate-file-size.ts
new file mode 100644
index 00000000000000..cc34462b268dda
--- /dev/null
+++ b/packages/media-utils/src/utils/validate-file-size.ts
@@ -0,0 +1,44 @@
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { UploadError } from './upload-error';
+
+/**
+ * Verifies whether the file is within the file upload size limits for the site.
+ *
+ * @param file File object.
+ * @param maxUploadFileSize Maximum upload size in bytes allowed for the site.
+ */
+export function validateFileSize( file: File, maxUploadFileSize?: number ) {
+ // Don't allow empty files to be uploaded.
+ if ( file.size <= 0 ) {
+ throw new UploadError( {
+ code: 'EMPTY_FILE',
+ message: sprintf(
+ // translators: %s: file name.
+ __( '%s: This file is empty.' ),
+ file.name
+ ),
+ file,
+ } );
+ }
+
+ if ( maxUploadFileSize && file.size > maxUploadFileSize ) {
+ throw new UploadError( {
+ code: 'SIZE_ABOVE_LIMIT',
+ message: sprintf(
+ // translators: %s: file name.
+ __(
+ '%s: This file exceeds the maximum upload size for this site.'
+ ),
+ file.name
+ ),
+ file,
+ } );
+ }
+}
diff --git a/packages/media-utils/src/utils/validate-mime-type-for-user.ts b/packages/media-utils/src/utils/validate-mime-type-for-user.ts
new file mode 100644
index 00000000000000..858c583561978e
--- /dev/null
+++ b/packages/media-utils/src/utils/validate-mime-type-for-user.ts
@@ -0,0 +1,46 @@
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { UploadError } from './upload-error';
+import { getMimeTypesArray } from './get-mime-types-array';
+
+/**
+ * Verifies if the user is allowed to upload this mime type.
+ *
+ * @param file File object.
+ * @param wpAllowedMimeTypes List of allowed mime types and file extensions.
+ */
+export function validateMimeTypeForUser(
+ file: File,
+ wpAllowedMimeTypes?: Record< string, string > | null
+) {
+ // Allowed types for the current WP_User.
+ const allowedMimeTypesForUser = getMimeTypesArray( wpAllowedMimeTypes );
+
+ if ( ! allowedMimeTypesForUser ) {
+ return;
+ }
+
+ const isAllowedMimeTypeForUser = allowedMimeTypesForUser.includes(
+ file.type
+ );
+
+ if ( file.type && ! isAllowedMimeTypeForUser ) {
+ throw new UploadError( {
+ code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER',
+ message: sprintf(
+ // translators: %s: file name.
+ __(
+ '%s: Sorry, you are not allowed to upload this file type.'
+ ),
+ file.name
+ ),
+ file,
+ } );
+ }
+}
diff --git a/packages/media-utils/src/utils/validate-mime-type.ts b/packages/media-utils/src/utils/validate-mime-type.ts
new file mode 100644
index 00000000000000..2d99455d7b60f1
--- /dev/null
+++ b/packages/media-utils/src/utils/validate-mime-type.ts
@@ -0,0 +1,43 @@
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { UploadError } from './upload-error';
+
+/**
+ * Verifies if the caller (e.g. a block) supports this mime type.
+ *
+ * @param file File object.
+ * @param allowedTypes List of allowed mime types.
+ */
+export function validateMimeType( file: File, allowedTypes?: string[] ) {
+ if ( ! allowedTypes ) {
+ return;
+ }
+
+ // Allowed type specified by consumer.
+ const isAllowedType = allowedTypes.some( ( allowedType ) => {
+ // If a complete mimetype is specified verify if it matches exactly the mime type of the file.
+ if ( allowedType.includes( '/' ) ) {
+ return allowedType === file.type;
+ }
+ // Otherwise a general mime type is used, and we should verify if the file mimetype starts with it.
+ return file.type.startsWith( `${ allowedType }/` );
+ } );
+
+ if ( file.type && ! isAllowedType ) {
+ throw new UploadError( {
+ code: 'MIME_TYPE_NOT_SUPPORTED',
+ message: sprintf(
+ // translators: %s: file name.
+ __( '%s: Sorry, this file type is not supported here.' ),
+ file.name
+ ),
+ file,
+ } );
+ }
+}
diff --git a/packages/media-utils/tsconfig.json b/packages/media-utils/tsconfig.json
new file mode 100644
index 00000000000000..8559f1507b7235
--- /dev/null
+++ b/packages/media-utils/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig.json",
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "declarationDir": "build-types",
+ "types": [ "gutenberg-env" ],
+ "checkJs": false
+ },
+ "include": [ "src/**/*" ],
+ "references": [
+ { "path": "../api-fetch" },
+ { "path": "../blob" },
+ { "path": "../element" },
+ { "path": "../i18n" }
+ ]
+}
diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md
index 32d51970c2141d..f32648df69728e 100644
--- a/packages/notices/CHANGELOG.md
+++ b/packages/notices/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/notices/package.json b/packages/notices/package.json
index 46715eb74a891f..a4e2c5ea8d98c1 100644
--- a/packages/notices/package.json
+++ b/packages/notices/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/notices",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "State management for notices.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/npm-package-json-lint-config/CHANGELOG.md b/packages/npm-package-json-lint-config/CHANGELOG.md
index cfb2ea682ba456..24dfd09fff55a2 100644
--- a/packages/npm-package-json-lint-config/CHANGELOG.md
+++ b/packages/npm-package-json-lint-config/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json
index 1acbcdede95aba..ad88e3fd442a1d 100644
--- a/packages/npm-package-json-lint-config/package.json
+++ b/packages/npm-package-json-lint-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/npm-package-json-lint-config",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "WordPress npm-package-json-lint shareable configuration.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md
index 90c3e96deb80aa..85feaa73dbc239 100644
--- a/packages/nux/CHANGELOG.md
+++ b/packages/nux/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 9.7.0 (2024-09-05)
+
## 9.6.0 (2024-08-21)
## 9.5.0 (2024-08-07)
diff --git a/packages/nux/package.json b/packages/nux/package.json
index 0997a29878bee6..7a8e565d163aa2 100644
--- a/packages/nux/package.json
+++ b/packages/nux/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/nux",
- "version": "9.6.0",
+ "version": "9.7.0",
"description": "NUX (New User eXperience) module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/patterns/CHANGELOG.md b/packages/patterns/CHANGELOG.md
index 612586efb30a5b..eaef3c431d6687 100644
--- a/packages/patterns/CHANGELOG.md
+++ b/packages/patterns/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 2.7.0 (2024-09-05)
+
## 2.6.0 (2024-08-21)
## 2.5.0 (2024-08-07)
diff --git a/packages/patterns/package.json b/packages/patterns/package.json
index 5ca0cdbe5b9df5..6d67aa7ef3ac9e 100644
--- a/packages/patterns/package.json
+++ b/packages/patterns/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/patterns",
- "version": "2.6.0",
+ "version": "2.7.0",
"description": "Management of user pattern editing.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/patterns/src/components/style.scss b/packages/patterns/src/components/style.scss
index 30fe0300503c48..1b053ab7ac2517 100644
--- a/packages/patterns/src/components/style.scss
+++ b/packages/patterns/src/components/style.scss
@@ -13,8 +13,8 @@
.components-form-token-field__suggestions-list:not(:empty) {
position: absolute;
border: $border-width solid var(--wp-admin-theme-color);
- border-bottom-left-radius: $radius-block-ui;
- border-bottom-right-radius: $radius-block-ui;
+ border-bottom-left-radius: $radius-small;
+ border-bottom-right-radius: $radius-small;
box-shadow: 0 0 0.5px 0.5px var(--wp-admin-theme-color);
box-sizing: border-box;
z-index: 1;
diff --git a/packages/plugins/CHANGELOG.md b/packages/plugins/CHANGELOG.md
index 14b9e4cffa4ad8..c2c010c8881150 100644
--- a/packages/plugins/CHANGELOG.md
+++ b/packages/plugins/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 7.7.0 (2024-09-05)
+
## 7.6.0 (2024-08-21)
## 7.5.0 (2024-08-07)
diff --git a/packages/plugins/package.json b/packages/plugins/package.json
index c5bf069bfaa170..9cc58fc5096be2 100644
--- a/packages/plugins/package.json
+++ b/packages/plugins/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/plugins",
- "version": "7.6.0",
+ "version": "7.7.0",
"description": "Plugins module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/postcss-plugins-preset/CHANGELOG.md b/packages/postcss-plugins-preset/CHANGELOG.md
index 1811914b5bd884..b16c9284417a31 100644
--- a/packages/postcss-plugins-preset/CHANGELOG.md
+++ b/packages/postcss-plugins-preset/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json
index 892586a194e6fc..d012dde494958e 100644
--- a/packages/postcss-plugins-preset/package.json
+++ b/packages/postcss-plugins-preset/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/postcss-plugins-preset",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "PostCSS sharable plugins preset for WordPress development.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/postcss-themes/CHANGELOG.md b/packages/postcss-themes/CHANGELOG.md
index 11da169548988e..774c2edf77e0c3 100644
--- a/packages/postcss-themes/CHANGELOG.md
+++ b/packages/postcss-themes/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json
index 57234c6f8d7fb7..29e617382161ed 100644
--- a/packages/postcss-themes/package.json
+++ b/packages/postcss-themes/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/postcss-themes",
- "version": "6.6.0",
+ "version": "6.7.0",
"description": "PostCSS plugin to generate theme colors.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/preferences-persistence/CHANGELOG.md b/packages/preferences-persistence/CHANGELOG.md
index 5b280b336f051c..a4a3081901bf52 100644
--- a/packages/preferences-persistence/CHANGELOG.md
+++ b/packages/preferences-persistence/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 2.7.0 (2024-09-05)
+
## 2.6.0 (2024-08-21)
## 2.5.0 (2024-08-07)
diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json
index f994ca28f63012..a318979abed2a1 100644
--- a/packages/preferences-persistence/package.json
+++ b/packages/preferences-persistence/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/preferences-persistence",
- "version": "2.6.0",
+ "version": "2.7.0",
"description": "Persistence utilities for `wordpress/preferences`.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/preferences/CHANGELOG.md b/packages/preferences/CHANGELOG.md
index 5a71145f2396d3..7d87ce93868757 100644
--- a/packages/preferences/CHANGELOG.md
+++ b/packages/preferences/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/preferences/package.json b/packages/preferences/package.json
index e39668e6ebe5d0..5b12d274af30ae 100644
--- a/packages/preferences/package.json
+++ b/packages/preferences/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/preferences",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Utilities for managing WordPress preferences.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/preferences/src/components/preferences-modal-tabs/index.js b/packages/preferences/src/components/preferences-modal-tabs/index.js
index f70430367cc617..d87e565f5e3364 100644
--- a/packages/preferences/src/components/preferences-modal-tabs/index.js
+++ b/packages/preferences/src/components/preferences-modal-tabs/index.js
@@ -110,7 +110,7 @@ export default function PreferencesModalTabs( { sections } ) {
return (
@@ -142,7 +142,7 @@ export default function PreferencesModalTabs( { sections } ) {
return (
{
- this.constructor.paths = getPhpFilePaths( this.options.props );
- } );
- }
-}
-
const cssLoaders = [
{
loader: MiniCSSExtractPlugin.loader,
@@ -345,7 +301,10 @@ const scriptConfig = {
cleanStaleWebpackAssets: false,
} ),
- new PhpFilePathsPlugin( { props: [ 'render', 'variations' ] } ),
+ new PhpFilePathsPlugin( {
+ context: getWordPressSrcDirectory(),
+ props: [ 'render', 'variations' ],
+ } ),
new CopyWebpackPlugin( {
patterns: [
{
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index c4582ba65d15e9..6661e9876683fa 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/scripts",
- "version": "28.6.0",
+ "version": "29.0.0",
"description": "Collection of reusable scripts for WordPress development.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
@@ -76,7 +76,7 @@
"postcss-import": "^16.1.0",
"postcss-loader": "^6.2.1",
"prettier": "npm:wp-prettier@3.0.3",
- "puppeteer-core": "^13.2.0",
+ "puppeteer-core": "^23.1.0",
"react-refresh": "^0.14.0",
"read-pkg-up": "^7.0.1",
"resolve-bin": "^0.4.0",
@@ -94,7 +94,7 @@
"webpack-dev-server": "^4.15.1"
},
"peerDependencies": {
- "@playwright/test": "^1.46.0",
+ "@playwright/test": "^1.47.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
diff --git a/packages/scripts/utils/config.js b/packages/scripts/utils/config.js
index 7196c0376377d4..dfb44730438c4a 100644
--- a/packages/scripts/utils/config.js
+++ b/packages/scripts/utils/config.js
@@ -351,22 +351,23 @@ function getWebpackEntryPoints( buildType ) {
/**
* Returns the list of PHP file paths found in `block.json` files for the given props.
*
- * @param {string[]} props The props to search for in the `block.json` files.
+ * @param {string} context The path to search for `block.json` files.
+ * @param {string[]} props The props to search for in the `block.json` files.
* @return {string[]} The list of PHP file paths.
*/
-function getPhpFilePaths( props ) {
+function getPhpFilePaths( context, props ) {
// Continue only if the source directory exists.
- if ( ! hasProjectFile( getWordPressSrcDirectory() ) ) {
+ if ( ! hasProjectFile( context ) ) {
return [];
}
// Checks whether any block metadata files can be detected in the defined source directory.
const blockMetadataFiles = glob( '**/block.json', {
absolute: true,
- cwd: fromProjectRoot( getWordPressSrcDirectory() ),
+ cwd: fromProjectRoot( context ),
} );
- const srcDirectory = fromProjectRoot( getWordPressSrcDirectory() + sep );
+ const srcDirectory = fromProjectRoot( context + sep );
return blockMetadataFiles.flatMap( ( blockMetadataFile ) => {
const blockJson = JSON.parse( readFileSync( blockMetadataFile ) );
@@ -396,7 +397,7 @@ function getPhpFilePaths( props ) {
) }" listed in "${ blockMetadataFile.replace(
fromProjectRoot( sep ),
''
- ) }". File is located outside of the "${ getWordPressSrcDirectory() }" directory.`
+ ) }". File is located outside of the "${ context }" directory.`
)
);
continue;
diff --git a/packages/scripts/utils/index.js b/packages/scripts/utils/index.js
index dc4008b16197d6..cb7e592f83d554 100644
--- a/packages/scripts/utils/index.js
+++ b/packages/scripts/utils/index.js
@@ -29,6 +29,7 @@ const {
getBlockJsonModuleFields,
getBlockJsonScriptFields,
} = require( './block-json' );
+const { PhpFilePathsPlugin } = require( './php-file-paths-plugin' );
module.exports = {
fromProjectRoot,
@@ -55,5 +56,6 @@ module.exports = {
hasPostCSSConfig,
hasPrettierConfig,
hasProjectFile,
+ PhpFilePathsPlugin,
spawnScript,
};
diff --git a/packages/scripts/utils/php-file-paths-plugin.js b/packages/scripts/utils/php-file-paths-plugin.js
new file mode 100644
index 00000000000000..6f95dae6505a80
--- /dev/null
+++ b/packages/scripts/utils/php-file-paths-plugin.js
@@ -0,0 +1,60 @@
+/**
+ * External dependencies
+ */
+const { validate } = require( 'schema-utils' );
+
+/**
+ * Internal dependencies
+ */
+const { getPhpFilePaths } = require( './config' );
+
+const phpFilePathsPluginSchema = {
+ type: 'object',
+ properties: {
+ context: {
+ type: 'string',
+ },
+ props: {
+ type: 'array',
+ items: {
+ type: 'string',
+ },
+ },
+ },
+};
+
+/**
+ * The plugin recomputes PHP file paths once on each compilation. It is necessary to avoid repeating processing
+ * when filtering every discovered PHP file in the source folder. This is the most performant way to ensure that
+ * changes in `block.json` files are picked up in watch mode.
+ */
+class PhpFilePathsPlugin {
+ /**
+ * PHP file paths from `render` and `variations` props found in `block.json` files.
+ *
+ * @type {string[]}
+ */
+ static paths;
+
+ constructor( options = {} ) {
+ validate( phpFilePathsPluginSchema, options, {
+ name: 'PHP File Paths Plugin',
+ baseDataPath: 'options',
+ } );
+
+ this.options = options;
+ }
+
+ apply( compiler ) {
+ const pluginName = this.constructor.name;
+
+ compiler.hooks.thisCompilation.tap( pluginName, () => {
+ this.constructor.paths = getPhpFilePaths(
+ this.options.context,
+ this.options.props
+ );
+ } );
+ }
+}
+
+module.exports = { PhpFilePathsPlugin };
diff --git a/packages/server-side-render/CHANGELOG.md b/packages/server-side-render/CHANGELOG.md
index 6246bff21cb488..d576f09c8e34b7 100644
--- a/packages/server-side-render/CHANGELOG.md
+++ b/packages/server-side-render/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 5.7.0 (2024-09-05)
+
## 5.6.0 (2024-08-21)
## 5.5.0 (2024-08-07)
diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json
index 054e8b938ce845..9ace449e3fe966 100644
--- a/packages/server-side-render/package.json
+++ b/packages/server-side-render/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/server-side-render",
- "version": "5.6.0",
+ "version": "5.7.0",
"description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/shortcode/CHANGELOG.md b/packages/shortcode/CHANGELOG.md
index eeb738b9e49123..6abdd3c52f6d74 100644
--- a/packages/shortcode/CHANGELOG.md
+++ b/packages/shortcode/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json
index 42ca3458d72a5b..10e368d06c9955 100644
--- a/packages/shortcode/package.json
+++ b/packages/shortcode/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/shortcode",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Shortcode module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/style-engine/CHANGELOG.md b/packages/style-engine/CHANGELOG.md
index b8462204ea958c..a5292bfdd6c191 100644
--- a/packages/style-engine/CHANGELOG.md
+++ b/packages/style-engine/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 2.7.0 (2024-09-05)
+
## 2.6.0 (2024-08-21)
### New Features
diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json
index 91409072cb6503..982ac1c1fa89db 100644
--- a/packages/style-engine/package.json
+++ b/packages/style-engine/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/style-engine",
- "version": "2.6.0",
+ "version": "2.7.0",
"description": "A suite of parsers and compilers for WordPress styles.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/stylelint-config/CHANGELOG.md b/packages/stylelint-config/CHANGELOG.md
index 4df376a34b0986..9cb29690af289e 100644
--- a/packages/stylelint-config/CHANGELOG.md
+++ b/packages/stylelint-config/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 22.7.0 (2024-09-05)
+
## 22.6.0 (2024-08-21)
## 22.5.0 (2024-08-07)
diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json
index aab9f8e8c3b417..19e6f19bfd2f87 100644
--- a/packages/stylelint-config/package.json
+++ b/packages/stylelint-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/stylelint-config",
- "version": "22.6.0",
+ "version": "22.7.0",
"description": "stylelint config for WordPress development.",
"author": "The WordPress Contributors",
"license": "MIT",
diff --git a/packages/sync/CHANGELOG.md b/packages/sync/CHANGELOG.md
index b38fb8b48945cc..071974be2a3a88 100644
--- a/packages/sync/CHANGELOG.md
+++ b/packages/sync/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 1.7.0 (2024-09-05)
+
## 1.6.0 (2024-08-21)
## 1.5.0 (2024-08-07)
diff --git a/packages/sync/package.json b/packages/sync/package.json
index 703de650e3f242..ba8c433e880c3b 100644
--- a/packages/sync/package.json
+++ b/packages/sync/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/sync",
- "version": "1.6.0",
+ "version": "1.7.0",
"description": "Sync Data.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/token-list/CHANGELOG.md b/packages/token-list/CHANGELOG.md
index 97b674024e637d..7258699a5322ae 100644
--- a/packages/token-list/CHANGELOG.md
+++ b/packages/token-list/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 3.7.0 (2024-09-05)
+
## 3.6.0 (2024-08-21)
## 3.5.0 (2024-08-07)
diff --git a/packages/token-list/package.json b/packages/token-list/package.json
index d40820a1f73a87..1a06b76ccb111b 100644
--- a/packages/token-list/package.json
+++ b/packages/token-list/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/token-list",
- "version": "3.6.0",
+ "version": "3.7.0",
"description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/undo-manager/CHANGELOG.md b/packages/undo-manager/CHANGELOG.md
index 63338342dfe54c..cbe86921a489d2 100644
--- a/packages/undo-manager/CHANGELOG.md
+++ b/packages/undo-manager/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 1.7.0 (2024-09-05)
+
## 1.6.0 (2024-08-21)
## 1.5.0 (2024-08-07)
diff --git a/packages/undo-manager/package.json b/packages/undo-manager/package.json
index 3607f6f6a990ca..4574d94f46d7e1 100644
--- a/packages/undo-manager/package.json
+++ b/packages/undo-manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/undo-manager",
- "version": "1.6.0",
+ "version": "1.7.0",
"description": "A small package to manage undo/redo.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md
index f9af22f9fa7447..fc3082dd1bd0c6 100644
--- a/packages/url/CHANGELOG.md
+++ b/packages/url/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/url/README.md b/packages/url/README.md
index 70e8579acc5182..0e4628669d11f2 100644
--- a/packages/url/README.md
+++ b/packages/url/README.md
@@ -322,6 +322,24 @@ _Returns_
- `boolean`: Whether or not it looks like an email.
+### isPhoneNumber
+
+Determines whether the given string looks like a phone number.
+
+_Usage_
+
+```js
+const isPhoneNumber = isPhoneNumber( '+1 (555) 123-4567' ); // true
+```
+
+_Parameters_
+
+- _phoneNumber_ `string`: The string to scrutinize.
+
+_Returns_
+
+- `boolean`: Whether or not it looks like a phone number.
+
### isURL
Determines whether the given string looks like a URL.
diff --git a/packages/url/package.json b/packages/url/package.json
index e83c05e594e5f9..c8360c81008fa2 100644
--- a/packages/url/package.json
+++ b/packages/url/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/url",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "WordPress URL utilities.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/url/src/index.js b/packages/url/src/index.js
index 141e19d3fa55fd..378334ba131a6a 100644
--- a/packages/url/src/index.js
+++ b/packages/url/src/index.js
@@ -1,5 +1,6 @@
export { isURL } from './is-url';
export { isEmail } from './is-email';
+export { isPhoneNumber } from './is-phone-number';
export { getProtocol } from './get-protocol';
export { isValidProtocol } from './is-valid-protocol';
export { getAuthority } from './get-authority';
diff --git a/packages/url/src/is-phone-number.js b/packages/url/src/is-phone-number.js
new file mode 100644
index 00000000000000..857b468bc52398
--- /dev/null
+++ b/packages/url/src/is-phone-number.js
@@ -0,0 +1,19 @@
+const PHONE_REGEXP = /^(tel:)?(\+)?\d{6,15}$/;
+
+/**
+ * Determines whether the given string looks like a phone number.
+ *
+ * @param {string} phoneNumber The string to scrutinize.
+ *
+ * @example
+ * ```js
+ * const isPhoneNumber = isPhoneNumber('+1 (555) 123-4567'); // true
+ * ```
+ *
+ * @return {boolean} Whether or not it looks like a phone number.
+ */
+export function isPhoneNumber( phoneNumber ) {
+ // Remove any seperator from phone number.
+ phoneNumber = phoneNumber.replace( /[-.() ]/g, '' );
+ return PHONE_REGEXP.test( phoneNumber );
+}
diff --git a/packages/url/src/test/index.js b/packages/url/src/test/index.js
index bd48105baed96d..4fc3d5e2970d67 100644
--- a/packages/url/src/test/index.js
+++ b/packages/url/src/test/index.js
@@ -17,6 +17,7 @@ import {
hasQueryArg,
isEmail,
isURL,
+ isPhoneNumber,
isValidAuthority,
isValidFragment,
isValidPath,
@@ -73,6 +74,52 @@ describe( 'isEmail', () => {
);
} );
+describe( 'isPhoneNumber', () => {
+ it.each( [
+ '+1 (555) 123-4567',
+ '(555) 123-4567',
+ '555-123-4567',
+ '5551234567',
+ '+91 987 654 3210',
+ '123-456-7890',
+ '(123) 456-7890',
+ '123 456 7890',
+ '123.456.7890',
+ '+1 123 456 7890',
+ '1234567890',
+ '+44 791 112 3456',
+ '(123) 4567',
+ '+1 (123) 45678901',
+ '12-34-56',
+ '123456789012345',
+ '+12 3456789012345',
+ 'tel:+1-123-456-7890',
+ ] )(
+ 'returns true when given things that look like a phone number: %s',
+ ( phoneNumber ) => {
+ expect( isPhoneNumber( phoneNumber ) ).toBe( true );
+ }
+ );
+
+ it.each( [
+ 'not a phone number',
+ '123',
+ '1234',
+ '12345',
+ '+91 123',
+ 'abc-def-ghij',
+ 'a123456789b',
+ '12-34-5',
+ 'tel:911',
+ 'tel:12345',
+ ] )(
+ "returns false when given things that don't look like a phone number: %s",
+ ( phoneNumber ) => {
+ expect( isPhoneNumber( phoneNumber ) ).toBe( false );
+ }
+ );
+} );
+
describe( 'getProtocol', () => {
it( 'returns the protocol part of a URL', () => {
expect(
diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md
index 8ce0b7b1ffb750..1bc96013d724bb 100644
--- a/packages/viewport/CHANGELOG.md
+++ b/packages/viewport/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 6.7.0 (2024-09-05)
+
## 6.6.0 (2024-08-21)
## 6.5.0 (2024-08-07)
diff --git a/packages/viewport/package.json b/packages/viewport/package.json
index 7575c63256b632..69fe9c8695c6ce 100644
--- a/packages/viewport/package.json
+++ b/packages/viewport/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/viewport",
- "version": "6.6.0",
+ "version": "6.7.0",
"description": "Viewport module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/warning/CHANGELOG.md b/packages/warning/CHANGELOG.md
index ca176032261544..bb7df7b6330625 100644
--- a/packages/warning/CHANGELOG.md
+++ b/packages/warning/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 3.7.0 (2024-09-05)
+
## 3.6.0 (2024-08-21)
## 3.5.0 (2024-08-07)
diff --git a/packages/warning/package.json b/packages/warning/package.json
index b18ad5ffc51678..1095d2efd75621 100644
--- a/packages/warning/package.json
+++ b/packages/warning/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/warning",
- "version": "3.6.0",
+ "version": "3.7.0",
"description": "Warning utility for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/widgets/CHANGELOG.md b/packages/widgets/CHANGELOG.md
index e95c3904f97e1e..be1af839314954 100644
--- a/packages/widgets/CHANGELOG.md
+++ b/packages/widgets/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index c7fbb82eb0cb1d..595bad0f733652 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/widgets",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/widgets/src/blocks/legacy-widget/editor.scss b/packages/widgets/src/blocks/legacy-widget/editor.scss
index 6b04ce0a407e43..8c213eec86f9fa 100644
--- a/packages/widgets/src/blocks/legacy-widget/editor.scss
+++ b/packages/widgets/src/blocks/legacy-widget/editor.scss
@@ -3,7 +3,7 @@
display: flow-root;
}
background: $white;
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
border: $border-width solid $gray-900;
padding: $grid-unit-15 - $border-width;
max-height: calc(100vh - #{ $border-width } - #{ $border-width });
@@ -95,7 +95,7 @@
cursor: pointer;
&:hover::after {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
border: $border-width solid $gray-600;
bottom: 0;
content: "";
diff --git a/packages/widgets/src/blocks/widget-group/editor.scss b/packages/widgets/src/blocks/widget-group/editor.scss
index 38798ef34c7c6e..1cd179539bc1a9 100644
--- a/packages/widgets/src/blocks/widget-group/editor.scss
+++ b/packages/widgets/src/blocks/widget-group/editor.scss
@@ -1,6 +1,6 @@
.wp-block-widget-group {
&.has-child-selected::after {
- border-radius: $radius-block-ui;
+ border-radius: $radius-small;
border: $border-width solid var(--wp-admin-theme-color);
bottom: 0;
content: "";
diff --git a/packages/wordcount/CHANGELOG.md b/packages/wordcount/CHANGELOG.md
index 33eb27b885ab48..0a5e3ae4d3b206 100644
--- a/packages/wordcount/CHANGELOG.md
+++ b/packages/wordcount/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased
+## 4.7.0 (2024-09-05)
+
## 4.6.0 (2024-08-21)
## 4.5.0 (2024-08-07)
diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json
index 55cbfe3311d285..4f6fe750e3fad6 100644
--- a/packages/wordcount/package.json
+++ b/packages/wordcount/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/wordcount",
- "version": "4.6.0",
+ "version": "4.7.0",
"description": "WordPress word count utility.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/phpunit/blocks/render-block-media-text-test.php b/phpunit/blocks/render-block-media-text-test.php
index 94c184408c89fb..f5b67f0a8dc9b7 100644
--- a/phpunit/blocks/render-block-media-text-test.php
+++ b/phpunit/blocks/render-block-media-text-test.php
@@ -81,15 +81,14 @@ public function test_render_block_core_media_text_featured_image() {
$rendered = gutenberg_render_block_core_media_text( $attributes, $content );
$this->assertStringContainsString( ' true,
'imageFill' => true,
);
$rendered = gutenberg_render_block_core_media_text( $attributes, $content );
- $this->assertStringContainsString( 'background-image:url(' . wp_get_attachment_image_url( self::$attachment_id, 'full' ) . ')', $rendered );
- $this->assertStringNotContainsString( ' assertStringContainsString( ' assertStringContainsString( ' true,
'imageFill' => true,
);
$rendered = gutenberg_render_block_core_media_text( $attributes, $content );
- $this->assertStringContainsString( 'background-image:url(' . wp_get_attachment_image_url( self::$attachment_id, 'full' ) . ')', $rendered );
- $this->assertStringNotContainsString( ' assertStringContainsString( ' assertStringContainsString( ' true,
'mediaPosition' => 'right',
'imageFill' => true,
);
$rendered = gutenberg_render_block_core_media_text( $attributes, $content );
- $this->assertStringContainsString( 'background-image:url(' . wp_get_attachment_image_url( self::$attachment_id, 'full' ) . ')', $rendered );
- $this->assertStringNotContainsString( ' assertStringContainsString( ' assertStringContainsString( ' true,
'mediaPosition' => 'right',
@@ -173,7 +170,6 @@ public function test_render_block_core_media_text_featured_image_media_on_right_
);
$rendered = gutenberg_render_block_core_media_text( $attributes, $content );
- $this->assertStringContainsString( 'background-image:url(' . wp_get_attachment_image_url( self::$attachment_id, 'full' ) . ')', $rendered );
- $this->assertStringNotContainsString( ' assertStringContainsString( ' {
).toHaveText( 'fallback value' );
} );
+ test( 'should show the prompt placeholder in field with empty value', async ( {
+ editor,
+ } ) => {
+ await editor.insertBlock( {
+ name: 'core/paragraph',
+ attributes: {
+ content: 'paragraph default content',
+ metadata: {
+ bindings: {
+ content: {
+ source: 'core/post-meta',
+ args: { key: 'empty_field' },
+ },
+ },
+ },
+ },
+ } );
+
+ const paragraphBlock = editor.canvas.getByRole( 'document', {
+ // Aria-label is changed for empty paragraphs.
+ name: 'Add empty_field',
+ } );
+
+ await expect( paragraphBlock ).toBeEmpty();
+
+ const placeholder = paragraphBlock.locator( 'span' );
+ await expect( placeholder ).toHaveAttribute(
+ 'data-rich-text-placeholder',
+ 'Add empty_field'
+ );
+ } );
+
test( 'should not show the value of a protected meta field', async ( {
editor,
} ) => {
@@ -1441,20 +1473,10 @@ test.describe( 'Block bindings', () => {
editor,
page,
} ) => {
- // Activate the block bindings UI experiment.
- await page.evaluate( () => {
- window.__experimentalBlockBindingsUI = true;
- } );
-
await editor.insertBlock( {
name: 'core/paragraph',
} );
- await page
- .getByRole( 'tabpanel', {
- name: 'Settings',
- } )
- .getByLabel( 'Attributes options' )
- .click();
+ await page.getByLabel( 'Attributes options' ).click();
const contentAttribute = page.getByRole( 'menuitemcheckbox', {
name: 'Show content',
} );
@@ -1464,11 +1486,6 @@ test.describe( 'Block bindings', () => {
editor,
page,
} ) => {
- // Activate the block bindings UI experiment.
- await page.evaluate( () => {
- window.__experimentalBlockBindingsUI = true;
- } );
-
await editor.insertBlock( {
name: 'core/paragraph',
attributes: {
@@ -1483,12 +1500,7 @@ test.describe( 'Block bindings', () => {
},
},
} );
- await page
- .getByRole( 'tabpanel', {
- name: 'Settings',
- } )
- .getByRole( 'button', { name: 'content' } )
- .click();
+ await page.getByRole( 'button', { name: 'content' } ).click();
await page
.getByRole( 'menuitemradio' )
@@ -1586,20 +1598,10 @@ test.describe( 'Block bindings', () => {
editor,
page,
} ) => {
- // Activate the block bindings UI experiment.
- await page.evaluate( () => {
- window.__experimentalBlockBindingsUI = true;
- } );
-
await editor.insertBlock( {
name: 'core/heading',
} );
- await page
- .getByRole( 'tabpanel', {
- name: 'Settings',
- } )
- .getByLabel( 'Attributes options' )
- .click();
+ await page.getByLabel( 'Attributes options' ).click();
const contentAttribute = page.getByRole( 'menuitemcheckbox', {
name: 'Show content',
} );
@@ -1786,11 +1788,6 @@ test.describe( 'Block bindings', () => {
editor,
page,
} ) => {
- // Activate the block bindings UI experiment.
- await page.evaluate( () => {
- window.__experimentalBlockBindingsUI = true;
- } );
-
await editor.insertBlock( {
name: 'core/buttons',
innerBlocks: [
@@ -2121,11 +2118,6 @@ test.describe( 'Block bindings', () => {
editor,
page,
} ) => {
- // Activate the block bindings UI experiment.
- await page.evaluate( () => {
- window.__experimentalBlockBindingsUI = true;
- } );
-
await editor.insertBlock( {
name: 'core/image',
} );
@@ -2414,11 +2406,9 @@ test.describe( 'Block bindings', () => {
},
} );
- const bindingsPanel = page
- .getByRole( 'tabpanel', {
- name: 'Settings',
- } )
- .locator( '.block-editor-bindings__panel' );
+ const bindingsPanel = page.locator(
+ '.block-editor-bindings__panel'
+ );
await expect( bindingsPanel ).toContainText( 'Server Source' );
} );
} );
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v5.serialized.html b/test/integration/fixtures/blocks/core__media-text__deprecated-v5.serialized.html
index 7d7a76c730b273..8c88b8b67795d7 100644
--- a/test/integration/fixtures/blocks/core__media-text__deprecated-v5.serialized.html
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v5.serialized.html
@@ -1,5 +1,5 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.html b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.html
new file mode 100644
index 00000000000000..22a2a3e18f74f6
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.json b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.json
new file mode 100644
index 00000000000000..bf195f6fa1c953
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.json
@@ -0,0 +1,29 @@
+[
+ {
+ "name": "core/media-text",
+ "isValid": true,
+ "attributes": {
+ "align": "wide",
+ "mediaAlt": "My alt text",
+ "mediaPosition": "left",
+ "mediaType": "image",
+ "mediaWidth": 50,
+ "isStackedOnMobile": true,
+ "mediaUrl": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
+ "imageFill": true
+ },
+ "innerBlocks": [
+ {
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "My Content",
+ "dropCap": false,
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": []
+ }
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.parsed.json b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.parsed.json
new file mode 100644
index 00000000000000..2e878c98a18a89
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.parsed.json
@@ -0,0 +1,29 @@
+[
+ {
+ "blockName": "core/media-text",
+ "attrs": {
+ "mediaType": "image",
+ "imageFill": true
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\t
My Content
\n\t\t",
+ "innerContent": [
+ "\n\t\t
My Content
\n\t\t"
+ ]
+ }
+ ],
+ "innerHTML": "\n
\n\t
\n\t\t \n\t
\n\t\t\n\t
\n
\n",
+ "innerContent": [
+ "\n
\n\t
\n\t\t \n\t
\n\t\t",
+ null,
+ "\n\t
\n
\n"
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.serialized.html b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.serialized.html
new file mode 100644
index 00000000000000..b9d5e14f90277c
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-no-focal-point-selected.serialized.html
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.html b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.html
new file mode 100644
index 00000000000000..47a0f0c126fa70
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.json b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.json
new file mode 100644
index 00000000000000..986e049099541f
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.json
@@ -0,0 +1,33 @@
+[
+ {
+ "name": "core/media-text",
+ "isValid": true,
+ "attributes": {
+ "align": "wide",
+ "mediaAlt": "My alt text",
+ "mediaPosition": "left",
+ "mediaType": "image",
+ "mediaWidth": 50,
+ "isStackedOnMobile": true,
+ "mediaUrl": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
+ "imageFill": true,
+ "focalPoint": {
+ "x": 0.84,
+ "y": 0.84
+ }
+ },
+ "innerBlocks": [
+ {
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "My Content",
+ "dropCap": false,
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": []
+ }
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.parsed.json b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.parsed.json
new file mode 100644
index 00000000000000..175fbad27927b7
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.parsed.json
@@ -0,0 +1,33 @@
+[
+ {
+ "blockName": "core/media-text",
+ "attrs": {
+ "mediaType": "image",
+ "imageFill": true,
+ "focalPoint": {
+ "x": 0.84,
+ "y": 0.84
+ }
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\t
My Content
\n\t\t",
+ "innerContent": [
+ "\n\t\t
My Content
\n\t\t"
+ ]
+ }
+ ],
+ "innerHTML": "\n
\n\t
\n\t\t \n\t
\n\t\t\n\t
\n
\n",
+ "innerContent": [
+ "\n
\n\t
\n\t\t \n\t
\n\t\t",
+ null,
+ "\n\t
\n
\n"
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.serialized.html b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.serialized.html
new file mode 100644
index 00000000000000..4b001f4f27c8ad
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7-image-fill-with-focal-point-selected.serialized.html
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7.html b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.html
new file mode 100644
index 00000000000000..92b4b4d2754133
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7.json b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.json
new file mode 100644
index 00000000000000..a6f0dff927874d
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.json
@@ -0,0 +1,30 @@
+[
+ {
+ "name": "core/media-text",
+ "isValid": true,
+ "attributes": {
+ "align": "none",
+ "mediaAlt": "My alt text",
+ "mediaPosition": "left",
+ "mediaType": "image",
+ "mediaWidth": 50,
+ "isStackedOnMobile": true,
+ "mediaUrl": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
+ "imageFill": true,
+ "useFeaturedImage": false
+ },
+ "innerBlocks": [
+ {
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "My Content",
+ "dropCap": false,
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": []
+ }
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7.parsed.json b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.parsed.json
new file mode 100644
index 00000000000000..49b392d4231384
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.parsed.json
@@ -0,0 +1,29 @@
+[
+ {
+ "blockName": "core/media-text",
+ "attrs": {
+ "mediaType": "image",
+ "imageFill": true
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\t
My Content
\n\t\t",
+ "innerContent": [
+ "\n\t\t
My Content
\n\t\t"
+ ]
+ }
+ ],
+ "innerHTML": "\n
\n\t
\n\t\t \n\t
\n\t\t\n\t
\n
\n",
+ "innerContent": [
+ "\n
\n\t
\n\t\t \n\t
\n\t\t",
+ null,
+ "\n\t
\n
\n"
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__media-text__deprecated-v7.serialized.html b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.serialized.html
new file mode 100644
index 00000000000000..b44ebe1c4ee896
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__media-text__deprecated-v7.serialized.html
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.html b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.html
index 22a2a3e18f74f6..30667e0dd24dc5 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.html
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.html
@@ -1,9 +1,8 @@
-
-
-
+
+
+
+ alt="My alt text" style="object-position:50% 50%"/>
My Content
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.json b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.json
index bf195f6fa1c953..bb57f08ad5cfa7 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.json
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.json
@@ -6,11 +6,12 @@
"align": "wide",
"mediaAlt": "My alt text",
"mediaPosition": "left",
+ "mediaUrl": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
"mediaType": "image",
"mediaWidth": 50,
"isStackedOnMobile": true,
- "mediaUrl": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
- "imageFill": true
+ "imageFill": true,
+ "useFeaturedImage": false
},
"innerBlocks": [
{
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.parsed.json b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.parsed.json
index 2e878c98a18a89..674b3724af8e88 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.parsed.json
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.parsed.json
@@ -2,6 +2,7 @@
{
"blockName": "core/media-text",
"attrs": {
+ "align": "wide",
"mediaType": "image",
"imageFill": true
},
@@ -19,9 +20,9 @@
]
}
],
- "innerHTML": "\n
\n\t
\n\t\t \n\t
\n\t\t\n\t
\n
\n",
+ "innerHTML": "\n
\n\t
\n\t\t \n\t
\n\t\t\n\t
\n
\n",
"innerContent": [
- "\n
\n\t
\n\t\t \n\t
\n\t\t",
+ "\n
\n\t
\n\t\t \n\t
\n\t\t",
null,
"\n\t
\n
\n"
]
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.serialized.html b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.serialized.html
index 40382e884f618e..b9d5e14f90277c 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.serialized.html
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-no-focal-point-selected.serialized.html
@@ -1,5 +1,5 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.html b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.html
index 47a0f0c126fa70..60e2cc7be572d4 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.html
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.html
@@ -1,9 +1,8 @@
-
-
-
+
+
+
+ alt="My alt text" style="object-position:84% 84%" />
My Content
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.json b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.json
index 986e049099541f..0de71c867457ef 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.json
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.json
@@ -6,15 +6,16 @@
"align": "wide",
"mediaAlt": "My alt text",
"mediaPosition": "left",
+ "mediaUrl": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
"mediaType": "image",
"mediaWidth": 50,
"isStackedOnMobile": true,
- "mediaUrl": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
"imageFill": true,
"focalPoint": {
"x": 0.84,
"y": 0.84
- }
+ },
+ "useFeaturedImage": false
},
"innerBlocks": [
{
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.parsed.json b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.parsed.json
index 175fbad27927b7..92bd57443a1c34 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.parsed.json
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.parsed.json
@@ -2,6 +2,7 @@
{
"blockName": "core/media-text",
"attrs": {
+ "align": "wide",
"mediaType": "image",
"imageFill": true,
"focalPoint": {
@@ -23,9 +24,9 @@
]
}
],
- "innerHTML": "\n
\n\t
\n\t\t \n\t
\n\t\t\n\t
\n
\n",
+ "innerHTML": "\n
\n\t
\n\t\t \n\t
\n\t\t\n\t
\n
\n",
"innerContent": [
- "\n
\n\t
\n\t\t \n\t
\n\t\t",
+ "\n
\n\t
\n\t\t \n\t
\n\t\t",
null,
"\n\t
\n
\n"
]
diff --git a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.serialized.html b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.serialized.html
index 9445d0ad259b22..4b001f4f27c8ad 100644
--- a/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.serialized.html
+++ b/test/integration/fixtures/blocks/core__media-text__image-fill-with-focal-point-selected.serialized.html
@@ -1,5 +1,5 @@
-
+
diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js
index 36329d39c1212b..fa0c6123dbcdba 100644
--- a/tools/webpack/blocks.js
+++ b/tools/webpack/blocks.js
@@ -4,11 +4,13 @@
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
const { join, sep } = require( 'path' );
const fastGlob = require( 'fast-glob' );
+const { realpathSync } = require( 'fs' );
/**
* WordPress dependencies
*/
const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
+const { PhpFilePathsPlugin } = require( '@wordpress/scripts/utils' );
/**
* Internal dependencies
@@ -90,6 +92,10 @@ module.exports = [
plugins: [
...plugins,
new DependencyExtractionWebpackPlugin( { injectPolyfill: false } ),
+ new PhpFilePathsPlugin( {
+ context: './packages/block-library/src/',
+ props: [ 'render', 'variations' ],
+ } ),
new CopyWebpackPlugin( {
patterns: [].concat(
[
@@ -127,17 +133,32 @@ module.exports = [
'build/widgets/blocks/',
} ).flatMap( ( [ from, to ] ) => [
{
- from: `${ from }/**/index.php`,
+ from: `${ from }/**/*.php`,
to( { absoluteFilename } ) {
- const [ , dirname ] = absoluteFilename.match(
- new RegExp(
- `([\\w-]+)${ escapeRegExp(
- sep
- ) }index\\.php$`
+ const [ , dirname, basename ] =
+ absoluteFilename.match(
+ new RegExp(
+ `([\\w-]+)${ escapeRegExp(
+ sep
+ ) }([\\w-]+)\\.php$`
+ )
+ );
+
+ if ( basename === 'index' ) {
+ return join( to, `${ dirname }.php` );
+ }
+ return join( to, dirname, `${ basename }.php` );
+ },
+ filter: ( filepath ) => {
+ return (
+ filepath.endsWith( sep + 'index.php' ) ||
+ PhpFilePathsPlugin.paths.includes(
+ realpathSync( filepath ).replace(
+ /\\/g,
+ '/'
+ )
)
);
-
- return join( to, `${ dirname }.php` );
},
transform: ( content ) => {
const prefix = 'gutenberg_';
diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js
deleted file mode 100644
index cbde31171fe26e..00000000000000
--- a/tools/webpack/interactivity.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * External dependencies
- */
-const { join } = require( 'path' );
-const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
-/**
- * WordPress dependencies
- */
-const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
-
-/**
- * Internal dependencies
- */
-const { baseConfig, plugins } = require( './shared' );
-
-module.exports = {
- ...baseConfig,
- name: 'interactivity',
- entry: {
- index: './packages/interactivity',
- debug: './packages/interactivity/src/debug',
- router: './packages/interactivity-router',
- navigation: './packages/block-library/src/navigation/view.js',
- query: './packages/block-library/src/query/view.js',
- image: './packages/block-library/src/image/view.js',
- file: './packages/block-library/src/file/view.js',
- search: './packages/block-library/src/search/view.js',
- },
- experiments: {
- outputModule: true,
- },
- output: {
- devtoolNamespace: 'wp',
- filename: './build/interactivity/[name].min.js',
- library: {
- type: 'module',
- },
- path: join( __dirname, '..', '..' ),
- environment: { module: true },
- module: true,
- chunkFormat: 'module',
- },
- resolve: {
- extensions: [ '.js', '.ts', '.tsx' ],
- },
- module: {
- rules: [
- {
- test: /\.(j|t)sx?$/,
- exclude: /node_modules/,
- use: [
- {
- loader: require.resolve( 'babel-loader' ),
- options: {
- cacheDirectory:
- process.env.BABEL_CACHE_DIRECTORY || true,
- babelrc: false,
- configFile: false,
- presets: [
- '@babel/preset-typescript',
- '@babel/preset-react',
- ],
- },
- },
- ],
- },
- ],
- },
- plugins: [
- ...plugins,
- // TODO: Move it to a different Webpack file.
- new CopyWebpackPlugin( {
- patterns: [
- {
- from: './node_modules/es-module-shims/dist/es-module-shims.wasm.js',
- to: './build/modules/importmap-polyfill.min.js',
- },
- ],
- } ),
- new DependencyExtractionWebpackPlugin(),
- ],
- watchOptions: {
- ignored: [ '**/node_modules' ],
- aggregateTimeout: 500,
- },
-};
diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js
index 055665c2feb1c8..a1610d0a5ba54e 100644
--- a/tools/webpack/packages.js
+++ b/tools/webpack/packages.js
@@ -34,6 +34,7 @@ const BUNDLED_PACKAGES = [
'@wordpress/interface',
'@wordpress/sync',
'@wordpress/undo-manager',
+ '@wordpress/fields',
];
// PHP files in packages that have to be copied during build.
diff --git a/tools/webpack/script-modules.js b/tools/webpack/script-modules.js
new file mode 100644
index 00000000000000..57652e0be28e2b
--- /dev/null
+++ b/tools/webpack/script-modules.js
@@ -0,0 +1,133 @@
+/**
+ * External dependencies
+ */
+const { join } = require( 'path' );
+
+/**
+ * WordPress dependencies
+ */
+const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
+
+/**
+ * Internal dependencies
+ */
+const { baseConfig, plugins } = require( './shared' );
+
+const WORDPRESS_NAMESPACE = '@wordpress/';
+const { createRequire } = require( 'node:module' );
+
+const rootURL = new URL( '..', `file://${ __dirname }` );
+const fromRootRequire = createRequire( rootURL );
+
+/** @type {Iterable<[string, string]>} */
+const iterableDeps = Object.entries(
+ fromRootRequire( './package.json' ).dependencies
+);
+
+/** @type {Map
} */
+const gutenbergScriptModules = new Map();
+for ( const [ packageName, versionSpecifier ] of iterableDeps ) {
+ if (
+ ! packageName.startsWith( WORDPRESS_NAMESPACE ) ||
+ ! versionSpecifier.startsWith( 'file:' ) ||
+ packageName.startsWith( WORDPRESS_NAMESPACE + 'react-native' )
+ ) {
+ continue;
+ }
+
+ const packageRequire = createRequire(
+ // Remove the leading "file:" specifier to build a package URL.
+ new URL( `${ versionSpecifier.substring( 5 ) }/`, rootURL )
+ );
+
+ const depPackageJson = packageRequire( './package.json' );
+ if ( ! Object.hasOwn( depPackageJson, 'wpScriptModuleExports' ) ) {
+ continue;
+ }
+
+ const moduleName = packageName.substring( WORDPRESS_NAMESPACE.length );
+ let { wpScriptModuleExports } = depPackageJson;
+
+ // Special handling for { "wpScriptModuleExports": "./build-module/index.js" }.
+ if ( typeof wpScriptModuleExports === 'string' ) {
+ wpScriptModuleExports = { '.': wpScriptModuleExports };
+ }
+
+ if ( Object.getPrototypeOf( wpScriptModuleExports ) !== Object.prototype ) {
+ throw new Error( 'wpScriptModuleExports must be an object' );
+ }
+
+ for ( const [ exportName, exportPath ] of Object.entries(
+ wpScriptModuleExports
+ ) ) {
+ if ( typeof exportPath !== 'string' ) {
+ throw new Error( 'wpScriptModuleExports paths must be strings' );
+ }
+
+ if ( ! exportPath.startsWith( './' ) ) {
+ throw new Error(
+ 'wpScriptModuleExports paths must start with "./"'
+ );
+ }
+
+ const name =
+ exportName === '.' ? 'index' : exportName.replace( /^\.\/?/, '' );
+
+ gutenbergScriptModules.set(
+ `${ moduleName }/${ name }`,
+ packageRequire.resolve( exportPath )
+ );
+ }
+}
+
+module.exports = {
+ ...baseConfig,
+ name: 'script-modules',
+ entry: Object.fromEntries( gutenbergScriptModules.entries() ),
+ experiments: {
+ outputModule: true,
+ },
+ output: {
+ devtoolNamespace: 'wp',
+ filename: './build-module/[name].min.js',
+ library: {
+ type: 'module',
+ },
+ path: join( __dirname, '..', '..' ),
+ environment: { module: true },
+ module: true,
+ chunkFormat: 'module',
+ asyncChunks: false,
+ },
+ resolve: {
+ extensions: [ '.js', '.ts', '.tsx' ],
+ },
+ module: {
+ rules: [
+ {
+ test: /\.(j|t)sx?$/,
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: require.resolve( 'babel-loader' ),
+ options: {
+ cacheDirectory:
+ process.env.BABEL_CACHE_DIRECTORY || true,
+ babelrc: false,
+ configFile: false,
+ presets: [
+ '@babel/preset-typescript',
+ '@babel/preset-react',
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ },
+ plugins: [ ...plugins, new DependencyExtractionWebpackPlugin() ],
+ watchOptions: {
+ ignored: [ '**/node_modules' ],
+ aggregateTimeout: 500,
+ },
+};
diff --git a/tsconfig.json b/tsconfig.json
index cf986ddbee72bf..3ab54f66019bca 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -25,6 +25,7 @@
{ "path": "packages/element" },
{ "path": "packages/escape-html" },
{ "path": "packages/eslint-plugin" },
+ { "path": "packages/fields" },
{ "path": "packages/hooks" },
{ "path": "packages/html-entities" },
{ "path": "packages/html-entities" },
@@ -35,6 +36,7 @@
{ "path": "packages/is-shallow-equal" },
{ "path": "packages/keycodes" },
{ "path": "packages/lazy-import" },
+ { "path": "packages/media-utils" },
{ "path": "packages/notices" },
{ "path": "packages/plugins" },
{ "path": "packages/prettier-config" },
diff --git a/webpack.config.js b/webpack.config.js
index 45b22cc5354dcf..51889b06d1eb44 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,13 +3,13 @@
*/
const blocksConfig = require( './tools/webpack/blocks' );
const developmentConfigs = require( './tools/webpack/development' );
-const interactivity = require( './tools/webpack/interactivity' );
+const scriptModules = require( './tools/webpack/script-modules' );
const packagesConfig = require( './tools/webpack/packages' );
const vendorsConfig = require( './tools/webpack/vendors' );
module.exports = [
...blocksConfig,
- interactivity,
+ scriptModules,
packagesConfig,
...developmentConfigs,
...vendorsConfig,