Skip to content

Commit

Permalink
Navigation: Add Post, Page, Category and Tag variations to Link
Browse files Browse the repository at this point in the history
Adds Post, Page, Category and Tag variations to the Navigation Link
block. Each variation sets the type attribute which in turn causes
LinkControl to filter its results using the /wp/v2/search API's type and
subtype params.
  • Loading branch information
noisysocks committed Aug 25, 2020
1 parent a0f36df commit 13a44f8
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 79 deletions.
3 changes: 3 additions & 0 deletions packages/block-editor/src/components/link-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import { ViewerFill } from './viewer-slot';
* @property {boolean=} showSuggestions Whether to present suggestions when typing the URL.
* @property {boolean=} showInitialSuggestions Whether to present initial suggestions immediately.
* @property {boolean=} withCreateSuggestion Whether to allow creation of link value from suggestion.
* @property {Object=} suggestionsQuery Query parameters to pass along to wp.blockEditor.__experimentalFetchLinkSuggestions.
*/

/**
Expand All @@ -109,6 +110,7 @@ function LinkControl( {
createSuggestion,
withCreateSuggestion,
inputValue: propInputValue = '',
suggestionsQuery = {},
} ) {
if ( withCreateSuggestion === undefined && createSuggestion ) {
withCreateSuggestion = true;
Expand Down Expand Up @@ -209,6 +211,7 @@ function LinkControl( {
showInitialSuggestions={ showInitialSuggestions }
allowDirectEntry={ ! noDirectEntry }
showSuggestions={ showSuggestions }
suggestionsQuery={ suggestionsQuery }
>
<div className="block-editor-link-control__search-actions">
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ const LinkControlSearchInput = forwardRef(
fetchSuggestions = null,
allowDirectEntry = true,
showInitialSuggestions = false,
suggestionsQuery = {},
},
ref
) => {
const genericSearchHandler = useSearchHandler(
suggestionsQuery,
allowDirectEntry,
withCreateSuggestion
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,15 @@ export const handleDirectEntry = ( val ) => {
] );
};

export const handleEntitySearch = async (
const handleEntitySearch = async (
val,
args,
suggestionsQuery,
fetchSearchSuggestions,
directEntryHandler,
withCreateSuggestion
) => {
let results = await Promise.all( [
fetchSearchSuggestions( val, {
...( args.isInitialSuggestions ? { perPage: 3 } : {} ),
} ),
fetchSearchSuggestions( val, suggestionsQuery ),
directEntryHandler( val ),
] );

Expand All @@ -65,12 +63,12 @@ export const handleEntitySearch = async (
// just for good measure. That way once the actual results run out we always
// have a URL option to fallback on.
results =
couldBeURL && ! args.isInitialSuggestions
couldBeURL && ! suggestionsQuery.isInitialSuggestions
? results[ 0 ].concat( results[ 1 ] )
: results[ 0 ];

// If displaying initial suggestions just return plain results.
if ( args.isInitialSuggestions ) {
if ( suggestionsQuery.isInitialSuggestions ) {
return results;
}

Expand Down Expand Up @@ -101,6 +99,7 @@ export const handleEntitySearch = async (
};

export default function useSearchHandler(
suggestionsQuery,
allowDirectEntry,
withCreateSuggestion
) {
Expand All @@ -117,12 +116,12 @@ export default function useSearchHandler(
: handleNoop;

return useCallback(
( val, args ) => {
( val, { isInitialSuggestions } ) => {
return isURLLike( val )
? directEntryHandler( val, args )
? directEntryHandler( val, { isInitialSuggestions } )
: handleEntitySearch(
val,
args,
{ ...suggestionsQuery, isInitialSuggestions },
fetchSearchSuggestions,
directEntryHandler,
withCreateSuggestion
Expand Down
27 changes: 24 additions & 3 deletions packages/block-library/src/navigation-link/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,27 @@ const useIsDraggingWithin = ( elementRef ) => {
return isDraggingWithin;
};

/**
* Given the Link block's type attribute, return the query params to give to
* /wp/v2/search.
*
* @param {string} type Link block's type attribute.
* @return {{ type?: string, subtype?: string }} Search query params.
*/
function getSuggestionsQuery( type ) {
switch ( type ) {
case 'post':
case 'page':
return { type: 'post', subtype: type };
case 'category':
return { type: 'term', subtype: 'category' };
case 'tag':
return { type: 'term', subtype: 'post_tag' };
default:
return {};
}
}

