diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index a57b857aac6234..65bb55e3f63201 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -49,26 +49,28 @@ jobs: - name: Gradle cache uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - - name: AVD cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - - name: Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 - with: - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - arch: x86_64 - profile: Nexus 6 - script: echo "Generated AVD snapshot for caching." + # AVD cache disabled as it caused emulator termination to hang indefinitely. + # https://github.com/ReactiveCircus/android-emulator-runner/issues/385 + # - name: AVD cache + # uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + # id: avd-cache + # with: + # path: | + # ~/.android/avd/* + # ~/.android/adb* + # key: avd-${{ matrix.api-level }} + # + # - name: Create AVD and generate snapshot for caching + # if: steps.avd-cache.outputs.cache-hit != 'true' + # uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 + # with: + # api-level: ${{ matrix.api-level }} + # force-avd-creation: false + # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + # disable-animations: false + # arch: x86_64 + # profile: Nexus 6 + # script: echo "Generated AVD snapshot for caching." - name: Run tests uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 diff --git a/docs/reference-guides/data/data-core-editor.md b/docs/reference-guides/data/data-core-editor.md index 713a247d88be29..9567d8e4b954fa 100644 --- a/docs/reference-guides/data/data-core-editor.md +++ b/docs/reference-guides/data/data-core-editor.md @@ -786,7 +786,7 @@ Return true if the current post has already been published. _Parameters_ - _state_ `Object`: Global application state. -- _currentPost_ `Object?`: Explicit current post for bypassing registry selector. +- _currentPost_ `[Object]`: Explicit current post for bypassing registry selector. _Returns_ diff --git a/package-lock.json b/package-lock.json index 9d5b12e749d123..59fb1310a15e72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53169,6 +53169,7 @@ "@wordpress/keycodes": "*", "@wordpress/notices": "*", "@wordpress/preferences": "*", + "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/rich-text": "*", "@wordpress/style-engine": "*", @@ -54309,7 +54310,6 @@ "@wordpress/plugins": "*", "@wordpress/preferences": "*", "@wordpress/primitives": "*", - "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/reusable-blocks": "*", "@wordpress/router": "*", diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 09fa62a9d082b7..008bd9088e0778 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -27,8 +27,9 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "types": "build-types", + "wpScript": true, "wpScriptModuleExports": "./build-module/module/index.js", + "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/dom-ready": "*", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 8a94b326a78194..b1d6d210807a87 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/data": "*", @@ -38,6 +39,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 26f156a0aa7d77..7d5b8dfd588897 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/autop/package.json b/packages/autop/package.json index 2169ff828da47a..336dda06edfe2c 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/blob/package.json b/packages/blob/package.json index 4b04d2ea2ddebd..0dc01ac7198f59 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 59025e1ad45c7a..18a40824aa4754 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -54,6 +55,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 2b5cc8d6275e89..b4672bc57690eb 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -59,6 +60,7 @@ "@wordpress/keycodes": "*", "@wordpress/notices": "*", "@wordpress/preferences": "*", + "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/rich-text": "*", "@wordpress/style-engine": "*", @@ -87,6 +89,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-editor/src/components/block-canvas/style.scss b/packages/block-editor/src/components/block-canvas/style.scss index 0b37b4dd1447e2..8f6064de0b615c 100644 --- a/packages/block-editor/src/components/block-canvas/style.scss +++ b/packages/block-editor/src/components/block-canvas/style.scss @@ -3,7 +3,7 @@ iframe[name="editor-canvas"] { width: 100%; height: 100%; display: block; - background-color: transparent; // Handles transitions between device previews @include editor-canvas-resize-animation; + background-color: $gray-300; } diff --git a/packages/block-editor/src/components/block-patterns-list/README.md b/packages/block-editor/src/components/block-patterns-list/README.md index f63ea449059572..18e7ead5d1805a 100644 --- a/packages/block-editor/src/components/block-patterns-list/README.md +++ b/packages/block-editor/src/components/block-patterns-list/README.md @@ -18,7 +18,6 @@ import { BlockPatternsList } from '@wordpress/block-editor'; const MyBlockPatternsList = () => ( ); @@ -33,13 +32,6 @@ An array of block patterns that can be shown in the block patterns list. - Type: `Array` - Required: Yes -#### shownPatterns - -An array of shown block patterns objects. - -- Type: `Array` -- Required: Yes - #### onClickPattern The performed event after a click on a block pattern. In most cases, the pattern is inserted in the block editor. diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index 2609cc2db97a13..741a92ddf10dfa 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -134,10 +134,14 @@ function BlockPattern( { } } onMouseLeave={ () => onHover?.( null ) } > - + } + > + + { showTitle && ( - shownPatterns.includes( pattern ) - )?.name; + const firstCompositeItemId = blockPatterns[ 0 ]?.name; setActiveCompositeId( firstCompositeItemId ); - }, [ shownPatterns, blockPatterns ] ); + }, [ blockPatterns ] ); return ( - { blockPatterns.map( ( pattern ) => { - const isShown = shownPatterns.includes( pattern ); - return isShown ? ( - - ) : ( - - ); - } ) } + { blockPatterns.map( ( pattern ) => ( + + ) ) } { pagingProps && } ); diff --git a/packages/block-editor/src/components/block-patterns-list/stories/index.story.js b/packages/block-editor/src/components/block-patterns-list/stories/index.story.js index 9eb393ea13e762..0ebb4520d98fd4 100644 --- a/packages/block-editor/src/components/block-patterns-list/stories/index.story.js +++ b/packages/block-editor/src/components/block-patterns-list/stories/index.story.js @@ -3,11 +3,6 @@ */ import blockLibraryStyles from '!!raw-loader!../../../../../block-library/build-style/style.css'; -/** - * WordPress dependencies - */ -import { useAsyncList } from '@wordpress/compose'; - /** * Internal dependencies */ @@ -26,13 +21,9 @@ export default { export const Default = { render: function Template( props ) { - const shownPatterns = useAsyncList( props.blockPatterns ); return ( - + ); }, diff --git a/packages/edit-site/src/components/async/index.js b/packages/block-editor/src/components/block-preview/async.js similarity index 100% rename from packages/edit-site/src/components/async/index.js rename to packages/block-editor/src/components/block-preview/async.js diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js index 9eef0f1dc2abd5..62b137ff37194b 100644 --- a/packages/block-editor/src/components/block-preview/index.js +++ b/packages/block-editor/src/components/block-preview/index.js @@ -19,6 +19,7 @@ import AutoHeightBlockPreview from './auto'; import EditorStyles from '../editor-styles'; import { store as blockEditorStore } from '../../store'; import { BlockListItems } from '../block-list'; +import { Async } from './async'; const EMPTY_ADDITIONAL_STYLES = []; @@ -86,6 +87,10 @@ export function BlockPreview( { ); } +const MemoizedBlockPreview = memo( BlockPreview ); + +MemoizedBlockPreview.Async = Async; + /** * BlockPreview renders a preview of a block or array of blocks. * @@ -97,7 +102,7 @@ export function BlockPreview( { * * @return {Component} The component to be rendered. */ -export default memo( BlockPreview ); +export default MemoizedBlockPreview; /** * This hook is used to lightly mark an element as a block preview wrapper diff --git a/packages/block-editor/src/components/block-toolbar/change-design.js b/packages/block-editor/src/components/block-toolbar/change-design.js index ecfeff6cb1ed3e..9da1affe4273cc 100644 --- a/packages/block-editor/src/components/block-toolbar/change-design.js +++ b/packages/block-editor/src/components/block-toolbar/change-design.js @@ -10,7 +10,6 @@ import { import { __ } from '@wordpress/i18n'; import { cloneBlock } from '@wordpress/blocks'; import { useMemo } from '@wordpress/element'; -import { useAsyncList } from '@wordpress/compose'; import { useSelect, useDispatch } from '@wordpress/data'; /** @@ -81,10 +80,6 @@ export default function ChangeDesign( { clientId } ) { .slice( 0, MAX_PATTERNS_TO_SHOW ); }, [ categories, currentPatternName, patterns ] ); - const currentShownPatterns = useAsyncList( - sameCategoryPatternsWithSingleWrapper - ); - if ( sameCategoryPatternsWithSingleWrapper.length < 2 ) { return null; } @@ -121,7 +116,6 @@ export default function ChangeDesign( { clientId } ) { paddingSize="none" > :first-child { - margin-block-start: 0; - margin-block-end: 0; - } - // Since this appender will only ever appear on an entirely empty document, we don't account for last-child. - // This is also because it will never be the last child, the block inserter that sits in this appender is the last child. + // The following prevents user agent styles from applying margins to the appender's inner paragraph. + // This in turn prevents layout shift due to layout styles removing margins from first and last children. + margin-block-start: 0; + margin-block-end: 0; } // Dropzone. diff --git a/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js b/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js index 709e005b587cea..296b432bd685c3 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js +++ b/packages/block-editor/src/components/inserter/block-patterns-explorer/pattern-list.js @@ -151,9 +151,6 @@ function PatternList( { { hasItems && ( <> { destinationClientId ); }, - [ onInsert, getClosestAllowedInsertionPoint, rootClientId ] + [ + getClosestAllowedInsertionPoint, + rootClientId, + onInsert, + createErrorNotice, + ] ); return [ items, categories, collections, onSelectItem ]; diff --git a/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js b/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js index 931796acceeb08..cdffef4a00513e 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js +++ b/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js @@ -2,11 +2,10 @@ * WordPress dependencies */ import { useMemo, useState, useEffect } from '@wordpress/element'; -import { useAsyncList, usePrevious } from '@wordpress/compose'; +import { usePrevious } from '@wordpress/compose'; import { getScrollContainer } from '@wordpress/dom'; const PAGE_SIZE = 20; -const INITIAL_INSERTER_RESULTS = 5; /** * Supplies values needed to page the patterns list client side. @@ -42,9 +41,6 @@ export default function usePatternsPaging( pageIndex * PAGE_SIZE + PAGE_SIZE ); }, [ pageIndex, currentCategoryPatterns ] ); - const categoryPatternsAsyncList = useAsyncList( categoryPatterns, { - step: INITIAL_INSERTER_RESULTS, - } ); const numPages = Math.ceil( currentCategoryPatterns.length / PAGE_SIZE ); const changePage = ( page ) => { const scrollContainer = getScrollContainer( @@ -68,7 +64,6 @@ export default function usePatternsPaging( return { totalItems, categoryPatterns, - categoryPatternsAsyncList, numPages, changePage, currentPage, diff --git a/packages/block-editor/src/components/inserter/search-results.js b/packages/block-editor/src/components/inserter/search-results.js index 9c001823745e6c..5a5725a3bb08cd 100644 --- a/packages/block-editor/src/components/inserter/search-results.js +++ b/packages/block-editor/src/components/inserter/search-results.js @@ -159,11 +159,6 @@ function InserterSearchResults( { const currentShownBlockTypes = useAsyncList( filteredBlockTypes, { step: INITIAL_INSERTER_RESULTS, } ); - const currentShownPatterns = useAsyncList( - currentShownBlockTypes.length === filteredBlockTypes.length - ? filteredBlockPatterns - : EMPTY_ARRAY - ); const hasItems = filteredBlockTypes.length > 0 || filteredBlockPatterns.length > 0; @@ -190,7 +185,6 @@ function InserterSearchResults( { >
() => { - return select( preferencesStore ).get( 'core', 'editorTool' ); + ( select ) => ( state ) => { + return ( + state.settings.editorTool ?? + select( preferencesStore ).get( 'core', 'editorTool' ) + ); } ); diff --git a/packages/block-editor/tsconfig.json b/packages/block-editor/tsconfig.json index 37fb61895d252e..a3c7d1ffd88077 100644 --- a/packages/block-editor/tsconfig.json +++ b/packages/block-editor/tsconfig.json @@ -18,6 +18,7 @@ { "path": "../dom" }, { "path": "../element" }, { "path": "../escape-html" }, + { "path": "../priority-queue" }, { "path": "../private-apis" }, { "path": "../hooks" }, { "path": "../html-entities" }, diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 2c880301c8355d..b196e53e5cd0f9 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -25,11 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "sideEffects": [ - "build-style/**", - "src/**/*.scss", - "{src,build,build-module}/*/init.js" - ], + "wpScript": true, "wpScriptModuleExports": { "./file/view": "./build-module/file/view.js", "./image/view": "./build-module/image/view.js", @@ -37,6 +33,11 @@ "./query/view": "./build-module/query/view.js", "./search/view": "./build-module/search/view.js" }, + "sideEffects": [ + "build-style/**", + "src/**/*.scss", + "{src,build,build-module}/*/init.js" + ], "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -88,6 +89,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-library/src/cover/edit/index.js b/packages/block-library/src/cover/edit/index.js index 0ad87601bfba8b..1202f28cee50cd 100644 --- a/packages/block-library/src/cover/edit/index.js +++ b/packages/block-library/src/cover/edit/index.js @@ -206,12 +206,24 @@ function CoverEdit( { // Try to use the previous selected image size if it's available // otherwise try the default image size or fallback to full size. - if ( sizeSlug && newMedia?.sizes?.[ sizeSlug ] ) { + if ( + sizeSlug && + ( newMedia?.sizes?.[ sizeSlug ] || + newMedia?.media_details?.sizes?.[ sizeSlug ] ) + ) { mediaAttributes.sizeSlug = sizeSlug; - mediaAttributes.url = newMedia?.sizes?.[ sizeSlug ]?.url; - } else if ( newMedia?.sizes?.[ imageDefaultSize ] ) { + mediaAttributes.url = + newMedia?.sizes?.[ sizeSlug ]?.url || + newMedia?.media_details?.sizes?.[ sizeSlug ]?.source_url; + } else if ( + newMedia?.sizes?.[ imageDefaultSize ] || + newMedia?.media_details?.sizes?.[ imageDefaultSize ] + ) { mediaAttributes.sizeSlug = imageDefaultSize; - mediaAttributes.url = newMedia?.sizes?.[ sizeSlug ]?.url; + mediaAttributes.url = + newMedia?.sizes?.[ imageDefaultSize ]?.url || + newMedia?.media_details?.sizes?.[ imageDefaultSize ] + ?.source_url; } else { mediaAttributes.sizeSlug = DEFAULT_MEDIA_SIZE_SLUG; } diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index f441a6e893290b..16e31217476026 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -4,7 +4,7 @@ "name": "core/image", "title": "Image", "category": "media", - "usesContext": [ "allowResize", "imageCrop", "fixedHeight" ], + "usesContext": [ "allowResize", "imageCrop", "fixedHeight", "postId", "postType", "queryId" ], "description": "Insert an image to make a visual statement.", "keywords": [ "img", "photo", "picture" ], "textdomain": "default", diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index a8d65951635522..61baba2263989c 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -32,6 +32,7 @@ import { __experimentalUseBorderProps as useBorderProps, __experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles, privateApis as blockEditorPrivateApis, + BlockSettingsMenuControls, } from '@wordpress/block-editor'; import { useEffect, useMemo, useState, useRef } from '@wordpress/element'; import { __, _x, sprintf, isRTL } from '@wordpress/i18n'; @@ -39,7 +40,7 @@ import { getFilename } from '@wordpress/url'; import { getBlockBindingsSource, switchToBlockType } from '@wordpress/blocks'; import { crop, overlayText, upload, chevronDown } from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; -import { store as coreStore } from '@wordpress/core-data'; +import { store as coreStore, useEntityProp } from '@wordpress/core-data'; /** * Internal dependencies @@ -893,6 +894,16 @@ export default function Image( { const shadowProps = getShadowClassesAndStyles( attributes ); const isRounded = attributes.className?.includes( 'is-style-rounded' ); + const { postType, postId, queryId } = context; + const isDescendentOfQueryLoop = Number.isFinite( queryId ); + + const [ , setFeaturedImage ] = useEntityProp( + 'postType', + postType, + 'featured_media', + postId + ); + let img = temporaryURL && hasImageErrored ? ( // Show a placeholder during upload when the blob URL can't be loaded. This can @@ -1094,10 +1105,37 @@ export default function Image( { ); } + /** + * Set the post's featured image with the current image. + */ + const setPostFeatureImage = () => { + setFeaturedImage( id ); + createSuccessNotice( __( 'Post featured image updated.' ), { + type: 'snackbar', + } ); + }; + + const featuredImageControl = ( + + { ( { selectedClientIds } ) => + selectedClientIds.length === 1 && + ! isDescendentOfQueryLoop && + postId && + id && + clientId === selectedClientIds[ 0 ] && ( + + { __( 'Set featured image' ) } + + ) + } + + ); + return ( <> { mediaReplaceFlow } { controls } + { featuredImageControl } { img } { return searchPatterns( blockPatterns, searchValue ); }, [ blockPatterns, searchValue ] ); - const shownBlockPatterns = useAsyncList( filteredBlockPatterns ); return ( diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index ae941c977e14fd..a318fd285cdc33 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -21,7 +21,6 @@ import { MenuItem, ToolbarButton, } from '@wordpress/components'; -import { useAsyncList } from '@wordpress/compose'; import { __, sprintf } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; import { useState } from '@wordpress/element'; @@ -85,7 +84,6 @@ function TemplatesList( { area, clientId, isEntityAvailable, onSelect } ) { isEntityAvailable && !! blockPatterns.length && ( area === 'header' || area === 'footer' ); - const shownTemplates = useAsyncList( blockPatterns ); if ( ! canReplace ) { return null; @@ -96,7 +94,6 @@ function TemplatesList( { area, clientId, isEntityAvailable, onSelect } ) { diff --git a/packages/block-library/src/template-part/edit/selection-modal.js b/packages/block-library/src/template-part/edit/selection-modal.js index 5fa5d9786d8c96..71093306b93b2f 100644 --- a/packages/block-library/src/template-part/edit/selection-modal.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -5,7 +5,6 @@ import { useMemo, useState } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; import { SearchControl, @@ -43,7 +42,6 @@ export default function TemplatePartSelectionModal( { return searchPatterns( partsAsPatterns, searchValue ); }, [ templateParts, searchValue ] ); - const shownTemplateParts = useAsyncList( filteredTemplateParts ); const blockPatterns = useAlternativeBlockPatterns( area, clientId ); const filteredBlockPatterns = useMemo( () => { return searchPatterns( blockPatterns, searchValue ); @@ -89,7 +87,6 @@ export default function TemplatePartSelectionModal( {

