@wordpress/block-library 9.40.2-next.v.202602241322.0 → 9.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/build/accordion/view.cjs +0 -34
- package/build/accordion/view.cjs.map +2 -2
- package/build/button/block.json +11 -3
- package/build/button/deprecated.cjs +246 -13
- package/build/button/deprecated.cjs.map +2 -2
- package/build/button/edit.cjs +45 -58
- package/build/button/edit.cjs.map +3 -3
- package/build/button/save.cjs +3 -7
- package/build/button/save.cjs.map +2 -2
- package/build/button/utils.cjs +59 -0
- package/build/button/utils.cjs.map +7 -0
- package/build/image/image.cjs +1 -1
- package/build/image/image.cjs.map +2 -2
- package/build/navigation/edit/index.cjs +4 -2
- package/build/navigation/edit/index.cjs.map +3 -3
- package/build/navigation/edit/leaf-more-menu.cjs +68 -6
- package/build/navigation/edit/leaf-more-menu.cjs.map +3 -3
- package/build/navigation/edit/menu-inspector-controls.cjs +20 -91
- package/build/navigation/edit/menu-inspector-controls.cjs.map +3 -3
- package/build/navigation/edit/navigation-link-ui.cjs +97 -0
- package/build/navigation/edit/navigation-link-ui.cjs.map +7 -0
- package/build/navigation/edit/navigation-list-view-header.cjs +86 -0
- package/build/navigation/edit/navigation-list-view-header.cjs.map +7 -0
- package/build/navigation/edit/navigation-menu-selector.cjs +4 -2
- package/build/navigation/edit/navigation-menu-selector.cjs.map +3 -3
- package/build/navigation/edit/placeholder/index.cjs +2 -2
- package/build/navigation/edit/placeholder/index.cjs.map +3 -3
- package/build/navigation-link/shared/controls.cjs +29 -52
- package/build/navigation-link/shared/controls.cjs.map +3 -3
- package/build/navigation-link/shared/use-link-preview.cjs +8 -9
- package/build/navigation-link/shared/use-link-preview.cjs.map +2 -2
- package/build/page-list-item/edit.cjs +6 -3
- package/build/page-list-item/edit.cjs.map +2 -2
- package/build/playlist/edit.cjs +43 -136
- package/build/playlist/edit.cjs.map +3 -3
- package/build/playlist/view.cjs +56 -38
- package/build/playlist/view.cjs.map +2 -2
- package/build/playlist-track/edit.cjs +0 -1
- package/build/playlist-track/edit.cjs.map +2 -2
- package/build/post-title/block.json +3 -0
- package/build/post-title/edit.cjs +2 -2
- package/build/post-title/edit.cjs.map +2 -2
- package/build/utils/waveform-player.cjs +68 -0
- package/build/utils/waveform-player.cjs.map +7 -0
- package/build/utils/waveform-utils.cjs +171 -0
- package/build/utils/waveform-utils.cjs.map +7 -0
- package/build-module/accordion/view.mjs +1 -35
- package/build-module/accordion/view.mjs.map +2 -2
- package/build-module/button/block.json +11 -3
- package/build-module/button/deprecated.mjs +246 -13
- package/build-module/button/deprecated.mjs.map +2 -2
- package/build-module/button/edit.mjs +47 -63
- package/build-module/button/edit.mjs.map +2 -2
- package/build-module/button/save.mjs +3 -7
- package/build-module/button/save.mjs.map +2 -2
- package/build-module/button/utils.mjs +33 -0
- package/build-module/button/utils.mjs.map +7 -0
- package/build-module/image/image.mjs +1 -1
- package/build-module/image/image.mjs.map +2 -2
- package/build-module/navigation/edit/index.mjs +4 -2
- package/build-module/navigation/edit/index.mjs.map +2 -2
- package/build-module/navigation/edit/leaf-more-menu.mjs +73 -7
- package/build-module/navigation/edit/leaf-more-menu.mjs.map +2 -2
- package/build-module/navigation/edit/menu-inspector-controls.mjs +21 -101
- package/build-module/navigation/edit/menu-inspector-controls.mjs.map +2 -2
- package/build-module/navigation/edit/navigation-link-ui.mjs +76 -0
- package/build-module/navigation/edit/navigation-link-ui.mjs.map +7 -0
- package/build-module/navigation/edit/navigation-list-view-header.mjs +58 -0
- package/build-module/navigation/edit/navigation-list-view-header.mjs.map +7 -0
- package/build-module/navigation/edit/navigation-menu-selector.mjs +5 -3
- package/build-module/navigation/edit/navigation-menu-selector.mjs.map +2 -2
- package/build-module/navigation/edit/placeholder/index.mjs +2 -2
- package/build-module/navigation/edit/placeholder/index.mjs.map +2 -2
- package/build-module/navigation-link/shared/controls.mjs +29 -53
- package/build-module/navigation-link/shared/controls.mjs.map +2 -2
- package/build-module/navigation-link/shared/use-link-preview.mjs +8 -9
- package/build-module/navigation-link/shared/use-link-preview.mjs.map +2 -2
- package/build-module/page-list-item/edit.mjs +6 -3
- package/build-module/page-list-item/edit.mjs.map +2 -2
- package/build-module/playlist/edit.mjs +41 -139
- package/build-module/playlist/edit.mjs.map +2 -2
- package/build-module/playlist/view.mjs +56 -38
- package/build-module/playlist/view.mjs.map +2 -2
- package/build-module/playlist-track/edit.mjs +0 -1
- package/build-module/playlist-track/edit.mjs.map +2 -2
- package/build-module/post-title/block.json +3 -0
- package/build-module/post-title/edit.mjs +2 -2
- package/build-module/post-title/edit.mjs.map +2 -2
- package/build-module/utils/waveform-player.mjs +43 -0
- package/build-module/utils/waveform-player.mjs.map +7 -0
- package/build-module/utils/waveform-utils.mjs +131 -0
- package/build-module/utils/waveform-utils.mjs.map +7 -0
- package/build-style/button/style-rtl.css +6 -0
- package/build-style/button/style.css +6 -0
- package/build-style/editor-rtl.css +13 -3
- package/build-style/editor.css +13 -3
- package/build-style/navigation-link/editor-rtl.css +10 -0
- package/build-style/navigation-link/editor.css +10 -0
- package/build-style/playlist/editor-rtl.css +3 -3
- package/build-style/playlist/editor.css +3 -3
- package/build-style/playlist/style-rtl.css +351 -17
- package/build-style/playlist/style.css +351 -17
- package/build-style/style-rtl.css +357 -17
- package/build-style/style.css +357 -17
- package/package.json +39 -38
- package/src/accordion/view.js +1 -44
- package/src/accordion-item/index.php +0 -1
- package/src/button/block.json +11 -3
- package/src/button/deprecated.js +254 -16
- package/src/button/edit.js +50 -61
- package/src/button/index.php +68 -0
- package/src/button/save.js +2 -8
- package/src/button/style.scss +49 -7
- package/src/button/test/utils.js +84 -0
- package/src/button/utils.js +42 -0
- package/src/cover/index.php +4 -4
- package/src/image/image.js +14 -15
- package/src/image/index.php +3 -1
- package/src/navigation/edit/index.js +4 -2
- package/src/navigation/edit/leaf-more-menu.js +86 -11
- package/src/navigation/edit/menu-inspector-controls.js +23 -142
- package/src/navigation/edit/navigation-link-ui.js +115 -0
- package/src/navigation/edit/navigation-list-view-header.js +62 -0
- package/src/navigation/edit/navigation-menu-selector.js +5 -3
- package/src/navigation/edit/placeholder/index.js +3 -2
- package/src/navigation/edit/test/navigation-menu-selector.js +23 -20
- package/src/navigation-link/editor.scss +18 -0
- package/src/navigation-link/shared/controls.js +35 -62
- package/src/navigation-link/shared/test/controls.js +5 -5
- package/src/navigation-link/shared/test/use-link-preview.test.js +19 -1
- package/src/navigation-link/shared/use-link-preview.js +14 -15
- package/src/page-list/index.php +1 -1
- package/src/page-list-item/edit.js +8 -7
- package/src/playlist/edit.js +60 -154
- package/src/playlist/editor.scss +3 -3
- package/src/playlist/index.php +15 -40
- package/src/playlist/style.scss +34 -27
- package/src/playlist/test/edit.js +137 -0
- package/src/playlist/view.js +97 -40
- package/src/playlist-track/edit.js +0 -1
- package/src/post-title/block.json +3 -0
- package/src/post-title/edit.js +4 -2
- package/src/query-title/index.php +1 -1
- package/src/search/index.php +1 -1
- package/src/utils/test/waveform-utils.js +328 -0
- package/src/utils/waveform-player.js +77 -0
- package/src/utils/waveform-utils.js +232 -0
- package/build/navigation/use-navigation-entities.cjs +0 -67
- package/build/navigation/use-navigation-entities.cjs.map +0 -7
- package/build-module/navigation/use-navigation-entities.mjs +0 -46
- package/build-module/navigation/use-navigation-entities.mjs.map +0 -7
- package/src/navigation/use-navigation-entities.js +0 -72
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
5
|
+
import { useDispatch } from '@wordpress/data';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
LinkUI,
|
|
12
|
+
updateAttributes,
|
|
13
|
+
useEntityBinding,
|
|
14
|
+
} from '../../navigation-link/shared';
|
|
15
|
+
|
|
16
|
+
const BLOCKS_WITH_LINK_UI_SUPPORT = [
|
|
17
|
+
'core/navigation-link',
|
|
18
|
+
'core/navigation-submenu',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function NavigationLinkUI( { block, insertedBlock, setInsertedBlock } ) {
|
|
22
|
+
const { updateBlockAttributes, removeBlock } =
|
|
23
|
+
useDispatch( blockEditorStore );
|
|
24
|
+
|
|
25
|
+
const supportsLinkControls = BLOCKS_WITH_LINK_UI_SUPPORT?.includes(
|
|
26
|
+
insertedBlock?.name
|
|
27
|
+
);
|
|
28
|
+
const blockWasJustInserted = insertedBlock?.clientId === block.clientId;
|
|
29
|
+
const showLinkControls = supportsLinkControls && blockWasJustInserted;
|
|
30
|
+
|
|
31
|
+
// Get binding utilities for the inserted block
|
|
32
|
+
const { createBinding, clearBinding } = useEntityBinding( {
|
|
33
|
+
clientId: insertedBlock?.clientId,
|
|
34
|
+
attributes: insertedBlock?.attributes || {},
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
if ( ! showLinkControls ) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cleanup function for auto-inserted Navigation Link blocks.
|
|
43
|
+
*
|
|
44
|
+
* Removes the block if it has no URL and clears the inserted block state.
|
|
45
|
+
* This ensures consistent cleanup behavior across different contexts.
|
|
46
|
+
*/
|
|
47
|
+
const cleanupInsertedBlock = () => {
|
|
48
|
+
// Prevent automatic block selection when removing blocks in list view context
|
|
49
|
+
// This avoids focus stealing that would close the list view and switch to canvas
|
|
50
|
+
const shouldAutoSelectBlock = false;
|
|
51
|
+
|
|
52
|
+
// Follows the exact same pattern as Navigation Link block's onClose handler
|
|
53
|
+
// If there is no URL then remove the auto-inserted block to avoid empty blocks
|
|
54
|
+
if ( ! insertedBlock?.attributes?.url && insertedBlock?.clientId ) {
|
|
55
|
+
// Remove the block entirely to avoid poor UX
|
|
56
|
+
// This matches the Navigation Link block's behavior
|
|
57
|
+
removeBlock( insertedBlock.clientId, shouldAutoSelectBlock );
|
|
58
|
+
}
|
|
59
|
+
setInsertedBlock( null );
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const setInsertedBlockAttributes =
|
|
63
|
+
( _insertedBlockClientId ) => ( _updatedAttributes ) => {
|
|
64
|
+
if ( ! _insertedBlockClientId ) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
updateBlockAttributes( _insertedBlockClientId, _updatedAttributes );
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Wrapper function to clean up original block when a new block is selected
|
|
71
|
+
const handleSetInsertedBlock = ( newBlock ) => {
|
|
72
|
+
// Prevent automatic block selection when removing blocks in list view context
|
|
73
|
+
// This avoids focus stealing that would close the list view and switch to canvas
|
|
74
|
+
const shouldAutoSelectBlock = false;
|
|
75
|
+
|
|
76
|
+
// If we have an existing inserted block and a new block is being set,
|
|
77
|
+
// remove the original block to avoid duplicates
|
|
78
|
+
if ( insertedBlock?.clientId && newBlock ) {
|
|
79
|
+
removeBlock( insertedBlock.clientId, shouldAutoSelectBlock );
|
|
80
|
+
}
|
|
81
|
+
setInsertedBlock( newBlock );
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<LinkUI
|
|
86
|
+
clientId={ insertedBlock?.clientId }
|
|
87
|
+
link={ insertedBlock?.attributes }
|
|
88
|
+
onBlockInsert={ handleSetInsertedBlock }
|
|
89
|
+
onClose={ () => {
|
|
90
|
+
// Use cleanup function
|
|
91
|
+
cleanupInsertedBlock();
|
|
92
|
+
} }
|
|
93
|
+
onChange={ ( updatedValue ) => {
|
|
94
|
+
// updateAttributes determines the final state and returns metadata
|
|
95
|
+
const { isEntityLink, attributes: updatedAttributes } =
|
|
96
|
+
updateAttributes(
|
|
97
|
+
updatedValue,
|
|
98
|
+
setInsertedBlockAttributes( insertedBlock?.clientId ),
|
|
99
|
+
insertedBlock?.attributes
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Handle URL binding based on the final computed state
|
|
103
|
+
// Only create bindings for entity links (posts, pages, taxonomies)
|
|
104
|
+
// Never create bindings for custom links (manual URLs)
|
|
105
|
+
if ( isEntityLink ) {
|
|
106
|
+
createBinding( updatedAttributes );
|
|
107
|
+
} else {
|
|
108
|
+
clearBinding();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setInsertedBlock( null );
|
|
112
|
+
} }
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
__experimentalHStack as HStack,
|
|
6
|
+
__experimentalHeading as Heading,
|
|
7
|
+
} from '@wordpress/components';
|
|
8
|
+
import { __ } from '@wordpress/i18n';
|
|
9
|
+
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import NavigationMenuSelector from './navigation-menu-selector';
|
|
15
|
+
import { unlock } from '../../lock-unlock';
|
|
16
|
+
|
|
17
|
+
const { useBlockDisplayTitle } = unlock( blockEditorPrivateApis );
|
|
18
|
+
|
|
19
|
+
const actionLabel =
|
|
20
|
+
/* translators: %s: The name of a menu. */ __( "Switch to '%s'" );
|
|
21
|
+
|
|
22
|
+
export default function NavigationListViewHeader( {
|
|
23
|
+
clientId,
|
|
24
|
+
blockEditingMode,
|
|
25
|
+
currentMenuId,
|
|
26
|
+
onSelectClassicMenu,
|
|
27
|
+
onSelectNavigationMenu,
|
|
28
|
+
onCreateNew,
|
|
29
|
+
createNavigationMenuIsSuccess,
|
|
30
|
+
createNavigationMenuIsError,
|
|
31
|
+
isManageMenusButtonDisabled,
|
|
32
|
+
} ) {
|
|
33
|
+
const blockTitle = useBlockDisplayTitle( {
|
|
34
|
+
clientId,
|
|
35
|
+
context: 'list-view',
|
|
36
|
+
} );
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<HStack className="wp-block-navigation-off-canvas-editor__header">
|
|
40
|
+
<Heading
|
|
41
|
+
className="wp-block-navigation-off-canvas-editor__title"
|
|
42
|
+
level={ 2 }
|
|
43
|
+
>
|
|
44
|
+
{ blockTitle }
|
|
45
|
+
</Heading>
|
|
46
|
+
{ blockEditingMode === 'default' && (
|
|
47
|
+
<NavigationMenuSelector
|
|
48
|
+
currentMenuId={ currentMenuId }
|
|
49
|
+
onSelectClassicMenu={ onSelectClassicMenu }
|
|
50
|
+
onSelectNavigationMenu={ onSelectNavigationMenu }
|
|
51
|
+
onCreateNew={ onCreateNew }
|
|
52
|
+
createNavigationMenuIsSuccess={
|
|
53
|
+
createNavigationMenuIsSuccess
|
|
54
|
+
}
|
|
55
|
+
createNavigationMenuIsError={ createNavigationMenuIsError }
|
|
56
|
+
actionLabel={ actionLabel }
|
|
57
|
+
isManageMenusButtonDisabled={ isManageMenusButtonDisabled }
|
|
58
|
+
/>
|
|
59
|
+
) }
|
|
60
|
+
</HStack>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -11,13 +11,12 @@ import { moreVertical } from '@wordpress/icons';
|
|
|
11
11
|
import { __, sprintf } from '@wordpress/i18n';
|
|
12
12
|
import { decodeEntities } from '@wordpress/html-entities';
|
|
13
13
|
import { useEffect, useMemo, useState } from '@wordpress/element';
|
|
14
|
-
import { useEntityProp } from '@wordpress/core-data';
|
|
14
|
+
import { useEntityRecords, useEntityProp } from '@wordpress/core-data';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Internal dependencies
|
|
18
18
|
*/
|
|
19
19
|
import useNavigationMenu from '../use-navigation-menu';
|
|
20
|
-
import useNavigationEntities from '../use-navigation-entities';
|
|
21
20
|
|
|
22
21
|
function buildMenuLabel( title, id, status ) {
|
|
23
22
|
if ( ! title ) {
|
|
@@ -53,7 +52,10 @@ function NavigationMenuSelector( {
|
|
|
53
52
|
|
|
54
53
|
actionLabel = actionLabel || createActionLabel;
|
|
55
54
|
|
|
56
|
-
const {
|
|
55
|
+
const { records: classicMenus } = useEntityRecords( 'root', 'menu', {
|
|
56
|
+
per_page: -1,
|
|
57
|
+
context: 'view',
|
|
58
|
+
} );
|
|
57
59
|
|
|
58
60
|
const {
|
|
59
61
|
navigationMenus,
|
|
@@ -6,11 +6,11 @@ import { __ } from '@wordpress/i18n';
|
|
|
6
6
|
import { navigation, Icon } from '@wordpress/icons';
|
|
7
7
|
import { speak } from '@wordpress/a11y';
|
|
8
8
|
import { useEffect } from '@wordpress/element';
|
|
9
|
+
import { useEntityRecords } from '@wordpress/core-data';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Internal dependencies
|
|
12
13
|
*/
|
|
13
|
-
import useNavigationEntities from '../../use-navigation-entities';
|
|
14
14
|
import PlaceholderPreview from './placeholder-preview';
|
|
15
15
|
import NavigationMenuSelector from '../navigation-menu-selector';
|
|
16
16
|
|
|
@@ -24,7 +24,8 @@ export default function NavigationPlaceholder( {
|
|
|
24
24
|
onSelectClassicMenu,
|
|
25
25
|
onCreateEmpty,
|
|
26
26
|
} ) {
|
|
27
|
-
const { isResolvingMenus, hasResolvedMenus } =
|
|
27
|
+
const { isResolving: isResolvingMenus, hasResolved: hasResolvedMenus } =
|
|
28
|
+
useEntityRecords( 'root', 'menu', { per_page: -1, context: 'view' } );
|
|
28
29
|
|
|
29
30
|
useEffect( () => {
|
|
30
31
|
if ( ! isSelected ) {
|
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
import { render, screen } from '@testing-library/react';
|
|
5
5
|
import userEvent from '@testing-library/user-event';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* WordPress dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { useEntityRecords } from '@wordpress/core-data';
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* Internal dependencies
|
|
9
14
|
*/
|
|
10
15
|
import NavigationMenuSelector from '../navigation-menu-selector';
|
|
11
16
|
import useNavigationMenu from '../../use-navigation-menu';
|
|
12
|
-
import useNavigationEntities from '../../use-navigation-entities';
|
|
13
17
|
|
|
14
18
|
jest.mock( '../../use-navigation-menu', () => {
|
|
15
19
|
// This allows us to tweak the returned value on each test.
|
|
@@ -17,14 +21,13 @@ jest.mock( '../../use-navigation-menu', () => {
|
|
|
17
21
|
return mock;
|
|
18
22
|
} );
|
|
19
23
|
|
|
20
|
-
jest.mock( '
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} );
|
|
24
|
+
jest.mock( '@wordpress/core-data', () => ( {
|
|
25
|
+
...jest.requireActual( '@wordpress/core-data' ),
|
|
26
|
+
useEntityRecords: jest.fn(),
|
|
27
|
+
} ) );
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
useEntityRecords.mockReturnValue( {
|
|
30
|
+
records: [],
|
|
28
31
|
} );
|
|
29
32
|
|
|
30
33
|
const navigationMenu1 = {
|
|
@@ -485,8 +488,8 @@ describe( 'NavigationMenuSelector', () => {
|
|
|
485
488
|
it( 'should not show classic menus if there are no classic menus', async () => {
|
|
486
489
|
const user = userEvent.setup();
|
|
487
490
|
|
|
488
|
-
|
|
489
|
-
|
|
491
|
+
useEntityRecords.mockReturnValue( {
|
|
492
|
+
records: [],
|
|
490
493
|
} );
|
|
491
494
|
|
|
492
495
|
render( <NavigationMenuSelector /> );
|
|
@@ -507,8 +510,8 @@ describe( 'NavigationMenuSelector', () => {
|
|
|
507
510
|
canUserCreateNavigationMenus: false,
|
|
508
511
|
} );
|
|
509
512
|
|
|
510
|
-
|
|
511
|
-
|
|
513
|
+
useEntityRecords.mockReturnValue( {
|
|
514
|
+
records: classicMenusFixture,
|
|
512
515
|
} );
|
|
513
516
|
|
|
514
517
|
render( <NavigationMenuSelector /> );
|
|
@@ -529,8 +532,8 @@ describe( 'NavigationMenuSelector', () => {
|
|
|
529
532
|
canUserCreateNavigationMenus: true,
|
|
530
533
|
} );
|
|
531
534
|
|
|
532
|
-
|
|
533
|
-
|
|
535
|
+
useEntityRecords.mockReturnValue( {
|
|
536
|
+
records: classicMenusFixture,
|
|
534
537
|
} );
|
|
535
538
|
|
|
536
539
|
render( <NavigationMenuSelector /> );
|
|
@@ -566,8 +569,8 @@ describe( 'NavigationMenuSelector', () => {
|
|
|
566
569
|
canUserCreateNavigationMenus: true,
|
|
567
570
|
} );
|
|
568
571
|
|
|
569
|
-
|
|
570
|
-
|
|
572
|
+
useEntityRecords.mockReturnValue( {
|
|
573
|
+
records: classicMenusFixture,
|
|
571
574
|
} );
|
|
572
575
|
|
|
573
576
|
const { rerender } = render(
|
|
@@ -598,8 +601,8 @@ describe( 'NavigationMenuSelector', () => {
|
|
|
598
601
|
canUserCreateNavigationMenus: true,
|
|
599
602
|
} );
|
|
600
603
|
|
|
601
|
-
|
|
602
|
-
|
|
604
|
+
useEntityRecords.mockReturnValue( {
|
|
605
|
+
records: classicMenusFixture,
|
|
603
606
|
} );
|
|
604
607
|
|
|
605
608
|
rerender(
|
|
@@ -631,8 +634,8 @@ describe( 'NavigationMenuSelector', () => {
|
|
|
631
634
|
canUserCreateNavigationMenus: true,
|
|
632
635
|
} );
|
|
633
636
|
|
|
634
|
-
|
|
635
|
-
|
|
637
|
+
useEntityRecords.mockReturnValue( {
|
|
638
|
+
records: classicMenusFixture,
|
|
636
639
|
} );
|
|
637
640
|
|
|
638
641
|
// Simulate the menu being created and component being re-rendered.
|
|
@@ -150,3 +150,21 @@
|
|
|
150
150
|
.navigation-link-control__error-text {
|
|
151
151
|
color: $alert-red;
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
.navigation-link-to__action-button {
|
|
155
|
+
grid-column: auto;
|
|
156
|
+
// When this is the last button AND in an odd position among its class
|
|
157
|
+
// siblings, it's unpaired — span the full grid row.
|
|
158
|
+
&:nth-last-child(1 of #{&}):nth-child(odd of #{&}) {
|
|
159
|
+
grid-column: 1 / -1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Artificially increase specificity to override `justify-content` styles.
|
|
163
|
+
// Not ideal, but it shouldn't be necessary once switching to the new `Button`
|
|
164
|
+
// component from `@wordpress/ui`.
|
|
165
|
+
&#{&}#{&} {
|
|
166
|
+
justify-content: center;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
Button,
|
|
6
6
|
__experimentalToolsPanel as ToolsPanel,
|
|
7
7
|
__experimentalToolsPanelItem as ToolsPanelItem,
|
|
8
|
-
__experimentalHStack as HStack,
|
|
9
8
|
CheckboxControl,
|
|
10
9
|
TextControl,
|
|
11
10
|
TextareaControl,
|
|
@@ -150,7 +149,7 @@ export function Controls( {
|
|
|
150
149
|
|
|
151
150
|
// Check if URL is viewable (not hash link or other relative path like ./ or ../)
|
|
152
151
|
const isViewableUrl =
|
|
153
|
-
url &&
|
|
152
|
+
!! url &&
|
|
154
153
|
( ! isHashLink( url ) ||
|
|
155
154
|
( isRelativePath( url ) && ! url.startsWith( '/' ) ) );
|
|
156
155
|
|
|
@@ -158,11 +157,6 @@ export function Controls( {
|
|
|
158
157
|
const viewUrl =
|
|
159
158
|
isViewableUrl && url.startsWith( '/' ) && homeUrl ? homeUrl + url : url;
|
|
160
159
|
|
|
161
|
-
const entityTypeName = getEntityTypeName(
|
|
162
|
-
attributes.type,
|
|
163
|
-
attributes.kind
|
|
164
|
-
);
|
|
165
|
-
|
|
166
160
|
return (
|
|
167
161
|
<ToolsPanel
|
|
168
162
|
label={ __( 'Settings' ) }
|
|
@@ -220,61 +214,6 @@ export function Controls( {
|
|
|
220
214
|
help={ helpText ? helpText : undefined }
|
|
221
215
|
/>
|
|
222
216
|
</ToolsPanelItem>
|
|
223
|
-
|
|
224
|
-
{ url && (
|
|
225
|
-
<HStack
|
|
226
|
-
className="navigation-link-to__actions"
|
|
227
|
-
alignment="left"
|
|
228
|
-
justify="left"
|
|
229
|
-
style={ { gridColumn: '1 / -1' } }
|
|
230
|
-
>
|
|
231
|
-
{ hasUrlBinding &&
|
|
232
|
-
isBoundEntityAvailable &&
|
|
233
|
-
entityRecord?.id &&
|
|
234
|
-
attributes.kind === 'post-type' &&
|
|
235
|
-
onNavigateToEntityRecord && (
|
|
236
|
-
<Button
|
|
237
|
-
size="compact"
|
|
238
|
-
variant="secondary"
|
|
239
|
-
onClick={ () => {
|
|
240
|
-
onNavigateToEntityRecord( {
|
|
241
|
-
postId: entityRecord.id,
|
|
242
|
-
postType: attributes.type,
|
|
243
|
-
} );
|
|
244
|
-
} }
|
|
245
|
-
__next40pxDefaultSize
|
|
246
|
-
>
|
|
247
|
-
{ sprintf(
|
|
248
|
-
/* translators: %s: entity type (e.g., "page", "post", "category") */
|
|
249
|
-
__( 'Edit %s' ),
|
|
250
|
-
entityTypeName
|
|
251
|
-
) }
|
|
252
|
-
</Button>
|
|
253
|
-
) }
|
|
254
|
-
{ isViewableUrl && (
|
|
255
|
-
<Button
|
|
256
|
-
size="compact"
|
|
257
|
-
variant="secondary"
|
|
258
|
-
href={ viewUrl }
|
|
259
|
-
target="_blank"
|
|
260
|
-
icon={ external }
|
|
261
|
-
iconPosition="right"
|
|
262
|
-
__next40pxDefaultSize
|
|
263
|
-
>
|
|
264
|
-
{ sprintf(
|
|
265
|
-
/* translators: %s: entity type (e.g., "page", "post", "category") or "link" for external links */
|
|
266
|
-
__( 'View %s' ),
|
|
267
|
-
attributes.kind &&
|
|
268
|
-
attributes.type &&
|
|
269
|
-
attributes.kind !== 'custom'
|
|
270
|
-
? entityTypeName
|
|
271
|
-
: __( 'link' )
|
|
272
|
-
) }
|
|
273
|
-
</Button>
|
|
274
|
-
) }
|
|
275
|
-
</HStack>
|
|
276
|
-
) }
|
|
277
|
-
|
|
278
217
|
<ToolsPanelItem
|
|
279
218
|
hasValue={ () => !! opensInNewTab }
|
|
280
219
|
label={ __( 'Open in new tab' ) }
|
|
@@ -291,6 +230,40 @@ export function Controls( {
|
|
|
291
230
|
}
|
|
292
231
|
/>
|
|
293
232
|
</ToolsPanelItem>
|
|
233
|
+
|
|
234
|
+
{ !! url &&
|
|
235
|
+
hasUrlBinding &&
|
|
236
|
+
isBoundEntityAvailable &&
|
|
237
|
+
entityRecord?.id &&
|
|
238
|
+
attributes.kind === 'post-type' &&
|
|
239
|
+
onNavigateToEntityRecord && (
|
|
240
|
+
<Button
|
|
241
|
+
variant="secondary"
|
|
242
|
+
onClick={ () => {
|
|
243
|
+
onNavigateToEntityRecord( {
|
|
244
|
+
postId: entityRecord.id,
|
|
245
|
+
postType: attributes.type,
|
|
246
|
+
} );
|
|
247
|
+
} }
|
|
248
|
+
__next40pxDefaultSize
|
|
249
|
+
className="navigation-link-to__action-button"
|
|
250
|
+
>
|
|
251
|
+
{ __( 'Edit' ) }
|
|
252
|
+
</Button>
|
|
253
|
+
) }
|
|
254
|
+
{ isViewableUrl && (
|
|
255
|
+
<Button
|
|
256
|
+
variant="secondary"
|
|
257
|
+
href={ viewUrl }
|
|
258
|
+
target="_blank"
|
|
259
|
+
icon={ external }
|
|
260
|
+
iconPosition="right"
|
|
261
|
+
__next40pxDefaultSize
|
|
262
|
+
className="navigation-link-to__action-button"
|
|
263
|
+
>
|
|
264
|
+
{ __( 'View' ) }
|
|
265
|
+
</Button>
|
|
266
|
+
) }
|
|
294
267
|
</>
|
|
295
268
|
) }
|
|
296
269
|
|
|
@@ -237,7 +237,7 @@ describe( 'Controls', () => {
|
|
|
237
237
|
|
|
238
238
|
render( <Controls { ...propsWithExternalLink } /> );
|
|
239
239
|
|
|
240
|
-
expect( screen.getByText( 'View
|
|
240
|
+
expect( screen.getByText( 'View' ) ).toBeVisible();
|
|
241
241
|
} );
|
|
242
242
|
|
|
243
243
|
it( 'shows "View page" for page links', () => {
|
|
@@ -253,7 +253,7 @@ describe( 'Controls', () => {
|
|
|
253
253
|
|
|
254
254
|
render( <Controls { ...propsWithPageLink } /> );
|
|
255
255
|
|
|
256
|
-
expect( screen.getByText( 'View
|
|
256
|
+
expect( screen.getByText( 'View' ) ).toBeVisible();
|
|
257
257
|
} );
|
|
258
258
|
|
|
259
259
|
it( 'shows "View post" for post links', () => {
|
|
@@ -269,7 +269,7 @@ describe( 'Controls', () => {
|
|
|
269
269
|
|
|
270
270
|
render( <Controls { ...propsWithPostLink } /> );
|
|
271
271
|
|
|
272
|
-
expect( screen.getByText( 'View
|
|
272
|
+
expect( screen.getByText( 'View' ) ).toBeVisible();
|
|
273
273
|
} );
|
|
274
274
|
|
|
275
275
|
it( 'shows "View category" for category links', () => {
|
|
@@ -285,7 +285,7 @@ describe( 'Controls', () => {
|
|
|
285
285
|
|
|
286
286
|
render( <Controls { ...propsWithCategoryLink } /> );
|
|
287
287
|
|
|
288
|
-
expect( screen.getByText( 'View
|
|
288
|
+
expect( screen.getByText( 'View' ) ).toBeVisible();
|
|
289
289
|
} );
|
|
290
290
|
|
|
291
291
|
it( 'shows "View link" for custom type links', () => {
|
|
@@ -301,7 +301,7 @@ describe( 'Controls', () => {
|
|
|
301
301
|
|
|
302
302
|
render( <Controls { ...propsWithCustomLink } /> );
|
|
303
303
|
|
|
304
|
-
expect( screen.getByText( 'View
|
|
304
|
+
expect( screen.getByText( 'View' ) ).toBeVisible();
|
|
305
305
|
} );
|
|
306
306
|
} );
|
|
307
307
|
} );
|
|
@@ -92,10 +92,28 @@ describe( 'computeDisplayUrl', () => {
|
|
|
92
92
|
it( 'should treat same-origin URLs as internal', () => {
|
|
93
93
|
const result = computeDisplayUrl( {
|
|
94
94
|
linkUrl: 'https://example.com/my-page',
|
|
95
|
-
|
|
95
|
+
homeUrl: 'https://example.com',
|
|
96
96
|
} );
|
|
97
97
|
expect( result.isExternal ).toBe( false );
|
|
98
98
|
} );
|
|
99
|
+
|
|
100
|
+
it( 'should treat same-origin URLs as internal when homeUrl includes a path', () => {
|
|
101
|
+
const result = computeDisplayUrl( {
|
|
102
|
+
linkUrl: 'https://example.com/my-page',
|
|
103
|
+
homeUrl: 'https://example.com/blog',
|
|
104
|
+
} );
|
|
105
|
+
expect( result.isExternal ).toBe( false );
|
|
106
|
+
expect( result.displayUrl ).toBe( '/my-page' );
|
|
107
|
+
} );
|
|
108
|
+
|
|
109
|
+
it( 'should treat http and https to same host as internal (compare by host, not origin)', () => {
|
|
110
|
+
const result = computeDisplayUrl( {
|
|
111
|
+
linkUrl: 'http://example.com/my-page',
|
|
112
|
+
homeUrl: 'https://example.com',
|
|
113
|
+
} );
|
|
114
|
+
expect( result.isExternal ).toBe( false );
|
|
115
|
+
expect( result.displayUrl ).toBe( '/my-page' );
|
|
116
|
+
} );
|
|
99
117
|
} );
|
|
100
118
|
|
|
101
119
|
describe( 'special protocols and edge cases', () => {
|
|
@@ -31,10 +31,10 @@ function capitalize( str ) {
|
|
|
31
31
|
*
|
|
32
32
|
* @param {Object} options - Parameters object
|
|
33
33
|
* @param {string} options.linkUrl - The URL to process
|
|
34
|
-
* @param {string} options.
|
|
34
|
+
* @param {string} options.homeUrl - The WordPress site URL (required for internal/external detection)
|
|
35
35
|
* @return {Object} Object with displayUrl and isExternal flag
|
|
36
36
|
*/
|
|
37
|
-
export function computeDisplayUrl( { linkUrl,
|
|
37
|
+
export function computeDisplayUrl( { linkUrl, homeUrl } = {} ) {
|
|
38
38
|
if ( ! linkUrl ) {
|
|
39
39
|
return { displayUrl: '', isExternal: false };
|
|
40
40
|
}
|
|
@@ -51,9 +51,10 @@ export function computeDisplayUrl( { linkUrl, siteUrl } = {} ) {
|
|
|
51
51
|
// This must happen before trusting the type attribute
|
|
52
52
|
try {
|
|
53
53
|
const parsedUrl = new URL( linkUrl );
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
|
|
54
|
+
// Compare by host (not origin) so http/https to same site both count as internal
|
|
55
|
+
const siteHost = new URL( homeUrl ).host;
|
|
56
|
+
|
|
57
|
+
if ( parsedUrl.host === siteHost ) {
|
|
57
58
|
// Show only the pathname (and search/hash if present)
|
|
58
59
|
let path = parsedUrl.pathname + parsedUrl.search + parsedUrl.hash;
|
|
59
60
|
// Remove trailing slash
|
|
@@ -62,12 +63,11 @@ export function computeDisplayUrl( { linkUrl, siteUrl } = {} ) {
|
|
|
62
63
|
}
|
|
63
64
|
displayUrl = path;
|
|
64
65
|
} else {
|
|
65
|
-
// Different
|
|
66
|
+
// Different host - this is an external link
|
|
66
67
|
isExternal = true;
|
|
67
68
|
}
|
|
68
69
|
} catch ( e ) {
|
|
69
|
-
// URL parsing failed -
|
|
70
|
-
// Since we already checked for relative paths and hash links above, treat as external
|
|
70
|
+
// URL parsing failed - treat as external (e.g. no homeUrl, or URL without protocol)
|
|
71
71
|
isExternal = true;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -174,13 +174,12 @@ export function useLinkPreview( {
|
|
|
174
174
|
hasBinding,
|
|
175
175
|
isEntityAvailable,
|
|
176
176
|
} ) {
|
|
177
|
-
// Get the WordPress
|
|
178
|
-
const
|
|
179
|
-
|
|
177
|
+
// Get the WordPress homepage URL from settings
|
|
178
|
+
const homeUrl = useSelect( ( select ) => {
|
|
179
|
+
return select( coreDataStore ).getEntityRecord(
|
|
180
180
|
'root',
|
|
181
|
-
'
|
|
182
|
-
);
|
|
183
|
-
return siteEntity?.url;
|
|
181
|
+
'__unstableBase'
|
|
182
|
+
)?.home;
|
|
184
183
|
}, [] );
|
|
185
184
|
|
|
186
185
|
const title =
|
|
@@ -194,7 +193,7 @@ export function useLinkPreview( {
|
|
|
194
193
|
// Compute display URL and external flag
|
|
195
194
|
const { displayUrl, isExternal } = computeDisplayUrl( {
|
|
196
195
|
linkUrl: url,
|
|
197
|
-
|
|
196
|
+
homeUrl,
|
|
198
197
|
} );
|
|
199
198
|
|
|
200
199
|
const image = useSelect(
|
package/src/page-list/index.php
CHANGED
|
@@ -166,7 +166,7 @@ function block_core_page_list_build_css_font_sizes( $context ) {
|
|
|
166
166
|
*
|
|
167
167
|
* @since 5.8.0
|
|
168
168
|
*
|
|
169
|
-
* @param
|
|
169
|
+
* @param string $submenu_visibility The submenu visibility mode: 'hover', 'click', or 'always'.
|
|
170
170
|
* @param boolean $show_submenu_icons Whether to show submenu indicator icons.
|
|
171
171
|
* @param boolean $is_navigation_child If block is a child of Navigation block.
|
|
172
172
|
* @param array $nested_pages The array of nested pages.
|
|
@@ -8,7 +8,6 @@ import clsx from 'clsx';
|
|
|
8
8
|
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
|
|
9
9
|
import { useSelect } from '@wordpress/data';
|
|
10
10
|
import { store as coreStore } from '@wordpress/core-data';
|
|
11
|
-
import { RawHTML } from '@wordpress/element';
|
|
12
11
|
import { safeHTML } from '@wordpress/dom';
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -78,9 +77,10 @@ export default function PageListItemEdit( { context, attributes } ) {
|
|
|
78
77
|
type="button"
|
|
79
78
|
className="wp-block-navigation-item__content wp-block-navigation-submenu__toggle"
|
|
80
79
|
aria-expanded="false"
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
dangerouslySetInnerHTML={ {
|
|
81
|
+
__html: safeHTML( label ),
|
|
82
|
+
} }
|
|
83
|
+
/>
|
|
84
84
|
<span className="wp-block-page-list__submenu-icon wp-block-navigation__submenu-icon">
|
|
85
85
|
<ItemSubmenuIcon />
|
|
86
86
|
</span>
|
|
@@ -91,9 +91,10 @@ export default function PageListItemEdit( { context, attributes } ) {
|
|
|
91
91
|
'wp-block-navigation-item__content': isNavigationChild,
|
|
92
92
|
} ) }
|
|
93
93
|
href={ link }
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
dangerouslySetInnerHTML={ {
|
|
95
|
+
__html: safeHTML( title ),
|
|
96
|
+
} }
|
|
97
|
+
/>
|
|
97
98
|
) }
|
|
98
99
|
{ hasChildren && (
|
|
99
100
|
<>
|