function NavigationLinkEdit( {
attributes,
hasDescendants,
Expand All @@ -111,7 +132,7 @@ function NavigationLinkEdit( {
mergeBlocks,
onReplace,
} ) {
const { label, opensInNewTab, url, description, rel } = attributes;
const { label, type, opensInNewTab, url, description, rel } = attributes;
const link = {
url,
opensInNewTab,
Expand Down Expand Up @@ -179,8 +200,7 @@ function NavigationLinkEdit( {
}

async function handleCreatePage( pageTitle ) {
const type = 'page';
const page = await saveEntityRecord( 'postType', type, {
const page = await saveEntityRecord( 'postType', 'page', {
title: pageTitle,
status: 'publish',
} );
Expand Down Expand Up @@ -300,6 +320,7 @@ function NavigationLinkEdit( {
showInitialSuggestions={ true }
withCreateSuggestion={ userCanCreatePages }
createSuggestion={ handleCreatePage }
suggestionsQuery={ getSuggestionsQuery( type ) }
onChange={ ( {
title: newTitle = '',
url: newURL = '',
Expand Down
40 changes: 40 additions & 0 deletions packages/block-library/src/navigation-link/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,56 @@ export { metadata, name };

export const settings = {
title: __( 'Link' ),

icon,

description: __( 'Add a page, link, or another item to your navigation.' ),

variations: [
{
name: 'link',
isDefault: true,
title: __( 'Link' ),
description: __( 'A link to a URL.' ),
attributes: {},
},
{
name: 'post',
title: __( 'Post' ),
description: __( 'A link to a post.' ),
attributes: { type: 'post' },
},
{
name: 'page',
title: __( 'Page' ),
description: __( 'A link to a page.' ),
attributes: { type: 'page' },
},
{
name: 'category',
title: __( 'Category' ),
description: __( 'A link to a category.' ),
attributes: { type: 'category' },
},
{
name: 'tag',
title: __( 'Tag' ),
description: __( 'A link to a tag.' ),
attributes: { type: 'tag' },
},
],

__experimentalLabel: ( { label } ) => label,

merge( leftAttributes, { label: rightLabel = '' } ) {
return {
...leftAttributes,
label: leftAttributes.label + rightLabel,
};
},

edit,

save,

deprecated: [
Expand Down
17 changes: 13 additions & 4 deletions packages/block-library/src/navigation/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { useRef } from '@wordpress/element';
import { useRef, useState } from '@wordpress/element';
import {
InnerBlocks,
InspectorControls,
Expand Down Expand Up @@ -48,7 +48,13 @@ function Navigation( {
// HOOKS
//
const ref = useRef();

const [ isPlaceholderShown, setIsPlaceholderShown ] = useState(
! hasExistingNavItems
);

const { selectBlock } = useDispatch( 'core/block-editor' );

const { TextColor, BackgroundColor, ColorPanel } = __experimentalUseColors(
[
{ name: 'textColor', property: 'color' },
Expand Down Expand Up @@ -85,13 +91,17 @@ function Navigation( {
};
}

// If we don't have existing items then show the Placeholder
if ( ! hasExistingNavItems ) {
//
// RENDER
//

if ( isPlaceholderShown ) {
return (
<Block.div>
<NavigationPlaceholder
ref={ ref }
onCreate={ ( blocks, selectNavigationBlock ) => {
setIsPlaceholderShown( false );
updateInnerBlocks( blocks );
if ( selectNavigationBlock ) {
selectBlock( clientId );
Expand All @@ -107,7 +117,6 @@ function Navigation( {
'is-vertical': attributes.orientation === 'vertical',
} );

// UI State: rendered Block UI
return (
<>
<BlockControls>
Expand Down
5 changes: 5 additions & 0 deletions packages/block-library/src/navigation/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ $navigation-item-height: 46px;
padding: $grid-unit-20;
}

// Ensure that an empty block has space around the appender.
.wp-block-navigation__container {
min-height: $navigation-item-height;
}

// Ensure sub-menus stay open and visible when a nested block is selected.
.wp-block-navigation__container.is-parent-of-selected-block {
visibility: visible;
Expand Down
6 changes: 2 additions & 4 deletions packages/block-library/src/navigation/placeholder.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,7 @@ function NavigationPlaceholder( { onCreate }, ref ) {
const createFromMenu = useCallback( () => {
// If an empty menu was selected, create an empty block.
if ( ! menuItems.length ) {
const blocks = [ createBlock( 'core/navigation-link' ) ];
onCreate( blocks );
onCreate( [] );
return;
}

Expand All @@ -263,8 +262,7 @@ function NavigationPlaceholder( { onCreate }, ref ) {
const { key } = selectedCreateOption;
switch ( key ) {
case CREATE_EMPTY_OPTION_VALUE: {
const blocks = [ createBlock( 'core/navigation-link' ) ];
onCreate( blocks );
onCreate( [] );
return;
}

Expand Down
74 changes: 45 additions & 29 deletions packages/edit-navigation/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,53 +34,69 @@ function disableInsertingNonNavigationBlocks( settings, name ) {
/**
* Fetches link suggestions from the API. This function is an exact copy of a function found at:
*
* wordpress/editor/src/components/provider/index.js
* packages/editor/src/components/provider/index.js
*
* It seems like there is no suitable package to import this from. Ideally it would be either part of core-data.
* Until we refactor it, just copying the code is the simplest solution.
*
* @param {string} search
* @param {Object} [searchArguments]
* @param {number} [searchArguments.perPage=20]
* @param {number} [searchArguments.isInitialSuggestions]
* @param {number} [searchArguments.type]
* @param {number} [searchArguments.subtype]
* @param {Object} [editorSettings]
* @param {boolean} [editorSettings.disablePostFormats=false]
* @return {Promise<Object[]>} List of suggestions
*/
const fetchLinkSuggestions = (
search,
{ perPage = 20 } = {},
{ isInitialSuggestions, type, subtype } = {},
{ disablePostFormats = false } = {}
) => {
const posts = apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'post',
} ),
} );
const perPage = isInitialSuggestions ? 3 : 20;

const terms = apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'term',
} ),
} );
const queries = [];

if ( ! type || type === 'post' ) {
queries.push(
apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'post',
subtype,
} ),
} )
);
}

let formats;
if ( disablePostFormats ) {
formats = Promise.resolve( [] );
} else {
formats = apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'post-format',
} ),
} );
if ( ! type || type === 'term' ) {
queries.push(
apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'term',
subtype,
} ),
} )
);
}

if ( ! disablePostFormats && ( ! type || type === 'post-format' ) ) {
queries.push(
apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'post-format',
subtype,
} ),
} )
);
}

return Promise.all( [ posts, terms, formats ] ).then( ( results ) => {
return Promise.all( queries ).then( ( results ) => {
return map( flatten( results ).slice( 0, perPage ), ( result ) => ( {
id: result.id,
url: result.url,
Expand Down
Loading

0 comments on commit 13a44f8

Please sign in to comment.