{ __( 'Existing template parts' ) }

{ onTemplatePartSelect( pattern.templatePart ); } } diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index d31ab188c0b6d6..010132ac971db1 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 7cca4a7ca1a68b..d430d2afeebf5a 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -25,6 +25,7 @@ "npm": ">=8.19.2" }, "main": "parser.js", + "wpScript": true, "sideEffects": false, "dependencies": { "pegjs": "^0.10.0", @@ -37,6 +38,5 @@ "build": "concurrently \"npm run build:js\" \"npm run build:php\"", "build:js": "pegjs --format commonjs -o ./parser.js ./grammar.pegjs", "build:php": "node bin/create-php-parser.js" - }, - "wpScript": true + } } diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 2d944fe656363b..42890e9b7d56bc 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "{src,build,build-module}/{index.js,store/index.js}" ], @@ -62,6 +63,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/commands/package.json b/packages/commands/package.json index 98eadca595ca86..cebf3237a00d75 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/components": "*", @@ -44,6 +45,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/components/package.json b/packages/components/package.json index faf923ad426bf6..6871511cf5b1e5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -25,11 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, + "types": "build-types", "sideEffects": [ "build-style/**", "src/**/*.scss" ], - "types": "build-types", "dependencies": { "@ariakit/react": "^0.4.10", "@babel/runtime": "7.25.7", @@ -83,6 +84,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/compose/package.json b/packages/compose/package.json index adff24f6663006..68b00b24298d87 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -49,6 +50,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json index ac15b3578cf6ff..db2fb900865c80 100644 --- a/packages/core-commands/package.json +++ b/packages/core-commands/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", @@ -48,6 +49,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 44ed3aaad5dd3a..a7216e931a70ca 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": [ "{src,build,build-module}/index.js" @@ -60,6 +61,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/core-data/src/private-apis.js b/packages/core-data/src/private-apis.js index 443db97957285d..e9fcf36f7e6090 100644 --- a/packages/core-data/src/private-apis.js +++ b/packages/core-data/src/private-apis.js @@ -2,9 +2,11 @@ * Internal dependencies */ import { useEntityRecordsWithPermissions } from './hooks/use-entity-records'; +import { RECEIVE_INTERMEDIATE_RESULTS } from './utils'; import { lock } from './lock-unlock'; export const privateApis = {}; lock( privateApis, { useEntityRecordsWithPermissions, + RECEIVE_INTERMEDIATE_RESULTS, } ); diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index ae0c7f456e533d..a35403c0493460 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -21,6 +21,7 @@ import { getUserPermissionCacheKey, getUserPermissionsFromAllowHeader, ALLOWED_RESOURCE_ACTIONS, + RECEIVE_INTERMEDIATE_RESULTS, } from './utils'; import { getSyncProvider } from './sync'; import { fetchBlockPatterns } from './fetch'; @@ -245,6 +246,14 @@ export const getEntityRecords = { exclusive: false } ); + const key = entityConfig.key || DEFAULT_ENTITY_KEY; + + function getResolutionsArgs( records ) { + return records + .filter( ( record ) => record?.[ key ] ) + .map( ( record ) => [ kind, name, record[ key ] ] ); + } + try { if ( query._fields ) { // If requesting specific fields, items and query association to said @@ -267,7 +276,8 @@ export const getEntityRecords = ...query, } ); - let records, meta; + let records = [], + meta; if ( entityConfig.supportsPagination && query.per_page !== -1 ) { const response = await apiFetch( { path, parse: false } ); records = Object.values( await response.json() ); @@ -279,6 +289,44 @@ export const getEntityRecords = response.headers.get( 'X-WP-TotalPages' ) ), }; + } else if ( + query.per_page === -1 && + query[ RECEIVE_INTERMEDIATE_RESULTS ] === true + ) { + let page = 1; + let totalPages; + + do { + const response = await apiFetch( { + path: addQueryArgs( path, { page, per_page: 100 } ), + parse: false, + } ); + const pageRecords = Object.values( await response.json() ); + + totalPages = parseInt( + response.headers.get( 'X-WP-TotalPages' ) + ); + + records.push( ...pageRecords ); + registry.batch( () => { + dispatch.receiveEntityRecords( + kind, + name, + records, + query + ); + dispatch.finishResolutions( + 'getEntityRecord', + getResolutionsArgs( pageRecords ) + ); + } ); + page++; + } while ( page <= totalPages ); + + meta = { + totalItems: records.length, + totalPages: 1, + }; } else { records = Object.values( await apiFetch( { path } ) ); meta = { @@ -318,11 +366,6 @@ export const getEntityRecords = // See https://github.com/WordPress/gutenberg/pull/26575 // See https://github.com/WordPress/gutenberg/pull/64504 if ( ! query?._fields && ! query.context ) { - const key = entityConfig.key || DEFAULT_ENTITY_KEY; - const resolutionsArgs = records - .filter( ( record ) => record?.[ key ] ) - .map( ( record ) => [ kind, name, record[ key ] ] ); - const targetHints = records .filter( ( record ) => record?.[ key ] ) .map( ( record ) => ( { @@ -356,7 +399,7 @@ export const getEntityRecords = ); dispatch.finishResolutions( 'getEntityRecord', - resolutionsArgs + getResolutionsArgs( records ) ); dispatch.finishResolutions( 'canUser', diff --git a/packages/core-data/src/utils/index.js b/packages/core-data/src/utils/index.js index 189635647779e5..db10359bda07dd 100644 --- a/packages/core-data/src/utils/index.js +++ b/packages/core-data/src/utils/index.js @@ -14,3 +14,4 @@ export { getUserPermissionsFromAllowHeader, ALLOWED_RESOURCE_ACTIONS, } from './user-permissions'; +export { RECEIVE_INTERMEDIATE_RESULTS } from './receive-intermediate-results'; diff --git a/packages/core-data/src/utils/receive-intermediate-results.js b/packages/core-data/src/utils/receive-intermediate-results.js new file mode 100644 index 00000000000000..53d2295b28b390 --- /dev/null +++ b/packages/core-data/src/utils/receive-intermediate-results.js @@ -0,0 +1,3 @@ +export const RECEIVE_INTERMEDIATE_RESULTS = Symbol( + 'RECEIVE_INTERMEDIATE_RESULTS' +); diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index ef6748f34205e8..41b3a7bd463aa3 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -23,6 +23,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/block-editor": "*", @@ -55,6 +56,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index 948efe7fb64022..e4898ff1d0e1ab 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -38,6 +39,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/data/package.json b/packages/data/package.json index 0e46b1a366918e..fd1ef7ef4d7489 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -50,6 +51,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index d61df206409152..ff20386862929e 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -1,9 +1,9 @@ # The `@wordpress/dataviews` package -The DataViews package offers two components to work with a given dataset: +The DataViews package offers two React components and a few utilites to work with a list of data: -- `DataViews`: allows rendering a dataset using different types of layouts (table, grid, list) and interaction capabilities (search, filters, sorting, etc.). -- `DataForm`: allows editing the items from the same dataset. +- `DataViews`: to render the dataset using different types of layouts (table, grid, list) and interaction capabilities (search, filters, sorting, etc.). +- `DataForm`: to edit the items of the dataset. ## Installation @@ -15,15 +15,19 @@ npm install @wordpress/dataviews --save ## `DataViews` + + ### Usage -The component is data agnostic, it just requires the data to be an array of objects with an unique identifier — it can work with data coming from a static (e.g.: JSON file) or dynamic source (e.g.: HTTP Request). Consumers are responsible to query the data source appropiately: +The `DataViews` component receives data and some other configuration to render the dataset. It'll call the `onChangeView` callback every time the user has interacted with the dataset in some way (sorted, filtered, changed layout, etc.): ![DataViews flow](https://developer.wordpress.org/files/2024/09/368600071-20aa078f-7c3d-406d-8dd0-8b764addd22a.png "DataViews flow") +Example: + ```jsx const Example = () => { - // Declare data, fields, etc. + const onChangeView = () => { /* React to user changes. */ } return ( { }; ``` - ### Properties #### `data`: `Object[]` -The dataset to work with, represented as a one-dimensional array. +A one-dimensional array of objects. Example: @@ -63,11 +66,28 @@ const data = [ ]; ``` -By default, dataviews would use each record's `id` as an unique identifier. If the records don't have a `id` property that identify them uniquely, they consumer needs to provide a `getItemId` function that returns an unique identifier for the record. +The data can come from anywhere, from a static JSON file to a dynamic source like a HTTP Request. It's the consumer's responsiblity to query the data source appropiately and update the dataset based on the user's choices for sorting, filtering, etc. + +Each record should have an `id` that identifies them uniquely. If they don't, the consumer should provide the `getItemId` property to `DataViews`: a function that returns an unique identifier for the record. + +#### `getItemId`: `function` + +Function that receives an item and returns an unique identifier for it. + +It's optional. The field will get a default implementation by `DataViews` that returns the value of the `item[ id ]`. + +Example: + +```js +// Custom getItemId function. +{ + getItemId={ ( item ) => item.name ?? item.id } +} +``` #### `fields`: `Object[]` -The fields describe the visible items for each record in the dataset. +The fields describe the visible items for each record in the dataset and how they behave (how to sort them, display them, etc.). See "Fields API" for a description of every property. Example: @@ -95,7 +115,7 @@ const fields = [ }, { id: 'author', - label: __( 'Author' ), + label: 'Author', render: ( { item } ) => { return { item.author }; }, @@ -110,7 +130,7 @@ const fields = [ }, { id: 'status', - label: __( 'Status' ), + label: 'Status', getValue: ( { item } ) => STATUSES.find( ( { value } ) => value === item.status )?.label ?? item.status, @@ -123,49 +143,6 @@ const fields = [ ]; ``` -Each field is an object with the following properties: - -- `id`: identifier for the field. Unique. -- `type`: the type of the field. See "Field types" section. -- `label`: the field's name to be shown in the UI. -- `header`: defaults to the label. Allows providing a React element to render the field labels. -- `getValue`: function that returns the value of the field, defaults to `field[id]`. -- `render`: function that renders the field. Optional, `getValue` will be used if `render` is not defined. -- `Edit`: function that renders an edit control for the field. Alternatively, can provide a string declaring one of default controls provided by DataForm `text`, `integer`, `datetime`, `radio`, `select`. -- `sort`: a compare function that determines the order of the records. -- `isValid`: callback to decide if a field should be displayed. -- `isVisible`: callback to decide if a field should be visible. -- `enableSorting`: whether the data can be sorted by the given field. True by default. -- `enableHiding`: whether the field can be hidden. True by default. -- `enableGlobalSearch`: whether the field is searchable. False by default. -- elements: The list of options to pick from when using the field as a filter or when editing (DataForm component). Providing a list of to the field automatically creates a filter for it. It expects an array of objects with the following properties: - - `value`: The id of the value to filter to (for internal use) - - `label`: The text that will be displayed in the UI for the item. - - `description`: A longer description that describes the element, to also be displayed. Optional. -- `filterBy`: configuration for the filters enabled by the `elements` property. - - `operators`: the list of operators supported by the field. See "Filter operators" below. - - `isPrimary`: whether it is a primary filter. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter. - -##### Field types - -Current supported types include: `text`, `integer`, `datetime`. - -If a field declares a `type` the `sort`, `isValid`, and `Edit` functions will be provided with default implementations. They will overriden if the field provides its own. - -##### Filter operators - - -| Operator | Selection | Description | Example | -| ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- | -| `is` | Single item | `EQUAL TO`. The item's field is equal to a single value. | Author is Admin | -| `isNot` | Single item | `NOT EQUAL TO`. The item's field is not equal to a single value. | Author is not Admin | -| `isAny` | Multiple items | `OR`. The item's field is present in a list of values. | Author is any: Admin, Editor | -| `isNone` | Multiple items | `NOT OR`. The item's field is not present in a list of values. | Author is none: Admin, Editor | -| `isAll` | Multiple items | `AND`. The item's field has all of the values in the list. | Category is all: Book, Review, Science Fiction | -| `isNotAll` | Multiple items | `NOT AND`. The item's field doesn't have all of the values in the list. | Category is not all: Book, Review, Science Fiction | - -`is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item. - #### `view`: `Object` The view object configures how the dataset is visible to the user. @@ -347,12 +324,6 @@ Whether the search input is enabled. `true` by default. What text to show in the search input. "Search" by default. -#### `getItemId`: `function` - -Function that receives an item and returns an unique identifier for it. - -By default, dataviews would use each record's `id` as an unique identifier. If the records don't have a `id` property that identify them uniquely, they consumer needs to provide a `getItemId` function that returns an unique identifier for the record. - #### `isLoading`: `boolean` Whether the data is loading. `false` by default. @@ -393,6 +364,8 @@ React component to be rendered next to the view config button. ## `DataForm` + + ### Usage ```jsx @@ -410,19 +383,43 @@ const Example = () => { } ``` - - ### Properties #### `data`: `Object` A single item to be edited. -This could be thought as as a single record coming from the `data` property of `DataViews` — though it doesn't need to be; it can be, for example, a mix of records if you support bulk editing. +It can be think of as a single record coming from the `data` property of `DataViews` — though it doesn't need to be. It can be totally separated or a mix of records if your app supports bulk editing. #### `fields`: `Object[]` -Same as `fields` property of `DataViews`. +The fields describe which parts of the data are visible and how they behave (how to edit them, validate them, etc.). See "Fields API" for a description of every property. + +Example: + +```js +const fields = [ + { + id: 'title', + type: 'text', + label: 'Title', + }, + { + id: 'date', + type: 'datetime', + label: 'Date', + }, + { + id: 'author', + type: 'text' + label: 'Author', + elements: [ + { value: 1, label: 'Admin' }, + { value: 2, label: 'User' }, + ], + }, +]; +``` #### `form`: `Object[]` @@ -497,6 +494,389 @@ Parameters: Returns a boolean indicating if the item is valid (true) or not (false). +## Fields API + +### `id` + +The unique identifier of the field. + +- Type: `string`. +- Required. + +Example: + +```js +{ id: 'field_id' } +``` + +### `type` + +Field type. One of `text`, `integer`, `datetime`. + +If a field declares a `type`, it gets default implementations for the `sort`, `isValid`, and `Edit` functions. They will overriden if the field provides its own. + +- Type: `string`. +- Optional. + +Example: + +```js +{ type: 'text' } +``` + +### `label` + +The field's name. This will be used across the UI. + +- Type: `string`. +- Optional. +- Defaults to the `id` value. + +Example: + +```js +{ label: 'Title' } +``` + +### `header` + +React component used by the layouts to display the field name — useful to add icons, etc. It's complementary to the `label` property. + +- Type: React component. +- Optional. +- Defaults to the `label` value. +- Props: none. +- Returns a React element that represents the field's name. + +Example: + +```js +{ + header: () => { /* Returns a react element. */ } +} +``` + +### `getValue` + +React component that returns the value of a field. This value is used in sorting the fields, or when filtering. + +- Type: React component. +- Optional. +- Defaults to `item[ id ]`. +- Props: + - `item` value to be processed. +- Returns a value that represents the field. + +Example: + +```js +{ + getValue: ( { item } ) => { /* The field's value. */ }; +} +``` + +### `render` + +React component that renders the field. This is used by the layouts. + +- Type: React component. +- Optional. +- Defaults to `getValue`. +- Props + - `item` value to be processed. +- Returns a React element that represents the field's value. + +Example: + +```js +{ + render: ( { item} ) => { /* React element to be displayed. */ } +} +``` + +### `Edit` + +React component that renders the control to edit the field. + +- Type: React component | `string`. If it's a string, it needs to be one of `text`, `integer`, `datetime`, `radio`, `select`. +- Required by DataForm. Optional if the field provided a `type`. +- Props: + - `data`: the item to be processed + - `field`: the field definition + - `onChange`: the callback with the updates + - `hideLabelFromVision`: boolean representing if the label should be hidden +- Returns a React element to edit the field's value. + +Example: + +```js +// A custom control defined by the field. +{ + Edit: ( { + data, + field, + onChange, + hideLabelFromVision + } ) => { + const value = field.getValue( { item: data } ); + + return ( + + ); + } +} +``` + +```js +// Use one of the core controls. +{ + Edit: 'radio' +} +``` + +```js +// Edit is optional when field's type is present. +// The field will use the default Edit function for text. +{ + type: 'text' +} +``` + +```js +// Edit can be provided even if field's type is present. +// The field will use its own custom control. +{ + type: 'text', + Edit: 'radio' +} +``` + +### `sort` + +Function to sort the records. + +- Type: `function`. +- Optional. +- Args + - `a`: the first item to compare + - `b`: the second item to compare + - `direction`: either `asc` (ascending) or `desc` (descending) +- Returns a number where: + - a negative value indicates that `a` should come before `b` + - a positive value indicates that `a` should come after `b` + - 0 indicates that `a` and `b` are considered equal + +Example: + +```js +// A custom sort function defined by the field. +{ + sort: ( a, b, direction ) => { + return direction === 'asc' + ? a.localeCompare( b ) + : b.localeCompare( a ); + } +} +``` + +```js +// If field type is provided, +// the field gets a default sort function. +{ + type: 'number' +} +``` + +```js +// Even if a field type is provided, +// fields can override the default sort function assigned for that type. +{ + type: 'number' + sort: ( a, b, direction ) => { /* Custom sort */ } +} +``` + +### `isValid` + +Function to validate a field's value. + +- Type: function. +- Optional. +- Args + - `item`: the data to validate + - `context`: an object containing the following props: + - `elements`: the elements defined by the field +- Returns a boolean, indicating if the field is valid or not. + +Example: + +```js +// Custom isValid function. +{ + isValid: ( item, context ) => { + return !! item; + } +} +``` + +```js +// If the field defines a type, +// it'll get a default isValid function for the type. +{ + type: 'number', +} +``` + +```js +// Even if the field provides a type, +// the field can override the default isValid function. +{ + type: 'number', + isValid: ( item, context ) => { /* Custom function. */ } +} +``` + +### `isVisible` + +Function that indicates if the field should be visible. + +- Type: `function`. +- Optional. +- Args + - `item`: the data to be processed +- Returns a `boolean` indicating if the field should be visible (`true`) or not (`false`). + +Example: + +```js +// Custom isVisible function. +{ + isVisible: ( item ) => { /* Custom implementation. */ } +} +``` + +### `enableSorting` + +Boolean indicating if the field is sortable. + +- Type: `boolean`. +- Optional. +- Defaults to `true`. + +Example: + +```js +{ enableSorting: true } +``` + +### `enableHiding` + +Boolean indicating if the field can be hidden. + +- Type: `boolean`. +- Optional. +- Defaults to `true`. + +Example: + +```js +{ enableHiding: true } +``` + +### `enableGlobalSearch` + +Boolean indicating if the field is searchable. + +- Type: `boolean`. +- Optional. +- Defaults to `false`. + +Example: + +```js +{ enableGlobalSearch: true } +``` + +### `elements` + +List of valid values for a field. If provided, it creates a DataViews' filter for the field. DataForm's edit control will use these values as well (see `Edit` field property). + +- Type: `array` of objects. +- Optional. +- Each object can have the following properties: + - `value`: required, the value to match against the field's value. + - `label`: required, the name to display to users. + - `description`: optional, a longer description of the item. + +Example: + +```js +{ + elements: [ + { value: '1', label: 'Product A' }, + { value: '2', label: 'Product B' }, + { value: '3', label: 'Product C' }, + { value: '4', label: 'Product D' }, + ] +} +``` + +### `filterBy` + +Configuration of the filters. + +- Type: `object`. +- Optional. +- Properties: + - `operators`: the list of operators supported by the field. See "operators" below. By default, a filter will support the `isAny` and `isNone` multi-selection operators. + - `isPrimary`: boolean, optional. Indicates if the filter is primary. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter. + +Operators: + +| Operator | Selection | Description | Example | +| ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- | +| `is` | Single item | `EQUAL TO`. The item's field is equal to a single value. | Author is Admin | +| `isNot` | Single item | `NOT EQUAL TO`. The item's field is not equal to a single value. | Author is not Admin | +| `isAny` | Multiple items | `OR`. The item's field is present in a list of values. | Author is any: Admin, Editor | +| `isNone` | Multiple items | `NOT OR`. The item's field is not present in a list of values. | Author is none: Admin, Editor | +| `isAll` | Multiple items | `AND`. The item's field has all of the values in the list. | Category is all: Book, Review, Science Fiction | +| `isNotAll` | Multiple items | `NOT AND`. The item's field doesn't have all of the values in the list. | Category is not all: Book, Review, Science Fiction | + +`is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item. + +Example: + +```js +// Set a filter as primary. +{ + filterBy: { + isPrimary: true + } +} +``` + +```js +// Configure a filter as single-selection. +{ + filterBy: { + operators: [ `is`, `isNot` ] + } +} +``` + +```js +// Configure a filter as multi-selection with all the options. +{ + filterBy: { + operators: [ `isAny`, `isNone`, `isAll`, `isNotAll` ] + } +} +``` + ## Contributing to this package This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 5599a84b92d34c..df30fea1a1c714 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -47,6 +48,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/date/package.json b/packages/date/package.json index f53c6314912271..d67c1dc527caee 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 69d2414b0c11a2..64ffc6cd30b251 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 0202229b202842..6e7986c4965dc6 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/dom/package.json b/packages/dom/package.json index 58c572545cc526..97576e9a22e0ff 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index c594722d180e4f..1b28c1d5f31aa6 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -66,6 +67,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 95f9cd5f05742a..41bf39c1e08332 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@react-spring/web": "^9.4.5", @@ -60,7 +61,6 @@ "@wordpress/plugins": "*", "@wordpress/preferences": "*", "@wordpress/primitives": "*", - "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/reusable-blocks": "*", "@wordpress/router": "*", @@ -82,6 +82,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/edit-site/src/components/editor-canvas-container/style.scss b/packages/edit-site/src/components/editor-canvas-container/style.scss index 0bdbc2bbe32355..52ac29da0696f6 100644 --- a/packages/edit-site/src/components/editor-canvas-container/style.scss +++ b/packages/edit-site/src/components/editor-canvas-container/style.scss @@ -1,6 +1,5 @@ .edit-site-editor-canvas-container { height: 100%; - background-color: $gray-300; // Controls height of editor and editor canvas container (style book, global styles revisions previews etc.) iframe { diff --git a/packages/edit-site/src/components/page-patterns/fields.js b/packages/edit-site/src/components/page-patterns/fields.js index 74433b3de72f0e..e016dca6cd8557 100644 --- a/packages/edit-site/src/components/page-patterns/fields.js +++ b/packages/edit-site/src/components/page-patterns/fields.js @@ -10,7 +10,7 @@ import { __experimentalHStack as HStack, Button, Tooltip, - Flex, + FlexBlock, } from '@wordpress/components'; import { __, _x } from '@wordpress/i18n'; import { useState, useMemo, useId } from '@wordpress/element'; @@ -25,7 +25,6 @@ import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies */ -import { Async } from '../async'; import { PATTERN_TYPES, TEMPLATE_PART_POST_TYPE, @@ -88,12 +87,12 @@ function PreviewField( { item } ) { { isEmpty && isTemplatePart && __( 'Empty template part' ) } { isEmpty && ! isTemplatePart && __( 'Empty pattern' ) } { ! isEmpty && ( - + - + ) } { !! description && ( @@ -123,12 +122,7 @@ function TitleField( { item } ) { const title = decodeEntities( defaultGetTitle( item ) ); return ( - + { item.type === PATTERN_TYPES.theme ? ( title ) : ( @@ -143,7 +137,7 @@ function TitleField( { item } ) { { title } ) } - + { item.type === PATTERN_TYPES.theme && ( { isEmpty && __( 'Empty template' ) } { ! isEmpty && ( - + - + ) }
diff --git a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js index 3498bed4c99a56..2dbd143766e67c 100644 --- a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js +++ b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js @@ -96,7 +96,32 @@ function useResolveEditedEntityAndContext( { postId, postType } ) { postTypeToResolve === 'page' && homepageId === postIdToResolve ) { - return getDefaultTemplateId( { slug: 'front-page' } ); + // The /lookup endpoint cannot currently handle a lookup + // when a page is set as the front page, so specifically in + // that case, we want to check if there is a front page + // template, and instead of falling back to the home + // template, we want to fall back to the page template. + const templates = getEntityRecords( + 'postType', + TEMPLATE_POST_TYPE, + { + per_page: -1, + } + ); + if ( templates ) { + const id = templates?.find( + ( { slug } ) => slug === 'front-page' + )?.id; + if ( id ) { + return id; + } + + // If no front page template is found, continue with the + // logic below (fetching the page template). + } else { + // Still resolving `templates`. + return undefined; + } } const editedEntity = getEditedEntityRecord( diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 0629dca5ec1052..331dbc742dbc8c 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -63,6 +64,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/editor/README.md b/packages/editor/README.md index bcd1727e046d07..e0b53362089f1b 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -1631,10 +1631,6 @@ _Related_ - -_Type_ - -- `Object` - ### storeConfig Post editor data store configuration. @@ -1643,10 +1639,6 @@ _Related_ - -_Type_ - -- `Object` - ### TableOfContents Renders a table of contents component. diff --git a/packages/editor/package.json b/packages/editor/package.json index 61cce478cf08fe..98e0c6d2255b74 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -25,6 +25,8 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, + "types": "build-types", "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -85,6 +87,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/editor/src/components/document-tools/index.js b/packages/editor/src/components/document-tools/index.js index b2295b555ce080..74118caaf5849c 100644 --- a/packages/editor/src/components/document-tools/index.js +++ b/packages/editor/src/components/document-tools/index.js @@ -35,6 +35,7 @@ function DocumentTools( { className, disableBlockTools = false } ) { inserterSidebarToggleRef, listViewToggleRef, showIconLabels, + showTools, } = useSelect( ( select ) => { const { get } = select( preferencesStore ); const { @@ -42,6 +43,8 @@ function DocumentTools( { className, disableBlockTools = false } ) { getEditorMode, getInserterSidebarToggleRef, getListViewToggleRef, + getRenderingMode, + getCurrentPostType, } = unlock( select( editorStore ) ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); @@ -56,6 +59,9 @@ function DocumentTools( { className, disableBlockTools = false } ) { showIconLabels: get( 'core', 'showIconLabels' ), isDistractionFree: get( 'core', 'distractionFree' ), isVisualMode: getEditorMode() === 'visual', + showTools: + getRenderingMode() !== 'post-only' || + getCurrentPostType() === 'wp_template', }; }, [] ); @@ -128,7 +134,7 @@ function DocumentTools( { className, disableBlockTools = false } ) { ) } { ( isWideViewport || ! showIconLabels ) && ( <> - { isLargeViewport && ( + { showTools && isLargeViewport && ( ); diff --git a/packages/editor/src/components/post-transform-panel/index.js b/packages/editor/src/components/post-transform-panel/index.js index 08139c2c3c6645..88ca6dc3965a11 100644 --- a/packages/editor/src/components/post-transform-panel/index.js +++ b/packages/editor/src/components/post-transform-panel/index.js @@ -5,7 +5,6 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { PanelBody } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useAsyncList } from '@wordpress/compose'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; import { serialize } from '@wordpress/blocks'; @@ -20,7 +19,6 @@ import { } from '../../store/constants'; function TemplatesList( { availableTemplates, onSelect } ) { - const shownTemplates = useAsyncList( availableTemplates ); if ( ! availableTemplates || availableTemplates?.length === 0 ) { return null; } @@ -29,7 +27,6 @@ function TemplatesList( { availableTemplates, onSelect } ) { diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 170ec96c107288..f5c45f431e2c85 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -7,6 +7,7 @@ import { store as coreStore, __experimentalFetchLinkSuggestions as fetchLinkSuggestions, __experimentalFetchUrlData as fetchUrlData, + privateApis as coreDataPrivateApis, } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -29,17 +30,12 @@ import { useGlobalStylesContext } from '../global-styles-provider'; const EMPTY_OBJECT = {}; function __experimentalReusableBlocksSelect( select ) { - const { getEntityRecords, hasFinishedResolution } = select( coreStore ); - const reusableBlocks = getEntityRecords( 'postType', 'wp_block', { + const { RECEIVE_INTERMEDIATE_RESULTS } = unlock( coreDataPrivateApis ); + const { getEntityRecords } = select( coreStore ); + return getEntityRecords( 'postType', 'wp_block', { per_page: -1, + [ RECEIVE_INTERMEDIATE_RESULTS ]: true, } ); - return hasFinishedResolution( 'getEntityRecords', [ - 'postType', - 'wp_block', - { per_page: -1 }, - ] ) - ? reusableBlocks - : undefined; } const BLOCK_EDITOR_SETTINGS = [ @@ -332,6 +328,10 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { : settings.template, __experimentalSetIsInserterOpened: setIsInserterOpened, [ sectionRootClientIdKey ]: sectionRootClientId, + editorTool: + renderingMode === 'post-only' && postType !== 'wp_template' + ? 'edit' + : undefined, }; return blockEditorSettings; @@ -359,6 +359,7 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { sectionRootClientId, globalStylesData, globalStylesLinksData, + renderingMode, ] ); } diff --git a/packages/editor/src/components/start-page-options/index.js b/packages/editor/src/components/start-page-options/index.js index 07fee67fbed19b..783a4a224fa378 100644 --- a/packages/editor/src/components/start-page-options/index.js +++ b/packages/editor/src/components/start-page-options/index.js @@ -9,7 +9,6 @@ import { __experimentalBlockPatternsList as BlockPatternsList, } from '@wordpress/block-editor'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; import { store as coreStore } from '@wordpress/core-data'; import { __unstableSerializeAndClean } from '@wordpress/blocks'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -66,7 +65,6 @@ export function useStartPatterns() { } function PatternSelection( { blockPatterns, onChoosePattern } ) { - const shownBlockPatterns = useAsyncList( blockPatterns ); const { editEntityRecord } = useDispatch( coreStore ); const { postType, postId } = useSelect( ( select ) => { const { getCurrentPostType, getCurrentPostId } = select( editorStore ); @@ -79,7 +77,6 @@ function PatternSelection( { blockPatterns, onChoosePattern } ) { return ( { editEntityRecord( 'postType', postType, postId, { blocks, diff --git a/packages/editor/src/components/start-template-options/index.js b/packages/editor/src/components/start-template-options/index.js index 3651c5c029a2c7..8d3910341aff43 100644 --- a/packages/editor/src/components/start-template-options/index.js +++ b/packages/editor/src/components/start-template-options/index.js @@ -6,7 +6,6 @@ import { __ } from '@wordpress/i18n'; import { useState, useMemo, useEffect } from '@wordpress/element'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; import { parse } from '@wordpress/blocks'; import { store as coreStore, useEntityBlockEditor } from '@wordpress/core-data'; @@ -111,11 +110,9 @@ function useStartPatterns( fallbackContent ) { function PatternSelection( { fallbackContent, onChoosePattern, postType } ) { const [ , , onChange ] = useEntityBlockEditor( 'postType', postType ); const blockPatterns = useStartPatterns( fallbackContent ); - const shownBlockPatterns = useAsyncList( blockPatterns ); return ( { onChange( blocks, { selection: undefined } ); onChoosePattern(); diff --git a/packages/editor/src/components/visual-editor/style.scss b/packages/editor/src/components/visual-editor/style.scss index fae61eda6b8013..63df28f0f1ba5a 100644 --- a/packages/editor/src/components/visual-editor/style.scss +++ b/packages/editor/src/components/visual-editor/style.scss @@ -1,8 +1,19 @@ .editor-visual-editor { position: relative; display: flex; + + // This duplicates the iframe background but it's necessary in some situations + // when the iframe doesn't cover the whole canvas + // like the "focused entities". background-color: $gray-300; + // This overrides the iframe background since it's applied again here + // It also prevents some style glitches if `editor-visual-editor` + // like when hovering the preview in the site editor. + iframe[name="editor-canvas"] { + background-color: transparent; + } + // Centralize the editor horizontally (flex-direction is column). align-items: center; diff --git a/packages/editor/src/store/constants.ts b/packages/editor/src/store/constants.ts index 78708f00cc9eb7..73d6a104370c37 100644 --- a/packages/editor/src/store/constants.ts +++ b/packages/editor/src/store/constants.ts @@ -8,8 +8,6 @@ export const EDIT_MERGE_PROPERTIES = new Set( [ 'meta' ] ); /** * Constant for the store module (or reducer) key. - * - * @type {string} */ export const STORE_NAME = 'core/editor'; diff --git a/packages/editor/src/store/index.js b/packages/editor/src/store/index.js index 392f5f42cbd55b..a3241a2fc7e184 100644 --- a/packages/editor/src/store/index.js +++ b/packages/editor/src/store/index.js @@ -18,8 +18,6 @@ import { unlock } from '../lock-unlock'; * Post editor data store configuration. * * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#registerStore - * - * @type {Object} */ export const storeConfig = { reducer, @@ -31,8 +29,6 @@ export const storeConfig = { * Store definition for the editor namespace. * * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore - * - * @type {Object} */ export const store = createReduxStore( STORE_NAME, { ...storeConfig, diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index 2396cb67c73e69..db452be5c17abb 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -442,8 +442,8 @@ export function isCurrentPostPending( state ) { /** * Return true if the current post has already been published. * - * @param {Object} state Global application state. - * @param {Object?} currentPost Explicit current post for bypassing registry selector. + * @param {Object} state Global application state. + * @param {Object} [currentPost] Explicit current post for bypassing registry selector. * * @return {boolean} Whether the post has been published. */ diff --git a/packages/element/package.json b/packages/element/package.json index 3004dadb7ea605..4a196255971cfb 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -40,6 +41,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index 72863ce15fd8b8..135876460bd05e 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/fields/package.json b/packages/fields/package.json index e0b7125cf3e4eb..34ef8992f93adf 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": [ "build-style/**", @@ -62,6 +63,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/format-library/package.json b/packages/format-library/package.json index f07ba0f4b61b35..41852910ff5389 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -46,6 +47,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/hooks/package.json b/packages/hooks/package.json index d0f2fd381b83ca..857cb24a877468 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index d717a89bf049b8..4ecab2056c9c3a 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -26,12 +26,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 33de2873f75a6a..53e81b64a65349 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "bin": { "pot-to-php": "./tools/pot-to-php.js" @@ -39,6 +40,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/icons/package.json b/packages/icons/package.json index 125b6f19beaa02..895f2e184b4e48 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -16,7 +16,6 @@ "url": "git+https://github.com/WordPress/gutenberg.git", "directory": "packages/icons" }, - "sideEffects": false, "bugs": { "url": "https://github.com/WordPress/gutenberg/issues" }, @@ -27,7 +26,9 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", + "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/element": "*", @@ -35,6 +36,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/interactivity-router/package.json b/packages/interactivity-router/package.json index ce9b268af556ac..378ddda56ee76b 100644 --- a/packages/interactivity-router/package.json +++ b/packages/interactivity-router/package.json @@ -25,8 +25,8 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "types": "build-types", "wpScriptModuleExports": "./build-module/index.js", + "types": "build-types", "dependencies": { "@wordpress/a11y": "*", "@wordpress/interactivity": "*" diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json index 106ed44bf44d39..c9edd586bc9959 100644 --- a/packages/interactivity/package.json +++ b/packages/interactivity/package.json @@ -25,11 +25,11 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "types": "build-types", "wpScriptModuleExports": { ".": "./build-module/index.js", "./debug": "./build-module/debug.js" }, + "types": "build-types", "dependencies": { "@preact/signals": "^1.3.0", "preact": "^10.24.2" diff --git a/packages/interface/package.json b/packages/interface/package.json index 807135ae0160a6..f6a7952cb0d051 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -52,6 +53,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 8da2643a58b840..7dafdea88e7a4f 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -34,6 +34,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -41,6 +42,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json index 50ee79d554a89f..ce029907c0dbf4 100644 --- a/packages/keyboard-shortcuts/package.json +++ b/packages/keyboard-shortcuts/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/data": "*", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index 3e820ef68cf97e..2038db1984760f 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 129909a948ef1d..328695f31925ee 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -25,6 +25,7 @@ }, "main": "build/index.js", "module": "build-module/index.js", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -41,6 +42,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index 20eef4dd83c6b4..e57b0f51844504 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -25,6 +25,7 @@ }, "main": "build/index.js", "module": "build-module/index.js", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/notices/package.json b/packages/notices/package.json index 34d2253870128b..f150616445b060 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/npm-package-json-lint-config/CHANGELOG.md b/packages/npm-package-json-lint-config/CHANGELOG.md index 9c0aafedcead1a..9dee828f63fea0 100644 --- a/packages/npm-package-json-lint-config/CHANGELOG.md +++ b/packages/npm-package-json-lint-config/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancement + +- Include `exports`, `wpScript`, `wpScriptModuleExports` and `sideEffects` in the `prefer-property-order` rule ([#66239](https://github.com/WordPress/gutenberg/pull/66239)). + ## 5.11.0 (2024-10-30) ## 5.10.0 (2024-10-16) diff --git a/packages/npm-package-json-lint-config/index.js b/packages/npm-package-json-lint-config/index.js index 831970c59bf3a6..e1abd5e34f99c5 100644 --- a/packages/npm-package-json-lint-config/index.js +++ b/packages/npm-package-json-lint-config/index.js @@ -56,8 +56,12 @@ const defaultConfig = { 'type', 'main', 'module', + 'exports', 'react-native', + 'wpScript', + 'wpScriptModuleExports', 'types', + 'sideEffects', 'bin', 'dependencies', 'devDependencies', diff --git a/packages/nux/package.json b/packages/nux/package.json index ccf1cb774c2424..50a4d5acf6d028 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -46,6 +47,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/patterns/package.json b/packages/patterns/package.json index 92ef655dc3c9c7..d29fc9bcf49bca 100644 --- a/packages/patterns/package.json +++ b/packages/patterns/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -53,6 +54,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/plugins/package.json b/packages/plugins/package.json index b741c0359b32b1..a926c2fd9a4e17 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -43,6 +44,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index e2623eac1a021c..05d2cf7031929d 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/preferences/package.json b/packages/preferences/package.json index ef231f359ba143..205f9b5d4966b1 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", @@ -47,6 +48,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 04c3cc86dddec3..d24bc1acf25e2d 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -26,10 +26,11 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, + "types": "build-types", "sideEffects": [ "src/**/*.scss" ], - "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/element": "*", @@ -40,6 +41,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index 91020c0f81db24..8c98b0b3552198 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index 9364a596c39af0..84436470521332 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json index d98a4d7711f89c..31c4d98fa475e0 100644 --- a/packages/react-i18n/package.json +++ b/packages/react-i18n/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -35,6 +36,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 489bc14632ded2..b3aa6fa79474a8 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -40,6 +41,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index 82a812163a0ccc..e2649609016642 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "{src,build,build-module}/{index.js,store/index.js}" ], @@ -48,6 +49,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 4a5fe0f8e9d5ca..f038e097668984 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -25,11 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, + "types": "build-types", "sideEffects": [ "src/**/*.scss", "{src,build,build-module}/{index.js,store/index.js}" ], - "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -47,6 +48,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/router/package.json b/packages/router/package.json index 1822e79a317102..3d80481c9b6baa 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -38,6 +39,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index ca5a44bca62b52..c09cfd1ac9aaaa 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fix + +- Make `start` script more resilient for developer errors ([#66752](https://github.com/WordPress/gutenberg/pull/66752)). + ## 30.4.0 (2024-10-30) ### Enhancements diff --git a/packages/scripts/utils/config.js b/packages/scripts/utils/config.js index dfb44730438c4a..3d99f3784859df 100644 --- a/packages/scripts/utils/config.js +++ b/packages/scripts/utils/config.js @@ -1,7 +1,6 @@ /** * External dependencies */ -const chalk = require( 'chalk' ); const { readFileSync } = require( 'fs' ); const { basename, dirname, extname, join, sep } = require( 'path' ); const { sync: glob } = require( 'fast-glob' ); @@ -21,7 +20,8 @@ const { getBlockJsonModuleFields, getBlockJsonScriptFields, } = require( './block-json' ); -const { log } = console; + +const { warn } = console; // See https://babeljs.io/docs/en/config-files#configuration-file-types. const hasBabelConfig = () => @@ -209,10 +209,8 @@ function getWebpackEntryPoints( buildType ) { // Continue only if the source directory exists. if ( ! hasProjectFile( getWordPressSrcDirectory() ) ) { - log( - chalk.yellow( - `Source directory "${ getWordPressSrcDirectory() }" was not found. Please confirm there is a "src" directory in the root or the value passed to --webpack-src-dir is correct.` - ) + warn( + `Source directory "${ getWordPressSrcDirectory() }" was not found. Please confirm there is a "src" directory in the root or the value passed to --webpack-src-dir is correct.` ); return {}; } @@ -240,12 +238,13 @@ function getWebpackEntryPoints( buildType ) { try { parsedBlockJson = JSON.parse( fileContents ); } catch ( error ) { - chalk.yellow( - `Skipping "${ blockMetadataFile.replace( + warn( + `Not scanning "${ blockMetadataFile.replace( fromProjectRoot( sep ), '' - ) }" due to malformed JSON.` + ) }" due to collect entry points due to malformed JSON.` ); + continue; } const fields = @@ -270,18 +269,16 @@ function getWebpackEntryPoints( buildType ) { // Takes the path without the file extension, and relative to the defined source directory. if ( ! filepath.startsWith( srcDirectory ) ) { - log( - chalk.yellow( - `Skipping "${ value.replace( - 'file:', - '' - ) }" listed in "${ blockMetadataFile.replace( - fromProjectRoot( sep ), - '' - ) }". File is located outside of the "${ getWordPressSrcDirectory() }" directory.` - ) + warn( + `Skipping "${ value.replace( + 'file:', + '' + ) }" listed in "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }". File is located outside of the "${ getWordPressSrcDirectory() }" directory.` ); - return; + continue; } const entryName = filepath .replace( extname( filepath ), '' ) @@ -298,18 +295,16 @@ function getWebpackEntryPoints( buildType ) { ); if ( ! entryFilepath ) { - log( - chalk.yellow( - `Skipping "${ value.replace( - 'file:', - '' - ) }" listed in "${ blockMetadataFile.replace( - fromProjectRoot( sep ), - '' - ) }". File does not exist in the "${ getWordPressSrcDirectory() }" directory.` - ) + warn( + `Skipping "${ value.replace( + 'file:', + '' + ) }" listed in "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }". File does not exist in the "${ getWordPressSrcDirectory() }" directory.` ); - return; + continue; } entryPoints[ entryName ] = entryFilepath; } @@ -334,10 +329,8 @@ function getWebpackEntryPoints( buildType ) { } ); if ( ! entryFile ) { - log( - chalk.yellow( - `No entry file discovered in the "${ getWordPressSrcDirectory() }" directory.` - ) + warn( + `No entry file discovered in the "${ getWordPressSrcDirectory() }" directory.` ); return {}; } @@ -370,13 +363,24 @@ function getPhpFilePaths( context, props ) { const srcDirectory = fromProjectRoot( context + sep ); return blockMetadataFiles.flatMap( ( blockMetadataFile ) => { - const blockJson = JSON.parse( readFileSync( blockMetadataFile ) ); - const paths = []; + let parsedBlockJson; + try { + parsedBlockJson = JSON.parse( readFileSync( blockMetadataFile ) ); + } catch ( error ) { + warn( + `Not scanning "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }" due to detect render files due to malformed JSON.` + ); + return paths; + } + for ( const prop of props ) { if ( - typeof blockJson?.[ prop ] !== 'string' || - ! blockJson[ prop ]?.startsWith( 'file:' ) + typeof parsedBlockJson?.[ prop ] !== 'string' || + ! parsedBlockJson[ prop ]?.startsWith( 'file:' ) ) { continue; } @@ -384,21 +388,19 @@ function getPhpFilePaths( context, props ) { // Removes the `file:` prefix. const filepath = join( dirname( blockMetadataFile ), - blockJson[ prop ].replace( 'file:', '' ) + parsedBlockJson[ prop ].replace( 'file:', '' ) ); // Takes the path without the file extension, and relative to the defined source directory. if ( ! filepath.startsWith( srcDirectory ) ) { - log( - chalk.yellow( - `Skipping "${ blockJson[ prop ].replace( - 'file:', - '' - ) }" listed in "${ blockMetadataFile.replace( - fromProjectRoot( sep ), - '' - ) }". File is located outside of the "${ context }" directory.` - ) + warn( + `Skipping "${ parsedBlockJson[ prop ].replace( + 'file:', + '' + ) }" listed in "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }". File is located outside of the "${ context }" directory.` ); continue; } diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index 0bb27f17ad62d0..cdfe9679c5049d 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -45,6 +46,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index f85efeb91e1803..c476788e3cfaf8 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "memize": "^2.0.1" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index 76ce2ccbb1445d..378f4dce91dad9 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -35,6 +36,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/sync/package.json b/packages/sync/package.json index 95be09803a75f1..d7181479327d3a 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -41,6 +42,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/token-list/package.json b/packages/token-list/package.json index 0d966edeb9ee7d..370d50e5b4700a 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/undo-manager/package.json b/packages/undo-manager/package.json index f0464d7fd2f9e2..66f895f8da887d 100644 --- a/packages/undo-manager/package.json +++ b/packages/undo-manager/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/url/package.json b/packages/url/package.json index 885efe28f8e8b4..2ecdb8eae18113 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 05e098f65e1aeb..224da8a871addf 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/compose": "*", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/vips/package.json b/packages/vips/package.json index 810b8d2b5c9fc4..69912b2eaed71e 100644 --- a/packages/vips/package.json +++ b/packages/vips/package.json @@ -25,12 +25,12 @@ }, "main": "build/index.js", "module": "build-module/index.js", + "wpScript": true, "types": "build-types", "dependencies": { "wasm-vips": "^0.0.10" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/warning/package.json b/packages/warning/package.json index cae2aefa421ffe..d9c5a4dd83dc10 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -25,10 +25,10 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/widgets/package.json b/packages/widgets/package.json index b7fa362d615856..a755890c48ed92 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -23,6 +23,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -44,6 +45,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index a9f8304f79d8ff..117e2227a926c8 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/storybook/stories/playground/zoom-out/index.js b/storybook/stories/playground/zoom-out/index.js index 4f2efcf48523e8..8b72a831d710e8 100644 --- a/storybook/stories/playground/zoom-out/index.js +++ b/storybook/stories/playground/zoom-out/index.js @@ -50,7 +50,7 @@ export default function EditorZoomOut( { zoomLevel } ) {
event.stopPropagation() } - style={ { background: '#ddd', border: '1px solid gray' } } + style={ { border: '1px solid gray' } } > { - test.beforeEach( async ( { admin, editor } ) => { - await admin.createNewPost(); - await expect( - editor.canvas.getByRole( 'textbox', { name: 'Add title' } ) - ).toBeFocused(); + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'emptytheme' ); + } ); + + test.beforeEach( async ( { admin } ) => { + await admin.visitSiteEditor( { + postId: 'emptytheme//index', + postType: 'wp_template', + canvas: 'edit', + } ); } ); test.afterAll( async ( { requestUtils } ) => { - await requestUtils.deleteAllPosts(); + await requestUtils.activateTheme( 'twentytwentyone' ); } ); test( 'Should prevent selecting intermediary blocks', async ( { editor, page, } ) => { + // Clear all content + await editor.setContent( '' ); + // Insert a section with a nested block and an editable block. await editor.insertBlock( { name: 'core/group', diff --git a/test/e2e/specs/site-editor/template-hierarchy.spec.js b/test/e2e/specs/site-editor/template-hierarchy.spec.js new file mode 100644 index 00000000000000..5fcc77da9942ce --- /dev/null +++ b/test/e2e/specs/site-editor/template-hierarchy.spec.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Template hierarchy', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyfour' ); + } ); + + test.afterEach( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test( 'shows correct template with page on front option', async ( { + admin, + page, + editor, + } ) => { + await admin.visitAdminPage( 'options-reading.php' ); + await page.click( 'input[name="show_on_front"][value="page"]' ); + await page.selectOption( 'select[name="page_on_front"]', '2' ); + await page.click( 'input[type="submit"]' ); + await admin.visitSiteEditor(); + + // Title block should contain "Sample Page" + await expect( + editor.canvas.locator( 'role=document[name="Block: Title"]' ) + ).toContainText( 'Sample Page' ); + + await admin.visitAdminPage( 'options-reading.php' ); + await page.click( 'input[name="show_on_front"][value="posts"]' ); + await page.click( 'input[type="submit"]' ); + } ); +} ); diff --git a/test/performance/fixtures/perf-utils.ts b/test/performance/fixtures/perf-utils.ts index 83213a59520dd5..592e8194852e3b 100644 --- a/test/performance/fixtures/perf-utils.ts +++ b/test/performance/fixtures/perf-utils.ts @@ -150,7 +150,7 @@ export class PerfUtils { } /** - * Generates and loads a 1000 empty paragraphs into the editor canvas. + * Generates and loads a 1000 paragraphs into the editor canvas. */ async load1000Paragraphs() { await this.page.waitForFunction( @@ -161,7 +161,7 @@ export class PerfUtils { const { createBlock } = window.wp.blocks; const { dispatch } = window.wp.data; const blocks = Array.from( { length: 1000 } ).map( () => - createBlock( 'core/paragraph' ) + createBlock( 'core/paragraph', { content: 'paragraph' } ) ); dispatch( 'core/block-editor' ).resetBlocks( blocks ); } ); diff --git a/test/performance/specs/post-editor.spec.js b/test/performance/specs/post-editor.spec.js index becbf375eff2ac..85d334749f6f51 100644 --- a/test/performance/specs/post-editor.spec.js +++ b/test/performance/specs/post-editor.spec.js @@ -270,7 +270,7 @@ test.describe( 'Post Editor Performance', () => { const canvas = await perfUtils.getCanvas(); const paragraphs = canvas.getByRole( 'document', { - name: /Empty block/i, + name: /Block: Paragraph/i, } ); const samples = 10;