diff --git a/.eslintrc.js b/.eslintrc.js index 484387a285e78..caf01b7cbd71d 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/backport-changelog/6.7/7258.md b/backport-changelog/6.7/7258.md new file mode 100644 index 0000000000000..6714b13b70b8d --- /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/backport-changelog/6.7/6910.md b/backport-changelog/6.8/6910.md similarity index 100% rename from backport-changelog/6.7/6910.md rename to backport-changelog/6.8/6910.md diff --git a/changelog.txt b/changelog.txt index 0fb2b93056b9b..dc0a2487f725f 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/reference-guides/interactivity-api/api-reference.md b/docs/reference-guides/interactivity-api/api-reference.md index e9b005d55899e..25498d10bde2e 100644 --- a/docs/reference-guides/interactivity-api/api-reference.md +++ b/docs/reference-guides/interactivity-api/api-reference.md @@ -84,7 +84,7 @@ It provides a **local** state available to a specific HTML node and its children The `wp-context` directive accepts a stringified JSON as a value. ```php -//render.php +// render.php
, , ] } secondaryActions={ secondaryActions } > - { __( 'This block contains unexpected or invalid content.' ) } + { __( 'Block contains unexpected or invalid content.' ) } { compare && ( .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 37dba80511d92..ea6128f153464 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 15fb83139237c..c3a279a618b5d 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 0000000000000..bb6edd066f06f --- /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 0000000000000..be5af54963060 --- /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 8dc6bfb2021f0..ad59030a8f440 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 98c0aff79eff3..76b46701758e8 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 ( <>
) } @@ -206,19 +199,23 @@ function BlockPatternsList( }, ref ) { - const compositeStore = useCompositeStore( { orientation } ); - const { setActiveId } = compositeStore; + const [ activeCompositeId, setActiveCompositeId ] = useState( undefined ); useEffect( () => { - // We reset the active composite item whenever the - // available patterns change, to make sure that - // focus is put back to the start. - setActiveId( undefined ); - }, [ setActiveId, shownPatterns, blockPatterns ] ); + // Reset the active composite item whenever the available patterns change, + // to make sure that Composite widget can receive focus correctly when its + // composite items change. The first composite item will receive focus. + const firstCompositeItemId = blockPatterns.find( ( pattern ) => + shownPatterns.includes( pattern ) + )?.name; + setActiveCompositeId( firstCompositeItemId ); + }, [ shownPatterns, blockPatterns ] ); return ( ( 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 46fd83c92d91f..7d66e19a214a2 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 287afaed4055c..823a656668a62 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 984670762262c..6c4789cb2924f 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -34,7 +34,6 @@ import { useShowHoveredOrFocusedGestures } from './utils'; import { store as blockEditorStore } from '../../store'; import __unstableBlockNameContext from './block-name-context'; import NavigableToolbar from '../navigable-toolbar'; -import Shuffle from './shuffle'; import { useHasBlockToolbar } from './use-has-block-toolbar'; /** @@ -66,7 +65,8 @@ export function PrivateBlockToolbar( { shouldShowVisualToolbar, showParentSelector, isUsingBindings, - canRemove, + hasParentPattern, + hasContentOnlyLocking, } = useSelect( ( select ) => { const { getBlockName, @@ -74,14 +74,13 @@ export function PrivateBlockToolbar( { getBlockParents, getSelectedBlockClientIds, isBlockValid, - getBlockRootClientId, getBlockEditingMode, getBlockAttributes, - canRemoveBlock, + getBlockParentsByBlockName, + getTemplateLock, } = select( blockEditorStore ); const selectedBlockClientIds = getSelectedBlockClientIds(); const selectedBlockClientId = selectedBlockClientIds[ 0 ]; - const blockRootClientId = getBlockRootClientId( selectedBlockClientId ); const parents = getBlockParents( selectedBlockClientId ); const firstParentClientId = parents[ parents.length - 1 ]; const parentBlockName = getBlockName( firstParentClientId ); @@ -99,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, @@ -106,7 +116,6 @@ export function PrivateBlockToolbar( { isDefaultEditingMode: _isDefaultEditingMode, blockType: selectedBlockClientId && getBlockType( _blockName ), shouldShowVisualToolbar: isValid && isVisual, - rootClientId: blockRootClientId, toolbarKey: `${ selectedBlockClientId }${ firstParentClientId }`, showParentSelector: parentBlockType && @@ -119,7 +128,8 @@ export function PrivateBlockToolbar( { selectedBlockClientIds.length === 1 && _isDefaultEditingMode, isUsingBindings: _isUsingBindings, - canRemove: canRemoveBlock( selectedBlockClientId ), + hasParentPattern: _hasParentPattern, + hasContentOnlyLocking: _hasTemplateLock, }; }, [] ); @@ -174,7 +184,7 @@ export function PrivateBlockToolbar( { isDefaultEditingMode && } { ( shouldShowVisualToolbar || isMultiToolbar ) && ( isDefaultEditingMode || - isContentOnlyEditingMode || + ( isContentOnlyEditingMode && ! hasParentPattern ) || isSynced ) && (
) } - { ! isMultiToolbar && canRemove && ( - - ) } - { 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 a37271e609b17..9f1325d7f95a1 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 bb044f9479c02..79f8be3f9cfe9 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 @@ -32,8 +32,11 @@ function ZoomOutModeInserters() { getSelectedBlockClientId, getHoveredBlockClientId, isBlockInsertionPointVisible, - } = select( blockEditorStore ); - const { sectionRootClientId: root } = unlock( 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 ( ); - - if ( isToggleButton || hasSingleBlockType ) { - inserterButton = ( - { inserterButton } - ); - } - return inserterButton; } } isAppender /> diff --git a/packages/block-editor/src/components/color-palette/test/control.js b/packages/block-editor/src/components/color-palette/test/control.js index 3a5dcc657a94c..e1a5529544f79 100644 --- a/packages/block-editor/src/components/color-palette/test/control.js +++ b/packages/block-editor/src/components/color-palette/test/control.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { render } from '@testing-library/react'; +import { render, waitFor, queryByAttribute } from '@testing-library/react'; /** * Internal dependencies @@ -10,9 +10,22 @@ import ColorPaletteControl from '../control'; const noop = () => {}; +async function renderAndValidate( ...renderArgs ) { + const view = render( ...renderArgs ); + await waitFor( () => { + const activeButton = queryByAttribute( + 'data-active-item', + view.baseElement, + 'true' + ); + expect( activeButton ).not.toBeNull(); + } ); + return view; +} + describe( 'ColorPaletteControl', () => { it( 'matches the snapshot', async () => { - const { container } = render( + const { container } = await renderAndValidate( 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 77725d2508ec5..51e0b4381a15d 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/color-panel.js b/packages/block-editor/src/components/global-styles/color-panel.js index 15f06310176c7..87c19adedbb27 100644 --- a/packages/block-editor/src/components/global-styles/color-panel.js +++ b/packages/block-editor/src/components/global-styles/color-panel.js @@ -33,7 +33,7 @@ import { unlock } from '../../lock-unlock'; export function useHasColorPanel( settings ) { const hasTextPanel = useHasTextPanel( settings ); - const hasBackgroundPanel = useHasBackgroundPanel( settings ); + const hasBackgroundPanel = useHasBackgroundColorPanel( settings ); const hasLinkPanel = useHasLinkPanel( settings ); const hasHeadingPanel = useHasHeadingPanel( settings ); const hasButtonPanel = useHasButtonPanel( settings ); @@ -97,7 +97,7 @@ export function useHasButtonPanel( settings ) { ); } -export function useHasBackgroundPanel( settings ) { +export function useHasBackgroundColorPanel( settings ) { const colors = useColorsPerOrigin( settings ); const gradients = useGradientsPerOrigin( settings ); return ( @@ -347,7 +347,7 @@ export default function ColorPanel( { }; // BackgroundColor - const showBackgroundPanel = useHasBackgroundPanel( settings ); + const showBackgroundPanel = useHasBackgroundColorPanel( settings ); const backgroundColor = decodeValue( inheritedValue?.color?.background ); const userBackgroundColor = decodeValue( value?.color?.background ); const gradient = decodeValue( inheritedValue?.color?.gradient ); diff --git a/packages/block-editor/src/components/global-styles/color-panel.native.js b/packages/block-editor/src/components/global-styles/color-panel.native.js index fcbff4e5a07d7..87002b5fa3e22 100644 --- a/packages/block-editor/src/components/global-styles/color-panel.native.js +++ b/packages/block-editor/src/components/global-styles/color-panel.native.js @@ -18,7 +18,7 @@ import InspectorControls from '../inspector-controls'; import { useHasColorPanel, useHasTextPanel, - useHasBackgroundPanel, + useHasBackgroundColorPanel, } from './color-panel.js'; import { useGlobalStyles } from './use-global-styles-context'; @@ -95,7 +95,7 @@ const ColorPanel = ( { ); // BackgroundColor - const showBackgroundPanel = useHasBackgroundPanel( settings ); + const showBackgroundPanel = useHasBackgroundColorPanel( settings ); const backgroundColor = decodeValue( inheritedValue?.color?.background ); const gradient = decodeValue( inheritedValue?.color?.gradient ); const setBackgroundColor = useCallback( diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js index 241d3bb93d1b1..8430703aec966 100644 --- a/packages/block-editor/src/components/global-styles/dimensions-panel.js +++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js @@ -11,12 +11,11 @@ import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalBoxControl as BoxControl, - __experimentalHStack as HStack, __experimentalUnitControl as UnitControl, __experimentalUseCustomUnits as useCustomUnits, - __experimentalView as View, + __experimentalInputControlPrefixWrapper as InputControlPrefixWrapper, } from '@wordpress/components'; -import { Icon, positionCenter, stretchWide } from '@wordpress/icons'; +import { Icon, alignNone, stretchWide } from '@wordpress/icons'; import { useCallback, useState, Platform } from '@wordpress/element'; /** @@ -252,7 +251,7 @@ export default function DimensionsPanel( { const minimumMargin = -Infinity; const [ minMarginValue, setMinMarginValue ] = useState( minimumMargin ); - // Content Size + // Content Width const showContentSizeControl = useHasContentSize( settings ) && includeLayoutControls; const contentSizeValue = decodeValue( inheritedValue?.layout?.contentSize ); @@ -268,7 +267,7 @@ export default function DimensionsPanel( { const hasUserSetContentSizeValue = () => !! value?.layout?.contentSize; const resetContentSizeValue = () => setContentSizeValue( undefined ); - // Wide Size + // Wide Width const showWideSizeControl = useHasWideSize( settings ) && includeLayoutControls; const wideSizeValue = decodeValue( inheritedValue?.layout?.wideSize ); @@ -464,8 +463,7 @@ export default function DimensionsPanel( { ) } { showContentSizeControl && ( - - { - setContentSizeValue( nextContentSize ); - } } - units={ units } - /> - - - - + { + setContentSizeValue( nextContentSize ); + } } + units={ units } + prefix={ + + + + } + /> ) } { showWideSizeControl && ( - - { - setWideSizeValue( nextWideSize ); - } } - units={ units } - /> - - - - + { + setWideSizeValue( nextWideSize ); + } } + units={ units } + prefix={ + + + + } + /> ) } { showPaddingControl && ( 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 e3a6c71adf8fc..1ed367e3c3ad0 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 32fbd50db2dbf..4b7c90ae4f222 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/grid-item-movers.js b/packages/block-editor/src/components/grid/grid-item-movers.js index fc9a3c5bf3995..a7f21181eb9a5 100644 --- a/packages/block-editor/src/components/grid/grid-item-movers.js +++ b/packages/block-editor/src/components/grid/grid-item-movers.js @@ -6,7 +6,7 @@ import clsx from 'clsx'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { VisuallyHidden, ToolbarButton, @@ -57,7 +57,7 @@ export function GridItemMovers( {
= columnCount } diff --git a/packages/block-editor/src/components/grid/style.scss b/packages/block-editor/src/components/grid/style.scss index 6790d683ca7d0..3a04eb006e791 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 ab80e0e8eb43e..0eae82b11e01f 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 c8db9f8cebf90..0d35b459190fe 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -203,7 +203,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { getBlockEditingMode, getBlockSettings, isDragging, - getSettings, + getSectionRootClientId, } = unlock( select( blockEditorStore ) ); let _isDropZoneDisabled; @@ -225,7 +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 { sectionRootClientId } = unlock( 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 f91e4365db30c..435f60ed9e2f1 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 6af26a1d746bf..bc833b0e4c36b 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 825c3fdfc353c..69c316bdbbcce 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 3703381b23a14..913be6a512973 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,23 +170,39 @@ export function PatternCategoryPreviews( { ) } - - { currentCategoryPatterns.length > 0 && ( - - ) } + + { 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 d3e7dba0df683..1af81d0231a1a 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 bfc858bc8c4de..73f38cfc306fb 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/style.scss b/packages/block-editor/src/components/inserter/style.scss index 21f8abfa59c9e..18389dc7e923c 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; +} 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 ec34035b754a9..ea2f45114bf9c 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 ff68be82a829f..6a80d47f02481 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 9ca1a72b9918a..34ec49a5e1cb4 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 1390a285fe163..b0a5f6af2651b 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 406053af89317..3d25597d2af90 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 6cb00206d93d7..68fcf150c4edc 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 a5b4eb0bf19b3..4d41289f324c0 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 379e245f03ff3..16183ccd6314f 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/index.js b/packages/block-editor/src/components/rich-text/index.js index e8fbec1e0b745..732b8dbf2c089 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -20,8 +20,9 @@ import { removeFormat, } from '@wordpress/rich-text'; import { Popover } from '@wordpress/components'; -import { getBlockType, store as blocksStore } from '@wordpress/blocks'; +import { store as blocksStore } from '@wordpress/blocks'; import deprecated from '@wordpress/deprecated'; +import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies @@ -163,49 +164,47 @@ export function RichTextWrapper( isBlockSelected, ] ); - const disableBoundBlocks = useSelect( + const { disableBoundBlock, bindingsPlaceholder } = useSelect( ( select ) => { - // Disable Rich Text editing if block bindings specify that. - let _disableBoundBlocks = false; - if ( blockBindings && canBindBlock( blockName ) ) { - const blockTypeAttributes = - getBlockType( blockName ).attributes; - const { getBlockBindingsSource } = unlock( - select( blocksStore ) - ); - for ( const [ attribute, binding ] of Object.entries( - blockBindings - ) ) { - if ( - blockTypeAttributes?.[ attribute ]?.source !== - 'rich-text' - ) { - break; - } - - // If the source is not defined, or if its value of `canUserEditValue` is `false`, disable it. - const blockBindingsSource = getBlockBindingsSource( - binding.source - ); - if ( - ! blockBindingsSource?.canUserEditValue?.( { - select, - context: blockContext, - args: binding.args, - } ) - ) { - _disableBoundBlocks = true; - break; - } - } + if ( + ! blockBindings?.[ identifier ] || + ! canBindBlock( blockName ) + ) { + return {}; } - return _disableBoundBlocks; + const relatedBinding = blockBindings[ identifier ]; + const { getBlockBindingsSource } = unlock( select( blocksStore ) ); + const blockBindingsSource = getBlockBindingsSource( + relatedBinding.source + ); + + const _disableBoundBlock = + ! blockBindingsSource?.canUserEditValue?.( { + select, + context: blockContext, + args: relatedBinding.args, + } ); + + const _bindingsPlaceholder = _disableBoundBlock + ? relatedBinding?.args?.key || blockBindingsSource?.label + : sprintf( + /* translators: %s: source label or key */ + __( 'Add %s' ), + relatedBinding?.args?.key || blockBindingsSource?.label + ); + + return { + disableBoundBlock: _disableBoundBlock, + bindingsPlaceholder: + ( ! adjustedValue || adjustedValue.length === 0 ) && + _bindingsPlaceholder, + }; }, - [ blockBindings, blockName ] + [ blockBindings, identifier, blockName, blockContext, adjustedValue ] ); - const shouldDisableEditing = readOnly || disableBoundBlocks; + const shouldDisableEditing = readOnly || disableBoundBlock; const { getSelectionStart, getSelectionEnd, getBlockRootClientId } = useSelect( blockEditorStore ); @@ -335,7 +334,7 @@ export function RichTextWrapper( selectionStart, selectionEnd, onSelectionChange, - placeholder, + placeholder: bindingsPlaceholder || placeholder, __unstableIsSelected: isSelected, __unstableDisableFormats: disableFormats, preserveWhiteSpace, @@ -404,9 +403,11 @@ export function RichTextWrapper( // Overridable props. role="textbox" aria-multiline={ ! disableLineBreaks } - aria-label={ placeholder } aria-readonly={ shouldDisableEditing } { ...props } + aria-label={ + bindingsPlaceholder || props[ 'aria-label' ] || placeholder + } { ...autocompleteProps } ref={ useMergeRefs( [ // Rich text ref must be first because its focus listener diff --git a/packages/block-editor/src/components/rich-text/style.scss b/packages/block-editor/src/components/rich-text/style.scss index a3bb78b2521db..ca274d378d408 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 cbcc8e30c5825..a387e5369d01e 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 8115fe8f72a64..ff4d52aaa493b 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 @@ -274,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. @@ -313,8 +330,8 @@ export default function useBlockDropZone( { getAllowedBlocks, isDragging, isGroupable, - getSettings, isZoomOutMode, + getSectionRootClientId, } = unlock( useSelect( blockEditorStore ) ); const { showInsertionPoint, @@ -360,7 +377,7 @@ export default function useBlockDropZone( { return; } - const { sectionRootClientId } = unlock( 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 @@ -422,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 = [ @@ -492,6 +513,8 @@ export default function useBlockDropZone( { getBlockNamesByClientId, getDraggedBlockClientIds, getBlockType, + getSectionRootClientId, + isZoomOutMode, getBlocks, getBlockListSettings, dropZoneElement, @@ -504,8 +527,6 @@ export default function useBlockDropZone( { isGroupable, getBlockVariations, getGroupingBlockName, - getSettings, - isZoomOutMode, ] ), 200 @@ -521,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 f4eeb253c1c2f..9380a224b2ff9 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 { @@ -30,21 +30,14 @@ flex-wrap: wrap; align-items: baseline; width: 100%; + gap: $grid-unit-15; } .block-editor-warning__actions { align-items: center; display: flex; - margin-top: 1em; + gap: $grid-unit-10; } - - .block-editor-warning__action { - margin: 0 $grid-unit-10 0 0; - } -} - -.block-editor-warning__secondary { - margin: auto 0 auto $grid-unit-10; } .components-popover.block-editor-warning__dropdown { diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 3b90ecc1a0c17..3b59de2238b96 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -9,11 +9,10 @@ import { __experimentalText as Text, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, - __experimentalTruncate as Truncate, __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'; @@ -28,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 ); @@ -96,17 +96,19 @@ function BlockBindingsAttribute( { attribute, binding } ) { const { source: sourceName, args } = binding || {}; const sourceProps = unlock( blocksPrivateApis ).getBlockBindingsSource( sourceName ); + const isSourceInvalid = ! sourceProps; return ( - - { attribute } + + { attribute } { !! binding && ( - - { args?.key || sourceProps?.label || sourceName } - + { isSourceInvalid + ? __( 'Invalid source' ) + : args?.key || sourceProps?.label || sourceName } ) } @@ -195,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; } @@ -230,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 ( - + { @@ -262,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/block-bindings.scss b/packages/block-editor/src/hooks/block-bindings.scss index 69eaa18d57dae..e8f05895922f0 100644 --- a/packages/block-editor/src/hooks/block-bindings.scss +++ b/packages/block-editor/src/hooks/block-bindings.scss @@ -1,6 +1,6 @@ div.block-editor-bindings__panel { grid-template-columns: repeat(auto-fit, minmax(100%, 1fr)); - button:hover .block-editor-bindings__item-explanation { + button:hover .block-editor-bindings__item span { color: inherit; } } diff --git a/packages/block-editor/src/hooks/layout.scss b/packages/block-editor/src/hooks/layout.scss index 83a044e3cdca7..3eedd1f629a63 100644 --- a/packages/block-editor/src/hooks/layout.scss +++ b/packages/block-editor/src/hooks/layout.scss @@ -1,15 +1,7 @@ -.block-editor-hooks__layout-controls { +.block-editor-hooks__layout-controls-units { display: flex; - margin-bottom: $grid-unit-10; - - .block-editor-hooks__layout-controls-unit { - display: flex; - margin-right: $grid-unit-30; - - svg { - margin: auto 0 $grid-unit-05 $grid-unit-10; - } - } + flex-direction: column; + gap: $grid-unit-20; } .block-editor-block-inspector .block-editor-hooks__layout-controls-unit-input { diff --git a/packages/block-editor/src/layouts/constrained.js b/packages/block-editor/src/layouts/constrained.js index b8dcde637a4d3..21aca422a315f 100644 --- a/packages/block-editor/src/layouts/constrained.js +++ b/packages/block-editor/src/layouts/constrained.js @@ -6,11 +6,12 @@ import { __experimentalUnitControl as UnitControl, __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, + __experimentalInputControlPrefixWrapper as InputControlPrefixWrapper, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { Icon, - positionCenter, + alignNone, stretchWide, justifyLeft, justifyCenter, @@ -71,61 +72,59 @@ export default { return ( <> { allowCustomContentAndWideSize && ( - <> -
-
- { - nextWidth = - 0 > parseFloat( nextWidth ) - ? '0' - : nextWidth; - onChange( { - ...layout, - contentSize: nextWidth, - } ); - } } - units={ units } - /> - -
-
- { - nextWidth = - 0 > parseFloat( nextWidth ) - ? '0' - : nextWidth; - onChange( { - ...layout, - wideSize: nextWidth, - } ); - } } - units={ units } - /> - -
-
+
+ { + nextWidth = + 0 > parseFloat( nextWidth ) + ? '0' + : nextWidth; + onChange( { + ...layout, + contentSize: nextWidth, + } ); + } } + units={ units } + prefix={ + + + + } + /> + { + nextWidth = + 0 > parseFloat( nextWidth ) + ? '0' + : nextWidth; + onChange( { + ...layout, + wideSize: nextWidth, + } ); + } } + units={ units } + prefix={ + + + + } + />

{ __( 'Customize the width for all elements that are assigned to the center or wide columns.' ) }

- +
) } { allowJustification && ( - ( { 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 { sectionRootClientId } = unlock( - registry.select( STORE_NAME ).getSettings() - ); + + const sectionRootClientId = select.getSectionRootClientId(); + if ( firstSelectedClientId ) { let sectionClientId; diff --git a/packages/block-editor/src/store/private-keys.js b/packages/block-editor/src/store/private-keys.js index 00fac5531b9c3..e24b4a6264e72 100644 --- a/packages/block-editor/src/store/private-keys.js +++ b/packages/block-editor/src/store/private-keys.js @@ -2,3 +2,4 @@ export const globalStylesDataKey = Symbol( 'globalStylesDataKey' ); export const globalStylesLinksDataKey = Symbol( 'globalStylesLinks' ); export const selectBlockPatternsKey = Symbol( 'selectBlockPatternsKey' ); export const reusableBlocksSelectKey = Symbol( 'reusableBlocksSelect' ); +export const sectionRootClientIdKey = Symbol( 'sectionRootClientIdKey' ); diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 9d3c5f27a7cc9..7e323cee6581a 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -20,7 +20,7 @@ import { checkAllowListRecursive, getAllPatternsDependants, getInsertBlockTypeDependants, - getParsedPattern, + getGrammar, } from './utils'; import { INSERTER_PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils'; import { STORE_NAME } from './constants'; @@ -28,6 +28,7 @@ import { unlock } from '../lock-unlock'; import { selectBlockPatternsKey, reusableBlocksSelectKey, + sectionRootClientIdKey, } from './private-keys'; export { getBlockSettings } from './get-block-settings'; @@ -300,10 +301,10 @@ export const hasAllowedPatterns = createRegistrySelector( ( select ) => if ( ! inserter ) { return false; } - const { blocks } = getParsedPattern( pattern ); + const grammar = getGrammar( pattern ); return ( - checkAllowListRecursive( blocks, allowedBlockTypes ) && - blocks.every( ( { name: blockName } ) => + checkAllowListRecursive( grammar, allowedBlockTypes ) && + grammar.every( ( { name: blockName } ) => canInsertBlockType( state, blockName, rootClientId ) ) ); @@ -543,3 +544,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/selectors.js b/packages/block-editor/src/store/selectors.js index 8d4d94bdaa98f..30fdb76bdbe78 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -27,6 +27,7 @@ import { getAllPatternsDependants, getInsertBlockTypeDependants, getParsedPattern, + getGrammar, } from './utils'; import { orderBy } from '../utils/sorting'; import { STORE_NAME } from './constants'; @@ -36,6 +37,7 @@ import { getContentLockingParent, getTemporarilyEditingAsBlocks, getTemporarilyEditingFocusModeToRevert, + getSectionRootClientId, } from './private-selectors'; /** @@ -2057,9 +2059,8 @@ export const getInserterItems = createRegistrySelector( ( select ) => if ( ! item.rootClientId ) { let sectionRootClientId; try { - sectionRootClientId = unlock( - getSettings( state ) - ).sectionRootClientId; + sectionRootClientId = + getSectionRootClientId( state ); } catch ( e ) {} if ( sectionRootClientId && @@ -2376,17 +2377,27 @@ export const __experimentalGetAllowedPatterns = createRegistrySelector( const { getAllPatterns } = unlock( select( STORE_NAME ) ); const patterns = getAllPatterns(); const { allowedBlockTypes } = getSettings( state ); - const parsedPatterns = patterns .filter( ( { inserter = true } ) => !! inserter ) - .map( getParsedPattern ); + .map( ( pattern ) => { + return { + ...pattern, + get blocks() { + return getParsedPattern( pattern ).blocks; + }, + }; + } ); + const availableParsedPatterns = parsedPatterns.filter( - ( { blocks } ) => - checkAllowListRecursive( blocks, allowedBlockTypes ) + ( pattern ) => + checkAllowListRecursive( + getGrammar( pattern ), + allowedBlockTypes + ) ); const patternsAllowed = availableParsedPatterns.filter( - ( { blocks } ) => - blocks.every( ( { name } ) => + ( pattern ) => + getGrammar( pattern ).every( ( { blockName: name } ) => canInsertBlockType( state, name, rootClientId ) ) ); @@ -2827,7 +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 { sectionRootClientId } = unlock( getSettings( state ) ); + const sectionRootClientId = getSectionRootClientId( state ); if ( sectionRootClientId ) { const sectionClientIds = getBlockOrder( state, @@ -2920,7 +2931,8 @@ export const getBlockEditingMode = createRegistrySelector( // sections. const editorMode = __unstableGetEditorMode( state ); if ( editorMode === 'zoom-out' ) { - const { sectionRootClientId } = unlock( getSettings( state ) ); + const sectionRootClientId = getSectionRootClientId( state ); + if ( clientId === '' /* ROOT_CONTAINER_CLIENT_ID */ ) { return sectionRootClientId ? 'disabled' : 'contentOnly'; } diff --git a/packages/block-editor/src/store/utils.js b/packages/block-editor/src/store/utils.js index 71c5ac44f45b8..b630912a5163d 100644 --- a/packages/block-editor/src/store/utils.js +++ b/packages/block-editor/src/store/utils.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { parse } from '@wordpress/blocks'; +import { parse as grammarParse } from '@wordpress/block-serialization-default-parser'; /** * Internal dependencies @@ -13,6 +14,7 @@ import { STORE_NAME } from './constants'; export const withRootClientIdOptionKey = Symbol( 'withRootClientId' ); const parsedPatternCache = new WeakMap(); +const grammarMapCache = new WeakMap(); function parsePattern( pattern ) { const blocks = parse( pattern.content, { @@ -37,14 +39,24 @@ function parsePattern( pattern ) { export function getParsedPattern( pattern ) { let parsedPattern = parsedPatternCache.get( pattern ); - if ( parsedPattern ) { - return parsedPattern; + if ( ! parsedPattern ) { + parsedPattern = parsePattern( pattern ); + parsedPatternCache.set( pattern, parsedPattern ); } - parsedPattern = parsePattern( pattern ); - parsedPatternCache.set( pattern, parsedPattern ); return parsedPattern; } +export function getGrammar( pattern ) { + let grammarMap = grammarMapCache.get( pattern ); + if ( ! grammarMap ) { + grammarMap = grammarParse( pattern.content ); + // Block names are null only at the top level for whitespace. + grammarMap = grammarMap.filter( ( block ) => block.blockName !== null ); + grammarMapCache.set( pattern, grammarMap ); + } + return grammarMap; +} + export const checkAllowList = ( list, item, defaultResult = null ) => { if ( typeof list === 'boolean' ) { return list; diff --git a/packages/block-editor/src/utils/dom.js b/packages/block-editor/src/utils/dom.js index e41ade344e49d..9c2e813ef742b 100644 --- a/packages/block-editor/src/utils/dom.js +++ b/packages/block-editor/src/utils/dom.js @@ -59,35 +59,19 @@ export function getBlockClientId( node ) { } /** - * Calculates the union of two rectangles, and optionally constrains this union within a containerRect's - * left and right values. - * The function returns a new DOMRect object representing this union. + * Calculates the union of two rectangles. * - * @param {DOMRect} rect1 First rectangle. - * @param {DOMRect} rect2 Second rectangle. - * @param {DOMRectReadOnly?} containerRect An optional container rectangle. The union will be clipped to this rectangle. + * @param {DOMRect} rect1 First rectangle. + * @param {DOMRect} rect2 Second rectangle. * @return {DOMRect} Union of the two rectangles. */ -export function rectUnion( rect1, rect2, containerRect ) { - let left = Math.min( rect1.left, rect2.left ); - let right = Math.max( rect1.right, rect2.right ); +export function rectUnion( rect1, rect2 ) { + const left = Math.min( rect1.left, rect2.left ); + const right = Math.max( rect1.right, rect2.right ); const bottom = Math.max( rect1.bottom, rect2.bottom ); const top = Math.min( rect1.top, rect2.top ); - /* - * To calculate visible bounds using rectUnion, take into account the outer - * horizontal limits of the container in which an element is supposed to be "visible". - * For example, if an element is positioned -10px to the left of the window x value (0), - * this function discounts the negative overhang because it's not visible and - * therefore not to be counted in the visibility calculations. - * Top and bottom values are not accounted for to accommodate vertical scroll. - */ - if ( containerRect ) { - left = Math.max( left, containerRect.left ); - right = Math.min( right, containerRect.right ); - } - - return new window.DOMRect( left, top, right - left, bottom - top ); + return new window.DOMRectReadOnly( left, top, right - left, bottom - top ); } /** @@ -112,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; } /** @@ -138,16 +137,10 @@ function isElementVisible( element ) { export function getVisibleElementBounds( element ) { const viewport = element.ownerDocument.defaultView; if ( ! viewport ) { - return new window.DOMRect(); + return new window.DOMRectReadOnly(); } let bounds = element.getBoundingClientRect(); - const viewportRect = new window.DOMRectReadOnly( - 0, - 0, - viewport.innerWidth, - viewport.innerHeight - ); const stack = [ element ]; let currentElement; @@ -156,11 +149,28 @@ export function getVisibleElementBounds( element ) { for ( const child of currentElement.children ) { if ( isElementVisible( child ) ) { const childBounds = child.getBoundingClientRect(); - bounds = rectUnion( bounds, childBounds, viewportRect ); + bounds = rectUnion( bounds, childBounds ); stack.push( child ); } } } + /* + * Take into account the outer horizontal limits of the container in which + * an element is supposed to be "visible". For example, if an element is + * positioned -10px to the left of the window x value (0), this function + * discounts the negative overhang because it's not visible and therefore + * not to be counted in the visibility calculations. Top and bottom values + * are not accounted for to accommodate vertical scroll. + */ + const left = Math.max( bounds.left, 0 ); + const right = Math.min( bounds.right, viewport.innerWidth ); + bounds = new window.DOMRectReadOnly( + left, + bounds.top, + right - left, + bounds.height + ); + return bounds; } 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 88b40a5aacfeb..dcb56fc86b267 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 ffc1a635aa1fb..7c50409bae1ac 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-editor/tsconfig.json b/packages/block-editor/tsconfig.json index 192b6f7de7e12..37fb61895d252 100644 --- a/packages/block-editor/tsconfig.json +++ b/packages/block-editor/tsconfig.json @@ -8,6 +8,7 @@ "references": [ { "path": "../a11y" }, { "path": "../api-fetch" }, + { "path": "../block-serialization-default-parser" }, { "path": "../blob" }, { "path": "../components" }, { "path": "../compose" }, diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 319d2b04a3168..4ee3ebc7564b5 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 5b522f01ea778..4d0212490858c 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", diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index 53422e1c4cb8c..5c90361e6bb43 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 && ( 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-edit-link/block.json b/packages/block-library/src/comment-edit-link/block.json index a49f9a23161b8..578b284715c2a 100644 --- a/packages/block-library/src/comment-edit-link/block.json +++ b/packages/block-library/src/comment-edit-link/block.json @@ -30,7 +30,11 @@ }, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, @@ -47,6 +51,13 @@ }, "interactivity": { "clientNavigation": true + }, + "__experimentalBorder": { + "radius": true, + "color": true, + "width": true, + "style": true } - } + }, + "style": "wp-block-comment-edit-link" } diff --git a/packages/block-library/src/comment-edit-link/style.scss b/packages/block-library/src/comment-edit-link/style.scss new file mode 100644 index 0000000000000..1ae7daf55009e --- /dev/null +++ b/packages/block-library/src/comment-edit-link/style.scss @@ -0,0 +1,4 @@ +.wp-block-comment-edit-link { + // This block has customizable padding, border-box makes that more predictable. + box-sizing: border-box; +} diff --git a/packages/block-library/src/comment-reply-link/block.json b/packages/block-library/src/comment-reply-link/block.json index c10129412145c..68aa93c3c1526 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 0000000000000..7eca6cee1aa97 --- /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 b5a12e34343da..dbe89a4e3a83e 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 = [ { ' ' }
- - - - ); - }, - }, - { - id: 'secondary', - label: 'Secondary action', - callback() {}, - }, -]; - -export const fields = [ - { - label: 'Image', - id: 'image', - header: ( - - - Image - - ), - render: ( { item } ) => { - return ( - - ); - }, - enableSorting: false, - }, - { - label: 'Title', - id: 'title', - enableHiding: false, - enableGlobalSearch: true, - render: ( { item } ) => { - return { item.title }; - }, - }, - { - id: 'date', - label: 'Date', - type: 'datetime', - }, - { - label: 'Type', - id: 'type', - enableHiding: false, - elements: [ - { value: 'Not a planet', label: 'Not a planet' }, - { value: 'Ice giant', label: 'Ice giant' }, - { value: 'Terrestrial', label: 'Terrestrial' }, - { value: 'Gas giant', label: 'Gas giant' }, - ], - }, - { - label: 'Satellites', - id: 'satellites', - type: 'integer', - enableSorting: true, - }, - { - label: 'Description', - id: 'description', - enableSorting: false, - enableGlobalSearch: true, - }, - { - label: 'Categories', - id: 'categories', - header: ( - - - Categories - - ), - elements: [ - { value: 'Space', label: 'Space' }, - { value: 'NASA', label: 'NASA' }, - { value: 'Planet', label: 'Planet' }, - { value: 'Solar system', label: 'Solar system' }, - { value: 'Ice giant', label: 'Ice giant' }, - ], - filterBy: { - operators: [ 'isAny', 'isNone', 'isAll', 'isNotAll' ], - }, - getValue: ( { item } ) => { - return item.categories; - }, - render: ( { item } ) => { - return item.categories.join( ',' ); - }, - enableSorting: false, - }, -]; diff --git a/packages/dataviews/src/components/dataviews/stories/fixtures.tsx b/packages/dataviews/src/components/dataviews/stories/fixtures.tsx new file mode 100644 index 0000000000000..ff098209b3468 --- /dev/null +++ b/packages/dataviews/src/components/dataviews/stories/fixtures.tsx @@ -0,0 +1,698 @@ +/** + * WordPress dependencies + */ +import { trash, image, Icon, category } from '@wordpress/icons'; +import { + Button, + __experimentalText as Text, + __experimentalHStack as HStack, + __experimentalVStack as VStack, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import type { Field, Action } from '../../../types'; + +export type Theme = { + slug: string; + name: string; + description: string; + requires: string; + tested: string; + tags: string[]; +}; + +export type SpaceObject = { + id: number; + title: string; + description: string; + image: string; + type: string; + categories: string[]; + satellites: number; + date: string; +}; + +export const data: SpaceObject[] = [ + { + id: 1, + title: 'Apollo', + description: 'Apollo description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Not a planet', + categories: [ 'Space', 'NASA' ], + satellites: 0, + date: '2021-01-01T00:00:00Z', + }, + { + id: 2, + title: 'Space', + description: 'Space description', + image: 'https://live.staticflickr.com/5678/21911065441_92e2d44708_b.jpg', + type: 'Not a planet', + categories: [ 'Space' ], + satellites: 0, + date: '2019-01-02T00:00:00Z', + }, + { + id: 3, + title: 'NASA', + description: 'NASA photo', + image: 'https://live.staticflickr.com/742/21712365770_8f70a2c91e_b.jpg', + type: 'Not a planet', + categories: [ 'NASA' ], + satellites: 0, + date: '2025-01-03T00:00:00Z', + }, + { + id: 4, + title: 'Neptune', + description: 'Neptune description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Ice giant', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 14, + date: '2020-01-01T00:00:00Z', + }, + { + id: 5, + title: 'Mercury', + description: 'Mercury description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 0, + date: '2020-01-02T01:00:00Z', + }, + { + id: 6, + title: 'Venus', + description: 'La planète Vénus', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 0, + date: '2020-01-02T00:00:00Z', + }, + { + id: 7, + title: 'Earth', + description: 'Earth description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 1, + date: '2023-01-03T00:00:00Z', + }, + { + id: 8, + title: 'Mars', + description: 'Mars description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 2, + date: '2020-01-01T00:00:00Z', + }, + { + id: 9, + title: 'Jupiter', + description: 'Jupiter description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Gas giant', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 95, + date: '2017-01-01T00:01:00Z', + }, + { + id: 10, + title: 'Saturn', + description: 'Saturn description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Gas giant', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 146, + date: '2020-02-01T00:02:00Z', + }, + { + id: 11, + title: 'Uranus', + description: 'Uranus description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Ice giant', + categories: [ 'Space', 'Ice giant', 'Solar system' ], + satellites: 28, + date: '2020-03-01T00:00:00Z', + }, +]; + +export const themeData: Theme[] = [ + { + slug: 'twentyeleven', + name: 'Twenty Eleven', + description: + 'The 2011 theme for WordPress is sophisticated, lightweight, and adaptable. Make it yours with a custom menu, header image, and background -- then go further with available theme options for light or dark color scheme, custom link colors, and three layout choices. Twenty Eleven comes equipped with a Showcase page template that transforms your front page into a showcase to show off your best content, widget support galore (sidebar, three footer areas, and a Showcase page widget area), and a custom "Ephemera" widget to display your Aside, Link, Quote, or Status posts. Included are styles for print and for the admin editor, support for featured images (as custom header images on posts and pages and as large images on featured "sticky" posts), and special styles for six different post formats.', + requires: '3.2', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'two-columns', + 'left-sidebar', + 'right-sidebar', + 'custom-background', + 'custom-colors', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-image-header', + 'featured-images', + 'flexible-header', + 'footer-widgets', + 'full-width-template', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentyfifteen', + name: 'Twenty Fifteen', + description: + "Our 2015 default theme is clean, blog-focused, and designed for clarity. Twenty Fifteen's simple, straightforward typography is readable on a wide variety of screen sizes, and suitable for multiple languages. We designed it using a mobile-first approach, meaning your content takes center-stage, regardless of whether your visitors arrive by smartphone, tablet, laptop, or desktop computer.", + requires: '4.1', + tested: '6.6', + tags: [ + 'blog', + 'two-columns', + 'left-sidebar', + 'accessibility-ready', + 'custom-background', + 'custom-colors', + 'custom-header', + 'custom-logo', + 'custom-menu', + 'editor-style', + 'featured-images', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentyfourteen', + name: 'Twenty Fourteen', + description: + "In 2014, our default theme lets you create a responsive magazine website with a sleek, modern design. Feature your favorite homepage content in either a grid or a slider. Use the three widget areas to customize your website, and change your content's layout with a full-width page template and a contributor page to show off your authors. Creating a magazine website with WordPress has never been easier.", + requires: '3.6', + tested: '6.6', + tags: [ + 'blog', + 'news', + 'two-columns', + 'three-columns', + 'left-sidebar', + 'right-sidebar', + 'custom-background', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'flexible-header', + 'footer-widgets', + 'full-width-template', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'translation-ready', + 'accessibility-ready', + 'block-patterns', + ], + }, + { + slug: 'twentynineteen', + name: 'Twenty Nineteen', + description: + "Our 2019 default theme is designed to show off the power of the block editor. It features custom styles for all the default blocks, and is built so that what you see in the editor looks like what you'll see on your website. Twenty Nineteen is designed to be adaptable to a wide range of websites, whether you’re running a photo blog, launching a new business, or supporting a non-profit. Featuring ample whitespace and modern sans-serif headlines paired with classic serif body text, it's built to be beautiful on all screen sizes.", + requires: '4.7', + tested: '6.6', + tags: [ + 'one-column', + 'accessibility-ready', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentyseventeen', + name: 'Twenty Seventeen', + description: + 'Twenty Seventeen brings your site to life with header video and immersive featured images. With a focus on business sites, it features multiple sections on the front page as well as widgets, navigation and social menus, a logo, and more. Personalize its asymmetrical grid with a custom color scheme and showcase your multimedia content with post formats. Our default theme for 2017 works great in many languages, for any abilities, and on any device.', + requires: '4.7', + tested: '6.6', + tags: [ + 'one-column', + 'two-columns', + 'right-sidebar', + 'flexible-header', + 'accessibility-ready', + 'custom-colors', + 'custom-header', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentysixteen', + name: 'Twenty Sixteen', + description: + 'Twenty Sixteen is a modernized take on an ever-popular WordPress layout — the horizontal masthead with an optional right sidebar that works perfectly for blogs and websites. It has custom color options with beautiful default color schemes, a harmonious fluid grid using a mobile-first approach, and impeccable polish in every detail. Twenty Sixteen will make your WordPress look beautiful everywhere.', + requires: '4.4', + tested: '6.6', + tags: [ + 'one-column', + 'two-columns', + 'right-sidebar', + 'accessibility-ready', + 'custom-background', + 'custom-colors', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'flexible-header', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'blog', + 'block-patterns', + ], + }, + { + slug: 'twentyten', + name: 'Twenty Ten', + description: + 'The 2010 theme for WordPress is stylish, customizable, simple, and readable -- make it yours with a custom menu, header image, and background. Twenty Ten supports six widgetized areas (two in the sidebar, four in the footer) and featured images (thumbnails for gallery posts and custom header images for posts and pages). It includes stylesheets for print and the admin Visual Editor, special styles for posts in the "Asides" and "Gallery" categories, and has an optional one-column page template that removes the sidebar.', + requires: '5.6', + tested: '6.6', + tags: [ + 'blog', + 'two-columns', + 'custom-header', + 'custom-background', + 'threaded-comments', + 'sticky-post', + 'translation-ready', + 'microformats', + 'rtl-language-support', + 'editor-style', + 'custom-menu', + 'flexible-header', + 'featured-images', + 'footer-widgets', + 'featured-image-header', + 'block-patterns', + ], + }, + { + slug: 'twentythirteen', + name: 'Twenty Thirteen', + description: + 'The 2013 theme for WordPress takes us back to the blog, featuring a full range of post formats, each displayed beautifully in their own unique way. Design details abound, starting with a vibrant color scheme and matching header images, beautiful typography and icons, and a flexible layout that looks great on any device, big or small.', + requires: '3.6', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'two-columns', + 'right-sidebar', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'translation-ready', + 'accessibility-ready', + 'block-patterns', + ], + }, + { + slug: 'twentytwelve', + name: 'Twenty Twelve', + description: + 'The 2012 theme for WordPress is a fully responsive theme that looks great on any device. Features include a front page template with its own widgets, an optional display font, styling for post formats on both index and single views, and an optional no-sidebar page template. Make it yours with a custom menu, header image, and background.', + requires: '3.5', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'two-columns', + 'right-sidebar', + 'custom-background', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'flexible-header', + 'footer-widgets', + 'full-width-template', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentytwenty', + name: 'Twenty Twenty', + description: + 'Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor. Organizations and businesses have the ability to create dynamic landing pages with endless layouts using the group and column blocks. The centered content column and fine-tuned typography also makes it perfect for traditional blogs. Complete editor styles give you a good idea of what your content will look like, even before you publish. You can give your site a personal touch by changing the background colors and the accent color in the Customizer. The colors of all elements on your site are automatically calculated based on the colors you pick, ensuring a high, accessible color contrast for your visitors.', + requires: '4.7', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'custom-background', + 'custom-colors', + 'custom-logo', + 'custom-menu', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'full-width-template', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + 'block-styles', + 'wide-blocks', + 'accessibility-ready', + ], + }, + { + slug: 'twentytwentyfour', + name: 'Twenty Twenty-Four', + description: + 'Twenty Twenty-Four is designed to be flexible, versatile and applicable to any website. Its collection of templates and patterns tailor to different needs, such as presenting a business, blogging and writing or showcasing work. A multitude of possibilities open up with just a few adjustments to color and typography. Twenty Twenty-Four comes with style variations and full page designs to help speed up the site building process, is fully compatible with the site editor, and takes advantage of new design tools introduced in WordPress 6.4.', + requires: '6.4', + tested: '6.6', + tags: [ + 'one-column', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'full-site-editing', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'wide-blocks', + 'block-styles', + 'style-variations', + 'accessibility-ready', + 'blog', + 'portfolio', + 'news', + ], + }, + { + slug: 'twentytwentyone', + name: 'Twenty Twenty-One', + description: + 'Twenty Twenty-One is a blank canvas for your ideas and it makes the block editor your best brush. With new block patterns, which allow you to create a beautiful layout in a matter of seconds, this theme’s soft colors and eye-catching — yet timeless — design will let your work shine. Take it for a spin! See how Twenty Twenty-One elevates your portfolio, business website, or personal blog.', + requires: '5.3', + tested: '6.6', + tags: [ + 'one-column', + 'accessibility-ready', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'blog', + 'portfolio', + ], + }, + { + slug: 'twentytwentythree', + name: 'Twenty Twenty-Three', + description: + 'Twenty Twenty-Three is designed to take advantage of the new design tools introduced in WordPress 6.1. With a clean, blank base as a starting point, this default theme includes ten diverse style variations created by members of the WordPress community. Whether you want to build a complex or incredibly simple website, you can do it quickly and intuitively through the bundled styles or dive into creation and full customization yourself.', + requires: '6.1', + tested: '6.6', + tags: [ + 'one-column', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'full-site-editing', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'wide-blocks', + 'block-styles', + 'style-variations', + 'accessibility-ready', + 'blog', + 'portfolio', + 'news', + ], + }, + { + slug: 'twentytwentytwo', + name: 'Twenty Twenty-Two', + description: + 'Built on a solidly designed foundation, Twenty Twenty-Two embraces the idea that everyone deserves a truly unique website. The theme’s subtle styles are inspired by the diversity and versatility of birds: its typography is lightweight yet strong, its color palette is drawn from nature, and its layout elements sit gently on the page. The true richness of Twenty Twenty-Two lies in its opportunity for customization. The theme is built to take advantage of the Site Editor features introduced in WordPress 5.9, which means that colors, typography, and the layout of every single page on your site can be customized to suit your vision. It also includes dozens of block patterns, opening the door to a wide range of professionally designed layouts in just a few clicks. Whether you’re building a single-page website, a blog, a business website, or a portfolio, Twenty Twenty-Two will help you create a site that is uniquely yours.', + requires: '5.9', + tested: '6.6', + tags: [ + 'one-column', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'full-site-editing', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'style-variations', + 'wide-blocks', + 'block-styles', + 'accessibility-ready', + 'blog', + 'portfolio', + 'news', + ], + }, +]; + +export const themeFields: Field< Theme >[] = [ + { id: 'slug', label: 'Slug' }, + { id: 'name', label: 'Name' }, + { + id: 'description', + label: 'Description', + render: ( { item } ) => ( + + { item.description } + + ), + }, + { id: 'requires', label: 'Requires at least' }, + { id: 'tested', label: 'Tested up to' }, + { + id: 'tags', + label: 'Tags', + render: ( { item } ) => item.tags.join( ', ' ), + }, +]; + +export const DEFAULT_VIEW = { + type: 'table' as const, + search: '', + page: 1, + perPage: 10, + layout: {}, + filters: [], +}; + +export const actions: Action< SpaceObject >[] = [ + { + id: 'delete', + label: 'Delete item', + isPrimary: true, + icon: trash, + hideModalHeader: true, + RenderModal: ( { items, closeModal } ) => { + return ( + + + { `Are you sure you want to delete "${ items[ 0 ].title }"?` } + + + + + + + ); + }, + }, + { + id: 'secondary', + label: 'Secondary action', + callback() {}, + }, +]; + +export const fields: Field< SpaceObject >[] = [ + { + label: 'Image', + id: 'image', + header: ( + + + Image + + ), + render: ( { item } ) => { + return ( + + ); + }, + enableSorting: false, + }, + { + label: 'Title', + id: 'title', + enableHiding: false, + enableGlobalSearch: true, + render: ( { item } ) => { + return { item.title }; + }, + }, + { + id: 'date', + label: 'Date', + type: 'datetime', + }, + { + label: 'Type', + id: 'type', + enableHiding: false, + elements: [ + { value: 'Not a planet', label: 'Not a planet' }, + { value: 'Ice giant', label: 'Ice giant' }, + { value: 'Terrestrial', label: 'Terrestrial' }, + { value: 'Gas giant', label: 'Gas giant' }, + ], + }, + { + label: 'Satellites', + id: 'satellites', + type: 'integer', + enableSorting: true, + }, + { + label: 'Description', + id: 'description', + enableSorting: false, + enableGlobalSearch: true, + }, + { + label: 'Categories', + id: 'categories', + header: ( + + + Categories + + ), + elements: [ + { value: 'Space', label: 'Space' }, + { value: 'NASA', label: 'NASA' }, + { value: 'Planet', label: 'Planet' }, + { value: 'Solar system', label: 'Solar system' }, + { value: 'Ice giant', label: 'Ice giant' }, + ], + filterBy: { + operators: [ 'isAny', 'isNone', 'isAll', 'isNotAll' ], + }, + getValue: ( { item } ) => { + return item.categories; + }, + render: ( { item } ) => { + return item.categories.join( ',' ); + }, + enableSorting: false, + }, +]; diff --git a/packages/dataviews/src/components/dataviews/stories/index.story.js b/packages/dataviews/src/components/dataviews/stories/index.story.js deleted file mode 100644 index 376b14a464666..0000000000000 --- a/packages/dataviews/src/components/dataviews/stories/index.story.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * WordPress dependencies - */ -import { useState, useMemo } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import DataViews from '../index'; -import { DEFAULT_VIEW, actions, data, fields } from './fixtures'; -import { LAYOUT_GRID, LAYOUT_LIST, LAYOUT_TABLE } from '../../../constants'; -import { filterSortAndPaginate } from '../../../filter-and-sort-data-view'; - -const meta = { - title: 'DataViews/DataViews', - component: DataViews, -}; -export default meta; - -export const Default = ( props ) => { - const [ view, setView ] = useState( DEFAULT_VIEW ); - const { data: shownData, paginationInfo } = useMemo( () => { - return filterSortAndPaginate( data, view, fields ); - }, [ view ] ); - return ( - - ); -}; -Default.args = { - actions, - defaultLayouts: { - [ LAYOUT_TABLE ]: { - layout: { - primaryField: 'title', - styles: { - image: { - width: 50, - }, - title: { - maxWidth: 400, - }, - type: { - maxWidth: 400, - }, - description: { - maxWidth: 200, - }, - }, - }, - }, - [ LAYOUT_GRID ]: { - layout: { - mediaField: 'image', - primaryField: 'title', - }, - }, - [ LAYOUT_LIST ]: { - layout: { - mediaField: 'image', - primaryField: 'title', - }, - }, - }, -}; diff --git a/packages/dataviews/src/components/dataviews/stories/index.story.tsx b/packages/dataviews/src/components/dataviews/stories/index.story.tsx new file mode 100644 index 0000000000000..3b3e1326115b0 --- /dev/null +++ b/packages/dataviews/src/components/dataviews/stories/index.story.tsx @@ -0,0 +1,186 @@ +/** + * WordPress dependencies + */ +import { useState, useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import DataViews from '../index'; +import { + DEFAULT_VIEW, + actions, + data, + fields, + themeData, + themeFields, +} from './fixtures'; +import { LAYOUT_GRID, LAYOUT_LIST, LAYOUT_TABLE } from '../../../constants'; +import { filterSortAndPaginate } from '../../../filter-and-sort-data-view'; +import type { CombinedField, View } from '../../../types'; + +import './style.css'; + +const meta = { + title: 'DataViews/DataViews', + component: DataViews, +}; +export default meta; + +const defaultLayouts = { + [ LAYOUT_TABLE ]: { + layout: { + primaryField: 'title', + styles: { + image: { + width: 50, + }, + title: { + maxWidth: 400, + }, + type: { + maxWidth: 400, + }, + description: { + maxWidth: 200, + }, + }, + }, + }, + [ LAYOUT_GRID ]: { + layout: { + mediaField: 'image', + primaryField: 'title', + }, + }, + [ LAYOUT_LIST ]: { + layout: { + mediaField: 'image', + primaryField: 'title', + }, + }, +}; + +export const Default = () => { + const [ view, setView ] = useState< View >( { + ...DEFAULT_VIEW, + fields: [ 'title', 'description', 'categories' ], + } ); + const { data: shownData, paginationInfo } = useMemo( () => { + return filterSortAndPaginate( data, view, fields ); + }, [ view ] ); + return ( + item.id.toString() } + paginationInfo={ paginationInfo } + data={ shownData } + view={ view } + fields={ fields } + onChangeView={ setView } + actions={ actions } + defaultLayouts={ defaultLayouts } + /> + ); +}; + +export const Empty = () => { + const [ view, setView ] = useState< View >( { + ...DEFAULT_VIEW, + fields: [ 'title', 'description', 'categories' ], + } ); + + return ( + item.id.toString() } + paginationInfo={ { totalItems: 0, totalPages: 0 } } + data={ [] } + view={ view } + fields={ fields } + onChangeView={ setView } + actions={ actions } + defaultLayouts={ defaultLayouts } + /> + ); +}; + +export const FieldsNoSortableNoHidable = () => { + const [ view, setView ] = useState< View >( { + ...DEFAULT_VIEW, + fields: [ 'title', 'description', 'categories' ], + } ); + const { data: shownData, paginationInfo } = useMemo( () => { + return filterSortAndPaginate( data, view, fields ); + }, [ view ] ); + + const _fields = fields.map( ( field ) => ( { + ...field, + enableSorting: false, + enableHiding: false, + } ) ); + + return ( + item.id.toString() } + paginationInfo={ paginationInfo } + data={ shownData } + view={ view } + fields={ _fields } + onChangeView={ setView } + defaultLayouts={ { + table: {}, + } } + /> + ); +}; + +export const CombinedFields = () => { + 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 ); + }, [ view ] ); + + return ( + item.name } + paginationInfo={ paginationInfo } + data={ shownData } + view={ view } + fields={ themeFields } + onChangeView={ setView } + 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 0000000000000..7c0c6565a6edf --- /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/components/dataviews/style.scss b/packages/dataviews/src/components/dataviews/style.scss index 1ce75d36020a7..8909c7cf1c7cf 100644 --- a/packages/dataviews/src/components/dataviews/style.scss +++ b/packages/dataviews/src/components/dataviews/style.scss @@ -7,6 +7,8 @@ container: dataviews-wrapper / inline-size; display: flex; flex-direction: column; + font-size: $default-font-size; + line-height: $default-line-height; } .dataviews__view-actions, diff --git a/packages/dataviews/src/dataviews-layouts/grid/style.scss b/packages/dataviews/src/dataviews-layouts/grid/style.scss index d154d2b614def..5fab362b0b47b 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/index.ts b/packages/dataviews/src/dataviews-layouts/index.ts index 9c96475777c00..eece17d0f4f10 100644 --- a/packages/dataviews/src/dataviews-layouts/index.ts +++ b/packages/dataviews/src/dataviews-layouts/index.ts @@ -16,7 +16,7 @@ import ViewTable from './table'; import ViewGrid from './grid'; import ViewList from './list'; import { LAYOUT_GRID, LAYOUT_LIST, LAYOUT_TABLE } from '../constants'; -import type { View } from '../types'; +import type { View, Field } from '../types'; export const VIEW_LAYOUTS = [ { @@ -39,7 +39,7 @@ export const VIEW_LAYOUTS = [ }, ]; -export function getMandatoryFields( view: View ): string[] { +export function getNotHidableFieldIds( view: View ): string[] { if ( view.type === 'table' ) { return [ view.layout?.primaryField ] .concat( @@ -64,3 +64,64 @@ export function getMandatoryFields( view: View ): string[] { return []; } + +function getCombinedFieldIds( view: View ): string[] { + const combinedFields: string[] = []; + if ( view.type === LAYOUT_TABLE && view.layout?.combinedFields ) { + view.layout.combinedFields.forEach( ( combination ) => { + combinedFields.push( ...combination.children ); + } ); + } + return combinedFields; +} + +export function getVisibleFieldIds( + view: View, + fields: Field< any >[] +): string[] { + const fieldsToExclude = getCombinedFieldIds( view ); + + if ( view.fields ) { + return view.fields.filter( ( id ) => ! fieldsToExclude.includes( id ) ); + } + + const visibleFields = []; + if ( view.type === LAYOUT_TABLE && view.layout?.combinedFields ) { + visibleFields.push( + ...view.layout.combinedFields.map( ( { id } ) => id ) + ); + } + visibleFields.push( + ...fields + .filter( ( { id } ) => ! fieldsToExclude.includes( id ) ) + .map( ( { id } ) => id ) + ); + + return visibleFields; +} + +export function getHiddenFieldIds( + view: View, + fields: Field< any >[] +): string[] { + const fieldsToExclude = [ + ...getCombinedFieldIds( view ), + ...getVisibleFieldIds( view, fields ), + ]; + + // The media field does not need to be in the view.fields to be displayed. + if ( view.type === LAYOUT_GRID && view.layout?.mediaField ) { + fieldsToExclude.push( view.layout?.mediaField ); + } + + if ( view.type === LAYOUT_LIST && view.layout?.mediaField ) { + fieldsToExclude.push( view.layout?.mediaField ); + } + + return fields + .filter( + ( { id, enableHiding } ) => + ! fieldsToExclude.includes( id ) && enableHiding + ) + .map( ( { id } ) => id ); +} diff --git a/packages/dataviews/src/dataviews-layouts/list/index.tsx b/packages/dataviews/src/dataviews-layouts/list/index.tsx index d8889f25b24e9..8a3f6a297338c 100644 --- a/packages/dataviews/src/dataviews-layouts/list/index.tsx +++ b/packages/dataviews/src/dataviews-layouts/list/index.tsx @@ -2,17 +2,11 @@ * External dependencies */ import clsx from 'clsx'; -// TODO: use the @wordpress/components one once public -// eslint-disable-next-line no-restricted-imports -import { useStoreState } from '@ariakit/react'; -// Import CompositeStore type, which is not exported from @wordpress/components. -// eslint-disable-next-line no-restricted-imports -import type { CompositeStore } from '@ariakit/react'; /** * WordPress dependencies */ -import { useInstanceId } from '@wordpress/compose'; +import { useInstanceId, usePrevious } from '@wordpress/compose'; import { __experimentalHStack as HStack, __experimentalVStack as VStack, @@ -20,6 +14,7 @@ import { privateApis as componentsPrivateApis, Spinner, VisuallyHidden, + Composite, } from '@wordpress/components'; import { useCallback, @@ -44,39 +39,110 @@ import type { Action, NormalizedField, ViewListProps } from '../../types'; interface ListViewItemProps< Item > { actions: Action< Item >[]; - id?: string; + idPrefix: string; isSelected: boolean; item: Item; mediaField?: NormalizedField< Item >; onSelect: ( item: Item ) => void; primaryField?: NormalizedField< Item >; - store: CompositeStore; visibleFields: NormalizedField< Item >[]; + onDropdownTriggerKeyDown: React.KeyboardEventHandler< HTMLButtonElement >; } -const { - useCompositeStoreV2: useCompositeStore, - CompositeV2: Composite, - CompositeItemV2: CompositeItem, - CompositeRowV2: CompositeRow, - DropdownMenuV2: DropdownMenu, -} = unlock( componentsPrivateApis ); +const { DropdownMenuV2: DropdownMenu } = unlock( componentsPrivateApis ); + +function generateItemWrapperCompositeId( idPrefix: string ) { + return `${ idPrefix }-item-wrapper`; +} +function generatePrimaryActionCompositeId( + idPrefix: string, + primaryActionId: string +) { + return `${ idPrefix }-primary-action-${ primaryActionId }`; +} +function generateDropdownTriggerCompositeId( idPrefix: string ) { + return `${ idPrefix }-dropdown`; +} + +function PrimaryActionGridCell< Item >( { + idPrefix, + primaryAction, + item, +}: { + idPrefix: string; + primaryAction: Action< Item >; + item: Item; +} ) { + const registry = useRegistry(); + const [ isModalOpen, setIsModalOpen ] = useState( false ); + + const compositeItemId = generatePrimaryActionCompositeId( + idPrefix, + primaryAction.id + ); + + const label = + typeof primaryAction.label === 'string' + ? primaryAction.label + : primaryAction.label( [ item ] ); + + return 'RenderModal' in primaryAction ? ( +
+ setIsModalOpen( true ) } + /> + } + > + { isModalOpen && ( + + action={ primaryAction } + items={ [ item ] } + closeModal={ () => setIsModalOpen( false ) } + /> + ) } + +
+ ) : ( +
+ { + primaryAction.callback( [ item ], { registry } ); + } } + /> + } + /> +
+ ); +} function ListItem< Item >( { actions, - id, + idPrefix, isSelected, item, mediaField, onSelect, primaryField, - store, visibleFields, + onDropdownTriggerKeyDown, }: ListViewItemProps< Item > ) { - const registry = useRegistry(); - const itemRef = useRef< HTMLElement >( null ); - const labelId = `${ id }-label`; - const descriptionId = `${ id }-description`; + const itemRef = useRef< HTMLDivElement >( null ); + const labelId = `${ idPrefix }-label`; + const descriptionId = `${ idPrefix }-description`; const [ isHovered, setIsHovered ] = useState( false ); const handleMouseEnter = () => { @@ -111,13 +177,6 @@ function ListItem< Item >( { }; }, [ actions, item ] ); - const [ isModalOpen, setIsModalOpen ] = useState( false ); - const primaryActionLabel = - primaryAction && - ( typeof primaryAction.label === 'string' - ? primaryAction.label - : primaryAction.label( [ item ] ) ); - const renderedMediaField = mediaField?.render ? ( ) : ( @@ -129,7 +188,7 @@ function ListItem< Item >( { ) : null; return ( - } role="row" @@ -146,11 +205,10 @@ function ListItem< Item >( { spacing={ 0 } >
- } role="button" - id={ id } + id={ generateItemWrapperCompositeId( idPrefix ) } aria-pressed={ isSelected } aria-labelledby={ labelId } aria-describedby={ descriptionId } @@ -198,7 +256,7 @@ function ListItem< Item >( {
- +
{ eligibleActions?.length > 0 && ( ( { width: 'auto', } } > - { primaryAction && 'RenderModal' in primaryAction && ( -
- - setIsModalOpen( true ) - } - /> - } - > - { isModalOpen && ( - - action={ primaryAction } - items={ [ item ] } - closeModal={ () => - setIsModalOpen( false ) - } - /> - ) } - -
+ { primaryAction && ( + ) } - { primaryAction && - ! ( 'RenderModal' in primaryAction ) && ( -
- { - primaryAction.callback( - [ item ], - { registry } - ); - } } - /> - } - /> -
- ) }
( { label={ __( 'Actions' ) } accessibleWhenDisabled disabled={ ! actions.length } - onKeyDown={ ( event: { - key: string; - preventDefault: () => void; - } ) => { - if ( - event.key === - 'ArrowDown' - ) { - // Prevent the default behaviour (open dropdown menu) and go down. - event.preventDefault(); - store.move( - store.down() - ); - } - if ( - event.key === 'ArrowUp' - ) { - // Prevent the default behavior (open dropdown menu) and go up. - event.preventDefault(); - store.move( - store.up() - ); - } - } } + onKeyDown={ + onDropdownTriggerKeyDown + } /> } /> @@ -315,7 +307,7 @@ function ListItem< Item >( { ) } - + ); } @@ -331,6 +323,7 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) { view, } = props; const baseId = useInstanceId( ViewList, 'view-list' ); + const selectedItem = data?.findLast( ( item ) => selection.includes( getItemId( item ) ) ); @@ -353,34 +346,108 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) { const onSelect = ( item: Item ) => onChangeSelection( [ getItemId( item ) ] ); - const getItemDomId = useCallback( - ( item?: Item ) => - item ? `${ baseId }-${ getItemId( item ) }` : undefined, + const generateCompositeItemIdPrefix = useCallback( + ( item: Item ) => `${ baseId }-${ getItemId( item ) }`, [ baseId, getItemId ] ); - const store = useCompositeStore( { - defaultActiveId: getItemDomId( selectedItem ), - } ) as CompositeStore; // TODO, remove once composite APIs are public + const isActiveCompositeItem = useCallback( + ( item: Item, idToCheck: string ) => { + // All composite items use the same prefix in their IDs. + return idToCheck.startsWith( + generateCompositeItemIdPrefix( item ) + ); + }, + [ generateCompositeItemIdPrefix ] + ); - // Manage focused item, when the active one is removed from the list. - const isActiveIdInList = useStoreState( - store, - ( state: { items: any[]; activeId: any } ) => - state.items.some( - ( item: { id: any } ) => item.id === state.activeId - ) + // Controlled state for the active composite item. + const [ activeCompositeId, setActiveCompositeId ] = useState< + string | null | undefined + >( undefined ); + + // Update the active composite item when the selected item changes. + useEffect( () => { + if ( selectedItem ) { + setActiveCompositeId( + generateItemWrapperCompositeId( + generateCompositeItemIdPrefix( selectedItem ) + ) + ); + } + }, [ selectedItem, generateCompositeItemIdPrefix ] ); + + const activeItemIndex = data.findIndex( ( item ) => + isActiveCompositeItem( item, activeCompositeId ?? '' ) ); + const previousActiveItemIndex = usePrevious( activeItemIndex ); + const isActiveIdInList = activeItemIndex !== -1; + + const selectCompositeItem = useCallback( + ( + targetIndex: number, + // Allows invokers to specify a custom function to generate the + // target composite item ID + generateCompositeId: ( idPrefix: string ) => string + ) => { + // Clamping between 0 and data.length - 1 to avoid out of bounds. + const clampedIndex = Math.min( + data.length - 1, + Math.max( 0, targetIndex ) + ); + const itemIdPrefix = generateCompositeItemIdPrefix( + data[ clampedIndex ] + ); + const targetCompositeItemId = generateCompositeId( itemIdPrefix ); + + setActiveCompositeId( targetCompositeItemId ); + document.getElementById( targetCompositeItemId )?.focus(); + }, + [ data, generateCompositeItemIdPrefix ] + ); + + // Select a new active composite item when the current active item + // is removed from the list. useEffect( () => { - if ( ! isActiveIdInList ) { - // Prefer going down, except if there is no item below (last item), then go up (last item in list). - if ( store.down() ) { - store.move( store.down() ); - } else if ( store.up() ) { - store.move( store.up() ); - } + const wasActiveIdInList = + previousActiveItemIndex !== undefined && + previousActiveItemIndex !== -1; + if ( ! isActiveIdInList && wasActiveIdInList ) { + // By picking `previousActiveItemIndex` as the next item index, we are + // basically picking the item that would have been after the deleted one. + // If the previously active (and removed) item was the last of the list, + // we will select the item before it — which is the new last item. + selectCompositeItem( + previousActiveItemIndex, + generateItemWrapperCompositeId + ); } - }, [ isActiveIdInList ] ); + }, [ isActiveIdInList, selectCompositeItem, previousActiveItemIndex ] ); + + // Prevent the default behavior (open dropdown menu) and instead select the + // dropdown menu trigger on the previous/next row. + // https://github.com/ariakit/ariakit/issues/3768 + const onDropdownTriggerKeyDown = useCallback( + ( event: React.KeyboardEvent< HTMLButtonElement > ) => { + if ( event.key === 'ArrowDown' ) { + // Select the dropdown menu trigger item in the next row. + event.preventDefault(); + selectCompositeItem( + activeItemIndex + 1, + generateDropdownTriggerCompositeId + ); + } + if ( event.key === 'ArrowUp' ) { + // Select the dropdown menu trigger item in the previous row. + event.preventDefault(); + selectCompositeItem( + activeItemIndex - 1, + generateDropdownTriggerCompositeId + ); + } + }, + [ selectCompositeItem, activeItemIndex ] + ); const hasData = data?.length; if ( ! hasData ) { @@ -404,22 +471,23 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) { render={
    } className="dataviews-view-list" role="grid" - store={ store } + activeId={ activeCompositeId } + setActiveId={ setActiveCompositeId } > { data.map( ( item ) => { - const id = getItemDomId( item ); + const id = generateCompositeItemIdPrefix( item ); return ( ); } ) } diff --git a/packages/dataviews/src/dataviews-layouts/list/style.scss b/packages/dataviews/src/dataviews-layouts/list/style.scss index 7344af6c3d1e8..ea3236f6d75e1 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/column-header-menu.tsx b/packages/dataviews/src/dataviews-layouts/table/column-header-menu.tsx index b019cbae36ab3..aff211fb613dc 100644 --- a/packages/dataviews/src/dataviews-layouts/table/column-header-menu.tsx +++ b/packages/dataviews/src/dataviews-layouts/table/column-header-menu.tsx @@ -25,7 +25,9 @@ import type { NormalizedField, SortDirection, ViewTable as ViewTableType, + Operator, } from '../../types'; +import { getVisibleFieldIds } from '../index'; const { DropdownMenuV2 } = unlock( componentsPrivateApis ); @@ -60,30 +62,43 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >( }: HeaderMenuProps< Item >, ref: Ref< HTMLButtonElement > ) { + const visibleFieldIds = getVisibleFieldIds( view, fields ); + const index = visibleFieldIds?.indexOf( fieldId ) as number; + const isSorted = view.sort?.field === fieldId; + let isHidable = false; + let isSortable = false; + let canAddFilter = false; + let header; + let operators: Operator[] = []; + const combinedField = view.layout?.combinedFields?.find( ( f ) => f.id === fieldId ); - const index = view.fields?.indexOf( fieldId ) as number; - if ( !! combinedField ) { - return combinedField.header || combinedField.label; - } const field = fields.find( ( f ) => f.id === fieldId ); - if ( ! field ) { - return null; + + if ( ! combinedField ) { + if ( ! field ) { + // No combined or regular field found. + return null; + } + + isHidable = field.enableHiding !== false; + isSortable = field.enableSorting !== false; + header = field.header; + + operators = sanitizeOperators( field ); + // Filter can be added: + // 1. If the field is not already part of a view's filters. + // 2. If the field meets the type and operator requirements. + // 3. If it's not primary. If it is, it should be already visible. + canAddFilter = + ! view.filters?.some( ( _filter ) => fieldId === _filter.field ) && + !! field.elements?.length && + !! operators.length && + ! field.filterBy?.isPrimary; + } else { + header = combinedField.header || combinedField.label; } - const isHidable = field.enableHiding !== false; - const isSortable = field.enableSorting !== false; - const isSorted = view.sort?.field === field.id; - const operators = sanitizeOperators( field ); - // Filter can be added: - // 1. If the field is not already part of a view's filters. - // 2. If the field meets the type and operator requirements. - // 3. If it's not primary. If it is, it should be already visible. - const canAddFilter = - ! view.filters?.some( ( _filter ) => field.id === _filter.field ) && - !! field.elements?.length && - !! operators.length && - ! field.filterBy?.isPrimary; return ( ( ref={ ref } variant="tertiary" > - { field.header } + { header } { view.sort && isSorted && (
- { /* eslint-enable jsx-a11y/no-redundant-roles */ }{ ' ' } + { /* eslint-enable jsx-a11y/no-redundant-roles */ }
{ setSelectedFont( null ); @@ -425,26 +427,40 @@ function FontCollection( { slug } ) { __nextHasNoMarginBottom /> - - { getSortedFontFaces( selectedFont ).map( - ( face, i ) => ( - - ) - ) } + { /* + * Disable reason: The `list` ARIA role is redundant but + * Safari+VoiceOver won't announce the list otherwise. + */ + /* eslint-disable jsx-a11y/no-redundant-roles */ } +
    + { getSortedFontFaces( selectedFont ).map( + ( face, i ) => ( +
  • + +
  • + ) + ) } +
+ { /* eslint-enable jsx-a11y/no-redundant-roles */ }
@@ -484,7 +500,7 @@ function FontCollection( { slug } ) { disabled={ page === 1 } showTooltip accessibleWhenDisabled - icon={ chevronLeft } + icon={ isRTL() ? chevronRight : chevronLeft } tooltipPosition="top" /> setPage( page + 1 ) } disabled={ page === totalPages } accessibleWhenDisabled - icon={ chevronRight } + icon={ isRTL() ? chevronLeft : chevronRight } tooltipPosition="top" /> diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js b/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js index 945db7ccb8ae6..f42f71095420b 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/installed-fonts.js @@ -21,8 +21,8 @@ import { import { useEntityRecord, store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { useContext, useEffect, useState } from '@wordpress/element'; -import { __, _x, sprintf } from '@wordpress/i18n'; -import { chevronLeft } from '@wordpress/icons'; +import { __, _x, sprintf, isRTL } from '@wordpress/i18n'; +import { chevronLeft, chevronRight } from '@wordpress/icons'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; /** @@ -354,7 +354,9 @@ function InstalledFonts() { { handleSetLibraryFontSelected( null ); @@ -399,15 +401,31 @@ function InstalledFonts() { __nextHasNoMarginBottom /> - { getFontFacesToDisplay( - libraryFontSelected - ).map( ( face, i ) => ( - - ) ) } + { /* + * Disable reason: The `list` ARIA role is redundant but + * Safari+VoiceOver won't announce the list otherwise. + */ + /* eslint-disable jsx-a11y/no-redundant-roles */ } +
    + { getFontFacesToDisplay( + libraryFontSelected + ).map( ( face, i ) => ( +
  • + +
  • + ) ) } +
+ { /* eslint-enable jsx-a11y/no-redundant-roles */ } 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 46972f4b7c52f..5d0e7cb185137 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 f85d69ad864ad..9bab48e308947 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 8d9b4238efa33..5f57c72f180b1 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 64fb1914af3dd..b2d929a7943db 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/fields.js b/packages/edit-site/src/components/page-patterns/fields.js index ff14aa371fdc7..ff9c0dbe81a04 100644 --- a/packages/edit-site/src/components/page-patterns/fields.js +++ b/packages/edit-site/src/components/page-patterns/fields.js @@ -126,7 +126,7 @@ function TitleField( { item } ) { { item.type === PATTERN_TYPES.theme ? ( diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index a5aa1eb9ac796..c62fe34daf7d6 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 4e21ca2c25be2..6a753921f6f40 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/pagination/index.js b/packages/edit-site/src/components/pagination/index.js index 66b89735b8cb4..8ef45883eb452 100644 --- a/packages/edit-site/src/components/pagination/index.js +++ b/packages/edit-site/src/components/pagination/index.js @@ -11,7 +11,7 @@ import { __experimentalText as Text, Button, } from '@wordpress/components'; -import { __, _x, _n, sprintf } from '@wordpress/i18n'; +import { __, _x, _n, sprintf, isRTL } from '@wordpress/i18n'; import { previous, chevronLeft, chevronRight, next } from '@wordpress/icons'; export default function Pagination( { @@ -50,7 +50,7 @@ export default function Pagination( { accessibleWhenDisabled disabled={ disabled || currentPage === 1 } label={ __( 'First page' ) } - icon={ previous } + icon={ isRTL() ? next : previous } size="compact" />
); diff --git a/packages/edit-site/src/components/style-book/style.scss b/packages/edit-site/src/components/style-book/style.scss index 3b2c6ab0867db..ab66ec288da31 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 0e699e6b4b242..e6568a23bb3a3 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 9face28c1bfe1..1aceecc4d8b1f 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 9e2582ac23328..78d823b56c4c1 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 eb185d530887d..f1cbcef0667d7 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 ed48d07a1d1e5..d24857f0f8a0c 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/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index 05bb9beb3b1fc..b380c2fd296d5 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 89ea15ef37849..ebd4af31e287d 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -862,6 +862,43 @@ _Returns_ - `Component`: The component to be rendered. +### PluginPreviewMenuItem + +Renders a menu item in the Preview dropdown, which can be used as a button or link depending on the props provided. The text within the component appears as the menu item label. + +_Usage_ + +```jsx +import { __ } from '@wordpress/i18n'; +import { PluginPreviewMenuItem } from '@wordpress/editor'; +import { external } from '@wordpress/icons'; + +function onPreviewClick() { + // Handle preview action +} + +const ExternalPreviewMenuItem = () => ( + + { __( 'Preview in new tab' ) } + +); +registerPlugin( 'external-preview-menu-item', { + render: ExternalPreviewMenuItem, +} ); +``` + +_Parameters_ + +- _props_ `Object`: Component properties. +- _props.href_ `[string]`: When `href` is provided, the menu item is rendered as an anchor instead of a button. It corresponds to the `href` attribute of the anchor. +- _props.icon_ `[WPBlockTypeIconRender]`: The icon to be rendered to the left of the menu item label. Can be a Dashicon slug or an SVG WP element. +- _props.onClick_ `[Function]`: The callback function to be executed when the user clicks the menu item. +- _props.other_ `[...*]`: Any additional props are passed through to the underlying MenuItem component. + +_Returns_ + +- `Component`: The rendered menu item component. + ### PluginSidebar Renders a sidebar when activated. The contents within the `PluginSidebar` will appear as content within the sidebar. It also automatically renders a corresponding `PluginSidebarMenuItem` component when `isPinnable` flag is set to `true`. If you wish to display the sidebar, you can with use the `PluginSidebarMoreMenuItem` component or the `wp.data.dispatch` API: diff --git a/packages/editor/package.json b/packages/editor/package.json index 7e120895c782f..a9d486b74e79c 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 1ecd6ea50ba27..7618ba6c36023 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 efd2606b82d39..49ce0c9b2d132 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/header/style.scss b/packages/editor/src/components/header/style.scss index 99c9cc70e166e..8712121fff3ea 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/index.js b/packages/editor/src/components/index.js index 91dcc883d661b..b42566aac653b 100644 --- a/packages/editor/src/components/index.js +++ b/packages/editor/src/components/index.js @@ -32,6 +32,7 @@ export { default as PluginMoreMenuItem } from './plugin-more-menu-item'; export { default as PluginPostPublishPanel } from './plugin-post-publish-panel'; export { default as PluginPostStatusInfo } from './plugin-post-status-info'; export { default as PluginPrePublishPanel } from './plugin-pre-publish-panel'; +export { default as PluginPreviewMenuItem } from './plugin-preview-menu-item'; export { default as PluginSidebar } from './plugin-sidebar'; export { default as PluginSidebarMoreMenuItem } from './plugin-sidebar-more-menu-item'; export { default as PostTemplatePanel } from './post-template/panel'; diff --git a/packages/editor/src/components/inserter-sidebar/index.js b/packages/editor/src/components/inserter-sidebar/index.js index bf613b5c8c001..fef5e54e52f68 100644 --- a/packages/editor/src/components/inserter-sidebar/index.js +++ b/packages/editor/src/components/inserter-sidebar/index.js @@ -38,13 +38,14 @@ export default function InserterSidebar() { 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 { sectionRootClientId } = unlock( getSettings() ); + const sectionRootClientId = getSectionRootClientId(); + if ( sectionRootClientId ) { return sectionRootClientId; } diff --git a/packages/editor/src/components/plugin-preview-menu-item/index.js b/packages/editor/src/components/plugin-preview-menu-item/index.js new file mode 100644 index 0000000000000..422248e17b88e --- /dev/null +++ b/packages/editor/src/components/plugin-preview-menu-item/index.js @@ -0,0 +1,52 @@ +/** + * WordPress dependencies + */ +import { compose } from '@wordpress/compose'; +import { MenuItem } from '@wordpress/components'; +import { withPluginContext } from '@wordpress/plugins'; +import { ActionItem } from '@wordpress/interface'; + +/** + * Renders a menu item in the Preview dropdown, which can be used as a button or link depending on the props provided. + * The text within the component appears as the menu item label. + * + * @param {Object} props Component properties. + * @param {string} [props.href] When `href` is provided, the menu item is rendered as an anchor instead of a button. It corresponds to the `href` attribute of the anchor. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The icon to be rendered to the left of the menu item label. Can be a Dashicon slug or an SVG WP element. + * @param {Function} [props.onClick] The callback function to be executed when the user clicks the menu item. + * @param {...*} [props.other] Any additional props are passed through to the underlying MenuItem component. + * + * @example + * ```jsx + * import { __ } from '@wordpress/i18n'; + * import { PluginPreviewMenuItem } from '@wordpress/editor'; + * import { external } from '@wordpress/icons'; + * + * function onPreviewClick() { + * // Handle preview action + * } + * + * const ExternalPreviewMenuItem = () => ( + * + * { __( 'Preview in new tab' ) } + * + * ); + * registerPlugin( 'external-preview-menu-item', { + * render: ExternalPreviewMenuItem, + * } ); + * ``` + * + * @return {Component} The rendered menu item component. + */ +export default compose( + withPluginContext( ( context, ownProps ) => { + return { + as: ownProps.as ?? MenuItem, + icon: ownProps.icon || context.icon, + name: 'core/plugin-preview-menu', + }; + } ) +)( ActionItem ); diff --git a/packages/editor/src/components/post-card-panel/style.scss b/packages/editor/src/components/post-card-panel/style.scss index 3547b0ab10493..73b638673f3e9 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 d3a4fbbcaef68..3a537471b42f2 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 03e86642493df..7f68002234466 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/style.scss b/packages/editor/src/components/post-publish-panel/style.scss index bcd7c798c7e9f..9892cf5430f9a 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-status/index.js b/packages/editor/src/components/post-status/index.js index ca89e40366b23..1d3050e7e3dd6 100644 --- a/packages/editor/src/components/post-status/index.js +++ b/packages/editor/src/components/post-status/index.js @@ -171,7 +171,7 @@ export default function PostStatus() { contentClassName="editor-change-status__content" popoverProps={ popoverProps } focusOnMount - renderToggle={ ( { onToggle } ) => ( + renderToggle={ ( { onToggle, isOpen } ) => ( diff --git a/packages/editor/src/components/post-template/block-theme.js b/packages/editor/src/components/post-template/block-theme.js index 7fcc5ead14f5b..62de10a2c715f 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 8f51df175c898..7edc7c0f18c27 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 c29dfd66a2d8c..ddb1776d479db 100644 --- a/packages/editor/src/components/post-trash/index.js +++ b/packages/editor/src/components/post-trash/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { Button, __experimentalConfirmDialog as ConfirmDialog, @@ -13,6 +13,7 @@ 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. @@ -20,12 +21,13 @@ import { store as editorStore } from '../../store'; * @return {JSX.Element|null} The rendered PostTrash component. */ export default function PostTrash() { - const { isNew, isDeleting, postId } = useSelect( ( select ) => { + 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 ); @@ -41,7 +43,7 @@ export default function PostTrash() { }; return ( - <> +