diff --git a/.wp-env.json b/.wp-env.json index ac8ae3d834924..86e72e0964232 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -5,6 +5,7 @@ "env": { "tests": { "mappings": { + "wp-content/plugins/gutenberg": ".", "wp-content/mu-plugins": "./packages/e2e-tests/mu-plugins", "wp-content/plugins/gutenberg-test-plugins": "./packages/e2e-tests/plugins", "wp-content/themes/gutenberg-test-themes": "./packages/e2e-tests/themes" diff --git a/docs/reference-guides/data/data-core-edit-post.md b/docs/reference-guides/data/data-core-edit-post.md index f11b3d65ab387..97c063fcdceb5 100644 --- a/docs/reference-guides/data/data-core-edit-post.md +++ b/docs/reference-guides/data/data-core-edit-post.md @@ -551,8 +551,4 @@ _Parameters_ - _blockName_ `string`: Name of the block. - _blockStyle_ `?string`: Name of the style that should be auto applied. If undefined, the "auto apply" setting of the block is removed. -_Returns_ - -- `Object`: Action object. - diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index 4d0b64340cd9e..f54e541122e15 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -148,7 +148,22 @@ _Returns_ ### getEntitiesByKind -Returns whether the entities for the give kind are loaded. +> **Deprecated** since WordPress 6.0. Use getEntitiesConfig instead + +Returns the loaded entities for the given kind. + +_Parameters_ + +- _state_ `Object`: Data state. +- _kind_ `string`: Entity kind. + +_Returns_ + +- `Array`: Array of entities with config matching kind. + +### getEntitiesConfig + +Returns the loaded entities for the given kind. _Parameters_ @@ -161,7 +176,9 @@ _Returns_ ### getEntity -Returns the entity object given its kind and name. +> **Deprecated** since WordPress 6.0. Use getEntityConfig instead + +Returns the entity config given its kind and name. _Parameters_ @@ -171,7 +188,21 @@ _Parameters_ _Returns_ -- `Object`: Entity +- `Object`: Entity config + +### getEntityConfig + +Returns the entity config given its kind and name. + +_Parameters_ + +- _state_ `Object`: Data state. +- _kind_ `string`: Entity kind. +- _name_ `string`: Entity name. + +_Returns_ + +- `Object`: Entity config ### getEntityRecord @@ -530,9 +561,9 @@ Action triggered to delete an entity record. _Parameters_ -- _kind_ `string`: Kind of the deleted entity. -- _name_ `string`: Name of the deleted entity. -- _recordId_ `string`: Record ID of the deleted entity. +- _kind_ `string`: Kind of the deleted entity record. +- _name_ `string`: Name of the deleted entity record. +- _recordId_ `string`: Record ID of the deleted entity record. - _query_ `?Object`: Special query parameters for the DELETE API call. - _options_ `[Object]`: Delete options. - _options.\_\_unstableFetch_ `[Function]`: Internal use only. Function to call instead of `apiFetch()`. Must return a promise. @@ -613,8 +644,8 @@ Returns an action object used in signalling that entity records have been receiv _Parameters_ -- _kind_ `string`: Kind of the received entity. -- _name_ `string`: Name of the received entity. +- _kind_ `string`: Kind of the received entity record. +- _name_ `string`: Name of the received entity record. - _records_ `Array|Object`: Records received. - _query_ `?Object`: Query Object. - _invalidateCache_ `?boolean`: Should invalidate query caches. diff --git a/package.json b/package.json index 01cc87dae7598..f4b537366996a 100755 --- a/package.json +++ b/package.json @@ -263,7 +263,6 @@ "lint:md-js": "wp-scripts lint-md-js", "lint:md-docs": "wp-scripts lint-md-docs", "native": "npm run --prefix packages/react-native-editor", - "pot-to-php": "./bin/pot-to-php.js", "postinstall": "patch-package && node ./patches/patch-xcode.js", "prepublishOnly": "npm run clean:package-types && npm run build:packages", "publish:check": "lerna updated", diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 68142bdc05c4a..43cab0244b940 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -18,6 +18,7 @@ import { ToolbarButton, Tooltip, ToolbarGroup, + KeyboardShortcuts, } from '@wordpress/components'; import { displayShortcut, isKeyboardEvent, ENTER } from '@wordpress/keycodes'; import { __, sprintf } from '@wordpress/i18n'; @@ -265,6 +266,64 @@ export const updateNavigationLinkBlockAttributes = ( } ); }; +const useIsInvalidLink = ( kind, type, id ) => { + const isPostType = + kind === 'post-type' || type === 'post' || type === 'page'; + const hasId = Number.isInteger( id ); + const postStatus = useSelect( + ( select ) => { + if ( ! isPostType ) { + return null; + } + const { getEntityRecord } = select( coreStore ); + return getEntityRecord( 'postType', type, id )?.status; + }, + [ isPostType, type, id ] + ); + + // Check Navigation Link validity if: + // 1. Link is 'post-type'. + // 2. It has an id. + // 3. It's neither null, nor undefined, as valid items might be either of those while loading. + // If those conditions are met, check if + // 1. The post status is published. + // 2. The Navigation Link item has no label. + // If either of those is true, invalidate. + const isInvalid = + isPostType && hasId && postStatus && 'trash' === postStatus; + const isDraft = 'draft' === postStatus; + + return [ isInvalid, isDraft ]; +}; + +const useMissingText = ( type ) => { + let missingText = ''; + + switch ( type ) { + case 'post': + /* translators: label for missing post in navigation link block */ + missingText = __( 'Select post' ); + break; + case 'page': + /* translators: label for missing page in navigation link block */ + missingText = __( 'Select page' ); + break; + case 'category': + /* translators: label for missing category in navigation link block */ + missingText = __( 'Select category' ); + break; + case 'tag': + /* translators: label for missing tag in navigation link block */ + missingText = __( 'Select tag' ); + break; + default: + /* translators: label for missing values in navigation link block */ + missingText = __( 'Add link' ); + } + + return missingText; +}; + /** * Removes HTML from a given string. * Note the does not provide XSS protection or otherwise attempt @@ -329,6 +388,7 @@ export default function NavigationLinkEdit( { clientId, } ) { const { + id, label, type, opensInNewTab, @@ -339,6 +399,8 @@ export default function NavigationLinkEdit( { kind, } = attributes; + const [ isInvalid, isDraft ] = useIsInvalidLink( kind, type, id ); + const link = { url, opensInNewTab, @@ -589,36 +651,23 @@ export default function NavigationLinkEdit( { onKeyDown, } ); - if ( ! url ) { + if ( ! url || isInvalid || isDraft ) { blockProps.onClick = () => setIsLinkOpen( true ); } const classes = classnames( 'wp-block-navigation-item__content', { - 'wp-block-navigation-link__placeholder': ! url, + 'wp-block-navigation-link__placeholder': ! url || isInvalid || isDraft, } ); - let missingText = ''; - switch ( type ) { - case 'post': - /* translators: label for missing post in navigation link block */ - missingText = __( 'Select post' ); - break; - case 'page': - /* translators: label for missing page in navigation link block */ - missingText = __( 'Select page' ); - break; - case 'category': - /* translators: label for missing category in navigation link block */ - missingText = __( 'Select category' ); - break; - case 'tag': - /* translators: label for missing tag in navigation link block */ - missingText = __( 'Select tag' ); - break; - default: - /* translators: label for missing values in navigation link block */ - missingText = __( 'Add link' ); - } + const missingText = useMissingText( type, isInvalid, isDraft ); + /* translators: Whether the navigation link is Invalid or a Draft. */ + const placeholderText = `(${ + isInvalid ? __( 'Invalid' ) : __( 'Draft' ) + })`; + const tooltipText = + isInvalid || isDraft + ? __( 'This item has been deleted, or is a draft' ) + : __( 'This item is missing a link' ); return ( @@ -677,46 +726,81 @@ export default function NavigationLinkEdit( { { /* eslint-enable */ } { ! url ? (
- - { missingText } + + <> + { missingText } + + { tooltipText } + +
) : ( - - setAttributes( { - label: labelValue, - } ) - } - onMerge={ mergeBlocks } - onReplace={ onReplace } - __unstableOnSplitAtEnd={ () => - insertBlocksAfter( - createBlock( 'core/navigation-link' ) - ) - } - aria-label={ __( 'Navigation link text' ) } - placeholder={ itemLabelPlaceholder } - withoutInteractiveFormatting - allowedFormats={ [ - 'core/bold', - 'core/italic', - 'core/image', - 'core/strikethrough', - ] } - onClick={ () => { - if ( ! url ) { - setIsLinkOpen( true ); - } - } } - /> + <> + { ! isInvalid && ! isDraft && ( + + setAttributes( { + label: labelValue, + } ) + } + onMerge={ mergeBlocks } + onReplace={ onReplace } + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( + createBlock( + 'core/navigation-link' + ) + ) + } + aria-label={ __( 'Navigation link text' ) } + placeholder={ itemLabelPlaceholder } + withoutInteractiveFormatting + allowedFormats={ [ + 'core/bold', + 'core/italic', + 'core/image', + 'core/strikethrough', + ] } + onClick={ () => { + if ( ! url ) { + setIsLinkOpen( true ); + } + } } + /> + ) } + { ( isInvalid || isDraft ) && ( +
+ + isSelected && + setIsLinkOpen( true ), + } } + /> + + <> + + { + /* Trim to avoid trailing white space when the placeholder text is not present */ + `${ label } ${ placeholderText }`.trim() + } + + + { tooltipText } + + + +
+ ) } + ) } { isLinkOpen && ( post_title; + + if ( isset( $seen_menu_names[ $nav_menu_name ] ) ) { + ++$seen_menu_names[ $nav_menu_name ]; + } else { + $seen_menu_names[ $nav_menu_name ] = 1; + } + $parsed_blocks = parse_blocks( $navigation_post->post_content ); // 'parse_blocks' includes a null block with '\n\n' as the content when @@ -508,10 +520,18 @@ function render_block_core_navigation( $attributes, $content, $block ) { $block_styles = isset( $attributes['styles'] ) ? $attributes['styles'] : ''; + // If the menu name has been used previously then append an ID + // to the name to ensure uniqueness across a given post. + if ( isset( $seen_menu_names[ $nav_menu_name ] ) && $seen_menu_names[ $nav_menu_name ] > 1 ) { + $count = $seen_menu_names[ $nav_menu_name ]; + $nav_menu_name = $nav_menu_name . ' ' . ( $count ); + } + $wrapper_attributes = get_block_wrapper_attributes( array( - 'class' => implode( ' ', $classes ), - 'style' => $block_styles . $colors['inline_styles'] . $font_sizes['inline_styles'], + 'class' => implode( ' ', $classes ), + 'style' => $block_styles . $colors['inline_styles'] . $font_sizes['inline_styles'], + 'aria-label' => $nav_menu_name, ) ); diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 2c99798342cfe..7135aa05aaf25 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -10,9 +10,9 @@ - `InputControl`: Allow `onBlur` for empty values to commit the change when `isPressEnterToChange` is true, and move reset behavior to the ESCAPE key. ([#39109](https://github.com/WordPress/gutenberg/pull/39109)). - `TreeGrid`: Add tests for Home/End keyboard navigation. Add `onFocusRow` callback for Home/End keyboard navigation, this was missed in the implementation PR. Modify test for expanding/collapsing a row as row 1 implements this now. Update README with latest changes. ([#39302](https://github.com/WordPress/gutenberg/pull/39302)) - `ToggleGroupControlOption`: Calculate width from button content and remove `LabelPlaceholderView` ([#39345](https://github.com/WordPress/gutenberg/pull/39345)) +- `BaseControl`: Add `__nextHasNoMarginBottom` prop for opting into the new margin-free styles ([#39325](https://github.com/WordPress/gutenberg/pull/39325)). - `Divider`: Make the divider visible by default (`display: inline`) in flow layout containers when the divider orientation is vertical ([#39316](https://github.com/WordPress/gutenberg/pull/39316)). - ### Bug Fix - Normalize `font-family` on `Button`, `ColorPalette`, `ComoboboxControl`, `DateTimePicker`, `FormTokenField`, `InputControl`, `SelectControl`, and `ToggleGroupControl` ([#38969](https://github.com/WordPress/gutenberg/pull/38969)). diff --git a/packages/components/src/base-control/README.md b/packages/components/src/base-control/README.md index 6ed5b75b76b33..6e507ee4df87a 100644 --- a/packages/components/src/base-control/README.md +++ b/packages/components/src/base-control/README.md @@ -10,7 +10,7 @@ Render a BaseControl for a textarea input: import { BaseControl } from '@wordpress/components'; const MyBaseControl = () => ( - +