From 99f955b7561dcfd1f1312404d4b9eab47f333ae8 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 00:14:34 +0200 Subject: [PATCH 01/10] Block editor settings: delay selecting pageOnFront, pageForPosts --- .../link-control/use-search-handler.js | 10 +-- .../index.js | 68 ++++++++++--------- .../provider/use-block-editor-settings.js | 29 ++++---- 3 files changed, 56 insertions(+), 51 deletions(-) diff --git a/packages/block-editor/src/components/link-control/use-search-handler.js b/packages/block-editor/src/components/link-control/use-search-handler.js index 455d228be1974..96cb49ba0e1e1 100644 --- a/packages/block-editor/src/components/link-control/use-search-handler.js +++ b/packages/block-editor/src/components/link-control/use-search-handler.js @@ -108,19 +108,19 @@ export default function useSearchHandler( allowDirectEntry, withCreateSuggestion ) { - const { fetchSearchSuggestions, pageOnFront, pageForPosts } = useSelect( + const { fetchSearchSuggestions, usePageSettings } = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); - + const settings = getSettings(); return { - pageOnFront: getSettings().pageOnFront, - pageForPosts: getSettings().pageForPosts, + usePageSettings: settings.__experimentalUsePageSettings, fetchSearchSuggestions: - getSettings().__experimentalFetchLinkSuggestions, + settings.__experimentalFetchLinkSuggestions, }; }, [] ); + const { pageOnFront, pageForPosts } = usePageSettings(); const directEntryHandler = allowDirectEntry ? handleDirectEntry diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index 4abc420434cc4..ff7efe18d9b4d 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -27,43 +27,48 @@ import { unlock } from '../../lock-unlock'; const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); const { PatternsMenuItems } = unlock( editPatternsPrivateApis ); -export default function WidgetAreasBlockEditorProvider( { - blockEditorSettings, - children, - ...props -} ) { - const mediaPermissions = useResourcePermissions( 'media' ); - const isLargeViewport = useViewportMatch( 'medium' ); - const { - reusableBlocks, - isFixedToolbarActive, - keepCaretInsideBlock, - pageOnFront, - pageForPosts, - } = useSelect( ( select ) => { - const { canUser, getEntityRecord, getEntityRecords } = - select( coreStore ); + +function usePageSettings() { + return useSelect( ( select ) => { + const { canUser, getEntityRecord } = select( coreStore ); + const siteSettings = canUser( 'read', 'settings' ) ? getEntityRecord( 'root', 'site' ) : undefined; + return { - widgetAreas: select( editWidgetsStore ).getWidgetAreas(), - widgets: select( editWidgetsStore ).getWidgets(), - reusableBlocks: ALLOW_REUSABLE_BLOCKS - ? getEntityRecords( 'postType', 'wp_block' ) - : [], - isFixedToolbarActive: !! select( preferencesStore ).get( - 'core/edit-widgets', - 'fixedToolbar' - ), - keepCaretInsideBlock: !! select( preferencesStore ).get( - 'core/edit-widgets', - 'keepCaretInsideBlock' - ), pageOnFront: siteSettings?.page_on_front, pageForPosts: siteSettings?.page_for_posts, }; }, [] ); +} + +export default function WidgetAreasBlockEditorProvider( { + blockEditorSettings, + children, + ...props +} ) { + const mediaPermissions = useResourcePermissions( 'media' ); + const isLargeViewport = useViewportMatch( 'medium' ); + const { reusableBlocks, isFixedToolbarActive, keepCaretInsideBlock } = + useSelect( ( select ) => { + const { getEntityRecords } = select( coreStore ); + return { + widgetAreas: select( editWidgetsStore ).getWidgetAreas(), + widgets: select( editWidgetsStore ).getWidgets(), + reusableBlocks: ALLOW_REUSABLE_BLOCKS + ? getEntityRecords( 'postType', 'wp_block' ) + : [], + isFixedToolbarActive: !! select( preferencesStore ).get( + 'core/edit-widgets', + 'fixedToolbar' + ), + keepCaretInsideBlock: !! select( preferencesStore ).get( + 'core/edit-widgets', + 'keepCaretInsideBlock' + ), + }; + }, [] ); const { setIsInserterOpened } = useDispatch( editWidgetsStore ); const settings = useMemo( () => { @@ -85,8 +90,7 @@ export default function WidgetAreasBlockEditorProvider( { mediaUpload: mediaUploadBlockEditor, templateLock: 'all', __experimentalSetIsInserterOpened: setIsInserterOpened, - pageOnFront, - pageForPosts, + __experimentalUsePageSettings: usePageSettings, }; }, [ blockEditorSettings, @@ -96,8 +100,6 @@ export default function WidgetAreasBlockEditorProvider( { mediaPermissions.canCreate, reusableBlocks, setIsInserterOpened, - pageOnFront, - pageForPosts, ] ); const widgetAreaId = useLastSelectedWidgetArea(); 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 49f61815f663d..1207eeca811b2 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -78,6 +78,21 @@ const BLOCK_EDITOR_SETTINGS = [ '__experimentalArchiveTitleNameLabel', ]; +function usePageSettings() { + return useSelect( ( select ) => { + const { canUser, getEntityRecord } = select( coreStore ); + + const siteSettings = canUser( 'read', 'settings' ) + ? getEntityRecord( 'root', 'site' ) + : undefined; + + return { + pageOnFront: siteSettings?.page_on_front, + pageForPosts: siteSettings?.page_for_posts, + }; + }, [] ); +} + /** * React hook used to compute the block editor settings to use for the post editor. * @@ -93,8 +108,6 @@ function useBlockEditorSettings( settings, postType, postId ) { hasUploadPermissions, canUseUnfilteredHTML, userCanCreatePages, - pageOnFront, - pageForPosts, userPatternCategories, restBlockPatterns, restBlockPatternCategories, @@ -104,17 +117,12 @@ function useBlockEditorSettings( settings, postType, postId ) { const { canUser, getRawEntityRecord, - getEntityRecord, getUserPatternCategories, getEntityRecords, getBlockPatterns, getBlockPatternCategories, } = select( coreStore ); - const siteSettings = canUser( 'read', 'settings' ) - ? getEntityRecord( 'root', 'site' ) - : undefined; - return { canUseUnfilteredHTML: getRawEntityRecord( 'postType', @@ -128,8 +136,6 @@ function useBlockEditorSettings( settings, postType, postId ) { : EMPTY_BLOCKS_LIST, // Reusable blocks are fetched in the native version of this hook. hasUploadPermissions: canUser( 'create', 'media' ) ?? true, userCanCreatePages: canUser( 'create', 'pages' ), - pageOnFront: siteSettings?.page_on_front, - pageForPosts: siteSettings?.page_for_posts, userPatternCategories: getUserPatternCategories(), restBlockPatterns: getBlockPatterns(), restBlockPatternCategories: getBlockPatternCategories(), @@ -229,8 +235,7 @@ function useBlockEditorSettings( settings, postType, postId ) { // Check these two properties: they were not present in the site editor. __experimentalCreatePageEntity: createPageEntity, __experimentalUserCanCreatePages: userCanCreatePages, - pageOnFront, - pageForPosts, + __experimentalUsePageSettings: usePageSettings, __experimentalPreferPatternsOnRoot: postType === 'wp_template', templateLock: postType === 'wp_navigation' ? 'insert' : settings.templateLock, @@ -251,8 +256,6 @@ function useBlockEditorSettings( settings, postType, postId ) { undo, createPageEntity, userCanCreatePages, - pageOnFront, - pageForPosts, postType, setIsInserterOpened, ] From 8d05001ba8700073466e87d8314df252524a7397 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 00:34:08 +0200 Subject: [PATCH 02/10] Check if usePageSettings exists --- .../src/components/link-control/use-search-handler.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/link-control/use-search-handler.js b/packages/block-editor/src/components/link-control/use-search-handler.js index 96cb49ba0e1e1..6cd9c11d91a73 100644 --- a/packages/block-editor/src/components/link-control/use-search-handler.js +++ b/packages/block-editor/src/components/link-control/use-search-handler.js @@ -120,7 +120,10 @@ export default function useSearchHandler( }, [] ); - const { pageOnFront, pageForPosts } = usePageSettings(); + // The function should either be undefined or a stable function reference + // throughout the editor lifetime, much like importing a function from a + // module. Maybe warn if this becomes a common pattern and it does change? + const { pageOnFront, pageForPosts } = usePageSettings?.() ?? {}; const directEntryHandler = allowDirectEntry ? handleDirectEntry From a9813b62f89ec627b200a98efa1f9744448ea06f Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 09:27:35 +0200 Subject: [PATCH 03/10] Use Symbol instead of experimental prefix --- .../src/components/link-control/use-search-handler.js | 3 ++- packages/block-editor/src/private-apis.js | 2 ++ packages/block-editor/src/private-settings-keys.js | 3 +++ .../src/components/provider/use-block-editor-settings.js | 6 +++++- 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 packages/block-editor/src/private-settings-keys.js diff --git a/packages/block-editor/src/components/link-control/use-search-handler.js b/packages/block-editor/src/components/link-control/use-search-handler.js index 6cd9c11d91a73..76cb5c9d92ded 100644 --- a/packages/block-editor/src/components/link-control/use-search-handler.js +++ b/packages/block-editor/src/components/link-control/use-search-handler.js @@ -17,6 +17,7 @@ import { URL_TYPE, } from './constants'; import { store as blockEditorStore } from '../../store'; +import { default as settingsKeys } from '../../private-settings-keys'; export const handleNoop = () => Promise.resolve( [] ); @@ -113,7 +114,7 @@ export default function useSearchHandler( const { getSettings } = select( blockEditorStore ); const settings = getSettings(); return { - usePageSettings: settings.__experimentalUsePageSettings, + usePageSettings: settings[ settingsKeys.usePageSettings ], fetchSearchSuggestions: settings.__experimentalFetchLinkSuggestions, }; diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 74bf4af421dfb..ec51a20f845d2 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -24,6 +24,7 @@ import { import { usesContextKey } from './components/rich-text/format-edit'; import { ExperimentalBlockCanvas } from './components/block-canvas'; import { getDuotoneFilter } from './components/duotone/utils'; +import { default as settingsKeys } from './private-settings-keys'; /** * Private @wordpress/block-editor APIs. @@ -52,4 +53,5 @@ lock( privateApis, { ReusableBlocksRenameHint, useReusableBlocksRenameHint, usesContextKey, + settingsKeys, } ); diff --git a/packages/block-editor/src/private-settings-keys.js b/packages/block-editor/src/private-settings-keys.js new file mode 100644 index 0000000000000..6d3b05ea309c7 --- /dev/null +++ b/packages/block-editor/src/private-settings-keys.js @@ -0,0 +1,3 @@ +export default { + usePageSettings: Symbol( 'usePageSettings' ), +}; 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 1207eeca811b2..e319e4f3c7167 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -9,6 +9,7 @@ import { __experimentalFetchUrlData as fetchUrlData, } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; /** * Internal dependencies @@ -16,6 +17,9 @@ import { __ } from '@wordpress/i18n'; import inserterMediaCategories from '../media-categories'; import { mediaUpload } from '../../utils'; import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +const { settingsKeys } = unlock( blockEditorPrivateApis ); const EMPTY_BLOCKS_LIST = []; @@ -235,7 +239,7 @@ function useBlockEditorSettings( settings, postType, postId ) { // Check these two properties: they were not present in the site editor. __experimentalCreatePageEntity: createPageEntity, __experimentalUserCanCreatePages: userCanCreatePages, - __experimentalUsePageSettings: usePageSettings, + [ settingsKeys.usePageSettings ]: usePageSettings, __experimentalPreferPatternsOnRoot: postType === 'wp_template', templateLock: postType === 'wp_navigation' ? 'insert' : settings.templateLock, From 3991a708ca6f3c0a33fb89ce47f600d40cabb62b Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 09:40:39 +0200 Subject: [PATCH 04/10] Add private keys for native --- packages/block-editor/src/private-apis.native.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/block-editor/src/private-apis.native.js b/packages/block-editor/src/private-apis.native.js index 5555e00477e7b..f710cbdc2aa3a 100644 --- a/packages/block-editor/src/private-apis.native.js +++ b/packages/block-editor/src/private-apis.native.js @@ -4,6 +4,7 @@ import * as globalStyles from './components/global-styles'; import { ExperimentalBlockEditorProvider } from './components/provider'; import { lock } from './lock-unlock'; +import { default as settingsKeys } from './private-settings-keys'; /** * Private @wordpress/block-editor APIs. @@ -12,4 +13,5 @@ export const privateApis = {}; lock( privateApis, { ...globalStyles, ExperimentalBlockEditorProvider, + settingsKeys, } ); From ac45b4415fdee5dc3f1d3d496141cc141029f20b Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 09:45:27 +0200 Subject: [PATCH 05/10] use new key for all editors --- .../components/widget-areas-block-editor-provider/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index ff7efe18d9b4d..b00b157092fc8 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -25,7 +25,9 @@ import { store as editWidgetsStore } from '../../store'; import { ALLOW_REUSABLE_BLOCKS } from '../../constants'; import { unlock } from '../../lock-unlock'; -const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); +const { ExperimentalBlockEditorProvider, settingsKeys } = unlock( + blockEditorPrivateApis +); const { PatternsMenuItems } = unlock( editPatternsPrivateApis ); function usePageSettings() { @@ -90,7 +92,7 @@ export default function WidgetAreasBlockEditorProvider( { mediaUpload: mediaUploadBlockEditor, templateLock: 'all', __experimentalSetIsInserterOpened: setIsInserterOpened, - __experimentalUsePageSettings: usePageSettings, + [ settingsKeys.usePageSettings ]: usePageSettings, }; }, [ blockEditorSettings, From 80a3e546a25c34d61ea361ea9261e99ad0b2f78c Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 11:53:21 +0200 Subject: [PATCH 06/10] Extract useHandleEntitySearch --- .../link-control/use-search-handler.js | 136 +++++++++--------- 1 file changed, 67 insertions(+), 69 deletions(-) diff --git a/packages/block-editor/src/components/link-control/use-search-handler.js b/packages/block-editor/src/components/link-control/use-search-handler.js index 76cb5c9d92ded..2e32b1eea367e 100644 --- a/packages/block-editor/src/components/link-control/use-search-handler.js +++ b/packages/block-editor/src/components/link-control/use-search-handler.js @@ -48,67 +48,7 @@ export const handleDirectEntry = ( val ) => { ] ); }; -const handleEntitySearch = async ( - val, - suggestionsQuery, - fetchSearchSuggestions, - withCreateSuggestion, - pageOnFront, - pageForPosts -) => { - const { isInitialSuggestions } = suggestionsQuery; - - const results = await fetchSearchSuggestions( val, suggestionsQuery ); - - // Identify front page and update type to match. - results.map( ( result ) => { - if ( Number( result.id ) === pageOnFront ) { - result.isFrontPage = true; - return result; - } else if ( Number( result.id ) === pageForPosts ) { - result.isBlogHome = true; - return result; - } - - return result; - } ); - - // If displaying initial suggestions just return plain results. - if ( isInitialSuggestions ) { - return results; - } - - // Here we append a faux suggestion to represent a "CREATE" option. This - // is detected in the rendering of the search results and handled as a - // special case. This is currently necessary because the suggestions - // dropdown will only appear if there are valid suggestions and - // therefore unless the create option is a suggestion it will not - // display in scenarios where there are no results returned from the - // API. In addition promoting CREATE to a first class suggestion affords - // the a11y benefits afforded by `URLInput` to all suggestions (eg: - // keyboard handling, ARIA roles...etc). - // - // Note also that the value of the `title` and `url` properties must correspond - // to the text value of the ``. This is because `title` is used - // when creating the suggestion. Similarly `url` is used when using keyboard to select - // the suggestion (the
`onSubmit` handler falls-back to `url`). - return isURLLike( val ) || ! withCreateSuggestion - ? results - : results.concat( { - // the `id` prop is intentionally ommitted here because it - // is never exposed as part of the component's public API. - // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. - title: val, // Must match the existing ``s text value. - url: val, // Must match the existing ``s text value. - type: CREATE_TYPE, - } ); -}; - -export default function useSearchHandler( - suggestionsQuery, - allowDirectEntry, - withCreateSuggestion -) { +function useHandleEntitySearch() { const { fetchSearchSuggestions, usePageSettings } = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); @@ -123,13 +63,76 @@ export default function useSearchHandler( ); // The function should either be undefined or a stable function reference // throughout the editor lifetime, much like importing a function from a - // module. Maybe warn if this becomes a common pattern and it does change? + // module. const { pageOnFront, pageForPosts } = usePageSettings?.() ?? {}; + return useCallback( + async ( val, suggestionsQuery, withCreateSuggestion ) => { + const { isInitialSuggestions } = suggestionsQuery; + + const results = await fetchSearchSuggestions( + val, + suggestionsQuery + ); + + // Identify front page and update type to match. + results.map( ( result ) => { + if ( Number( result.id ) === pageOnFront ) { + result.isFrontPage = true; + return result; + } else if ( Number( result.id ) === pageForPosts ) { + result.isBlogHome = true; + return result; + } + + return result; + } ); + + // If displaying initial suggestions just return plain results. + if ( isInitialSuggestions ) { + return results; + } + + // Here we append a faux suggestion to represent a "CREATE" option. This + // is detected in the rendering of the search results and handled as a + // special case. This is currently necessary because the suggestions + // dropdown will only appear if there are valid suggestions and + // therefore unless the create option is a suggestion it will not + // display in scenarios where there are no results returned from the + // API. In addition promoting CREATE to a first class suggestion affords + // the a11y benefits afforded by `URLInput` to all suggestions (eg: + // keyboard handling, ARIA roles...etc). + // + // Note also that the value of the `title` and `url` properties must correspond + // to the text value of the ``. This is because `title` is used + // when creating the suggestion. Similarly `url` is used when using keyboard to select + // the suggestion (the `onSubmit` handler falls-back to `url`). + return isURLLike( val ) || ! withCreateSuggestion + ? results + : results.concat( { + // the `id` prop is intentionally ommitted here because it + // is never exposed as part of the component's public API. + // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. + title: val, // Must match the existing ``s text value. + url: val, // Must match the existing ``s text value. + type: CREATE_TYPE, + } ); + }, + [ fetchSearchSuggestions, pageOnFront, pageForPosts ] + ); +} + +export default function useSearchHandler( + suggestionsQuery, + allowDirectEntry, + withCreateSuggestion +) { const directEntryHandler = allowDirectEntry ? handleDirectEntry : handleNoop; + const handleEntitySearch = useHandleEntitySearch(); + return useCallback( ( val, { isInitialSuggestions } ) => { return isURLLike( val ) @@ -137,17 +140,12 @@ export default function useSearchHandler( : handleEntitySearch( val, { ...suggestionsQuery, isInitialSuggestions }, - fetchSearchSuggestions, - withCreateSuggestion, - pageOnFront, - pageForPosts + withCreateSuggestion ); }, [ directEntryHandler, - fetchSearchSuggestions, - pageOnFront, - pageForPosts, + handleEntitySearch, suggestionsQuery, withCreateSuggestion, ] From cc803451332655d67d4ad69cdfe2d13a8f5274a1 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 12:21:02 +0200 Subject: [PATCH 07/10] Absorb fetchLinkSuggestions --- .../link-control/use-search-handler.js | 93 ++----------------- .../block-editor/src/private-settings-keys.js | 2 +- .../index.js | 78 +++++++++++++++- packages/edit-widgets/src/index.js | 4 - .../provider/use-block-editor-settings.js | 77 +++++++++++++-- 5 files changed, 154 insertions(+), 100 deletions(-) diff --git a/packages/block-editor/src/components/link-control/use-search-handler.js b/packages/block-editor/src/components/link-control/use-search-handler.js index 2e32b1eea367e..ea4f0472d62c9 100644 --- a/packages/block-editor/src/components/link-control/use-search-handler.js +++ b/packages/block-editor/src/components/link-control/use-search-handler.js @@ -9,18 +9,10 @@ import { useSelect } from '@wordpress/data'; * Internal dependencies */ import isURLLike from './is-url-like'; -import { - CREATE_TYPE, - TEL_TYPE, - MAILTO_TYPE, - INTERNAL_TYPE, - URL_TYPE, -} from './constants'; +import { TEL_TYPE, MAILTO_TYPE, INTERNAL_TYPE, URL_TYPE } from './constants'; import { store as blockEditorStore } from '../../store'; import { default as settingsKeys } from '../../private-settings-keys'; -export const handleNoop = () => Promise.resolve( [] ); - export const handleDirectEntry = ( val ) => { let type = URL_TYPE; @@ -48,90 +40,23 @@ export const handleDirectEntry = ( val ) => { ] ); }; -function useHandleEntitySearch() { - const { fetchSearchSuggestions, usePageSettings } = useSelect( - ( select ) => { - const { getSettings } = select( blockEditorStore ); - const settings = getSettings(); - return { - usePageSettings: settings[ settingsKeys.usePageSettings ], - fetchSearchSuggestions: - settings.__experimentalFetchLinkSuggestions, - }; - }, - [] - ); - // The function should either be undefined or a stable function reference - // throughout the editor lifetime, much like importing a function from a - // module. - const { pageOnFront, pageForPosts } = usePageSettings?.() ?? {}; - - return useCallback( - async ( val, suggestionsQuery, withCreateSuggestion ) => { - const { isInitialSuggestions } = suggestionsQuery; - - const results = await fetchSearchSuggestions( - val, - suggestionsQuery - ); - - // Identify front page and update type to match. - results.map( ( result ) => { - if ( Number( result.id ) === pageOnFront ) { - result.isFrontPage = true; - return result; - } else if ( Number( result.id ) === pageForPosts ) { - result.isBlogHome = true; - return result; - } - - return result; - } ); - - // If displaying initial suggestions just return plain results. - if ( isInitialSuggestions ) { - return results; - } - - // Here we append a faux suggestion to represent a "CREATE" option. This - // is detected in the rendering of the search results and handled as a - // special case. This is currently necessary because the suggestions - // dropdown will only appear if there are valid suggestions and - // therefore unless the create option is a suggestion it will not - // display in scenarios where there are no results returned from the - // API. In addition promoting CREATE to a first class suggestion affords - // the a11y benefits afforded by `URLInput` to all suggestions (eg: - // keyboard handling, ARIA roles...etc). - // - // Note also that the value of the `title` and `url` properties must correspond - // to the text value of the ``. This is because `title` is used - // when creating the suggestion. Similarly `url` is used when using keyboard to select - // the suggestion (the `onSubmit` handler falls-back to `url`). - return isURLLike( val ) || ! withCreateSuggestion - ? results - : results.concat( { - // the `id` prop is intentionally ommitted here because it - // is never exposed as part of the component's public API. - // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. - title: val, // Must match the existing ``s text value. - url: val, // Must match the existing ``s text value. - type: CREATE_TYPE, - } ); - }, - [ fetchSearchSuggestions, pageOnFront, pageForPosts ] - ); -} - export default function useSearchHandler( suggestionsQuery, allowDirectEntry, withCreateSuggestion ) { + const handleNoop = useCallback( () => Promise.resolve( [] ), [] ); const directEntryHandler = allowDirectEntry ? handleDirectEntry : handleNoop; - const handleEntitySearch = useHandleEntitySearch(); + const useHandleEntitySearch = useSelect( ( select ) => { + const settings = select( blockEditorStore ).getSettings(); + return settings[ settingsKeys.useLinkControlEntitySearch ]; + }, [] ); + // Note that this is meant to be a stable function and doesn't change, so it + // doesn't break the rules of hooks. + const handleEntitySearch = useHandleEntitySearch?.() || handleNoop; return useCallback( ( val, { isInitialSuggestions } ) => { diff --git a/packages/block-editor/src/private-settings-keys.js b/packages/block-editor/src/private-settings-keys.js index 6d3b05ea309c7..e58de7c064145 100644 --- a/packages/block-editor/src/private-settings-keys.js +++ b/packages/block-editor/src/private-settings-keys.js @@ -1,3 +1,3 @@ export default { - usePageSettings: Symbol( 'usePageSettings' ), + useLinkControlEntitySearch: Symbol( 'useLinkControlEntitySearch' ), }; diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index b00b157092fc8..5b4f67c601c3b 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -9,9 +9,13 @@ import { useEntityBlockEditor, store as coreStore, useResourcePermissions, + __experimentalFetchLinkSuggestions as fetchLinkSuggestions, } from '@wordpress/core-data'; -import { useMemo } from '@wordpress/element'; -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { useMemo, useCallback } from '@wordpress/element'; +import { + privateApis as blockEditorPrivateApis, + store as blockEditorStore, +} from '@wordpress/block-editor'; import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -30,8 +34,15 @@ const { ExperimentalBlockEditorProvider, settingsKeys } = unlock( ); const { PatternsMenuItems } = unlock( editPatternsPrivateApis ); -function usePageSettings() { - return useSelect( ( select ) => { +function useLinkControlEntitySearch() { + const settings = useSelect( + ( select ) => select( blockEditorStore ).getSettings(), + [] + ); + // The function should either be undefined or a stable function reference + // throughout the editor lifetime, much like importing a function from a + // module. + const { pageOnFront, pageForPosts } = useSelect( ( select ) => { const { canUser, getEntityRecord } = select( coreStore ); const siteSettings = canUser( 'read', 'settings' ) @@ -43,6 +54,62 @@ function usePageSettings() { pageForPosts: siteSettings?.page_for_posts, }; }, [] ); + + return useCallback( + async ( val, suggestionsQuery, withCreateSuggestion ) => { + const { isInitialSuggestions } = suggestionsQuery; + + const results = await fetchLinkSuggestions( + val, + suggestionsQuery, + settings + ); + + // Identify front page and update type to match. + results.map( ( result ) => { + if ( Number( result.id ) === pageOnFront ) { + result.isFrontPage = true; + return result; + } else if ( Number( result.id ) === pageForPosts ) { + result.isBlogHome = true; + return result; + } + + return result; + } ); + + // If displaying initial suggestions just return plain results. + if ( isInitialSuggestions ) { + return results; + } + + // Here we append a faux suggestion to represent a "CREATE" option. This + // is detected in the rendering of the search results and handled as a + // special case. This is currently necessary because the suggestions + // dropdown will only appear if there are valid suggestions and + // therefore unless the create option is a suggestion it will not + // display in scenarios where there are no results returned from the + // API. In addition promoting CREATE to a first class suggestion affords + // the a11y benefits afforded by `URLInput` to all suggestions (eg: + // keyboard handling, ARIA roles...etc). + // + // Note also that the value of the `title` and `url` properties must correspond + // to the text value of the ``. This is because `title` is used + // when creating the suggestion. Similarly `url` is used when using keyboard to select + // the suggestion (the `onSubmit` handler falls-back to `url`). + return ! withCreateSuggestion + ? results + : results.concat( { + // the `id` prop is intentionally ommitted here because it + // is never exposed as part of the component's public API. + // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. + title: val, // Must match the existing ``s text value. + url: val, // Must match the existing ``s text value. + type: '__CREATE__', + } ); + }, + [ pageOnFront, pageForPosts, settings ] + ); } export default function WidgetAreasBlockEditorProvider( { @@ -92,7 +159,8 @@ export default function WidgetAreasBlockEditorProvider( { mediaUpload: mediaUploadBlockEditor, templateLock: 'all', __experimentalSetIsInserterOpened: setIsInserterOpened, - [ settingsKeys.usePageSettings ]: usePageSettings, + [ settingsKeys.useLinkControlEntitySearch ]: + useLinkControlEntitySearch, }; }, [ blockEditorSettings, diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index eb87d22fefef9..768b12a62c031 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -15,7 +15,6 @@ import { __experimentalGetCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; -import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; import { registerLegacyWidgetBlock, registerLegacyWidgetVariations, @@ -82,9 +81,6 @@ export function initializeEditor( id, settings ) { registerBlock( widgetArea ); registerWidgetGroupBlock(); - settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => - fetchLinkSuggestions( search, searchOptions, settings ); - // As we are unregistering `core/freeform` to avoid the Classic block, we must // replace it with something as the default freeform content handler. Failure to // do this will result in errors in the default block parser. 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 e319e4f3c7167..88abe920b2f95 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -9,7 +9,10 @@ import { __experimentalFetchUrlData as fetchUrlData, } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { + privateApis as blockEditorPrivateApis, + store as blockEditorStore, +} from '@wordpress/block-editor'; /** * Internal dependencies @@ -82,8 +85,15 @@ const BLOCK_EDITOR_SETTINGS = [ '__experimentalArchiveTitleNameLabel', ]; -function usePageSettings() { - return useSelect( ( select ) => { +function useLinkControlEntitySearch() { + const settings = useSelect( + ( select ) => select( blockEditorStore ).getSettings(), + [] + ); + // The function should either be undefined or a stable function reference + // throughout the editor lifetime, much like importing a function from a + // module. + const { pageOnFront, pageForPosts } = useSelect( ( select ) => { const { canUser, getEntityRecord } = select( coreStore ); const siteSettings = canUser( 'read', 'settings' ) @@ -95,6 +105,62 @@ function usePageSettings() { pageForPosts: siteSettings?.page_for_posts, }; }, [] ); + + return useCallback( + async ( val, suggestionsQuery, withCreateSuggestion ) => { + const { isInitialSuggestions } = suggestionsQuery; + + const results = await fetchLinkSuggestions( + val, + suggestionsQuery, + settings + ); + + // Identify front page and update type to match. + results.map( ( result ) => { + if ( Number( result.id ) === pageOnFront ) { + result.isFrontPage = true; + return result; + } else if ( Number( result.id ) === pageForPosts ) { + result.isBlogHome = true; + return result; + } + + return result; + } ); + + // If displaying initial suggestions just return plain results. + if ( isInitialSuggestions ) { + return results; + } + + // Here we append a faux suggestion to represent a "CREATE" option. This + // is detected in the rendering of the search results and handled as a + // special case. This is currently necessary because the suggestions + // dropdown will only appear if there are valid suggestions and + // therefore unless the create option is a suggestion it will not + // display in scenarios where there are no results returned from the + // API. In addition promoting CREATE to a first class suggestion affords + // the a11y benefits afforded by `URLInput` to all suggestions (eg: + // keyboard handling, ARIA roles...etc). + // + // Note also that the value of the `title` and `url` properties must correspond + // to the text value of the ``. This is because `title` is used + // when creating the suggestion. Similarly `url` is used when using keyboard to select + // the suggestion (the `onSubmit` handler falls-back to `url`). + return ! withCreateSuggestion + ? results + : results.concat( { + // the `id` prop is intentionally ommitted here because it + // is never exposed as part of the component's public API. + // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. + title: val, // Must match the existing ``s text value. + url: val, // Must match the existing ``s text value. + type: '__CREATE__', + } ); + }, + [ pageOnFront, pageForPosts, settings ] + ); } /** @@ -224,8 +290,6 @@ function useBlockEditorSettings( settings, postType, postId ) { __experimentalBlockPatterns: blockPatterns, __experimentalBlockPatternCategories: blockPatternCategories, __experimentalUserPatternCategories: userPatternCategories, - __experimentalFetchLinkSuggestions: ( search, searchOptions ) => - fetchLinkSuggestions( search, searchOptions, settings ), inserterMediaCategories, __experimentalFetchRichUrlData: fetchUrlData, // Todo: This only checks the top level post, not the post within a template or any other entity that can be edited. @@ -239,7 +303,8 @@ function useBlockEditorSettings( settings, postType, postId ) { // Check these two properties: they were not present in the site editor. __experimentalCreatePageEntity: createPageEntity, __experimentalUserCanCreatePages: userCanCreatePages, - [ settingsKeys.usePageSettings ]: usePageSettings, + [ settingsKeys.useLinkControlEntitySearch ]: + useLinkControlEntitySearch, __experimentalPreferPatternsOnRoot: postType === 'wp_template', templateLock: postType === 'wp_navigation' ? 'insert' : settings.templateLock, From 15f78d91e0f45820d3065ac5eb46e2bf8ac3a9ad Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 12:33:36 +0200 Subject: [PATCH 08/10] Move hook to core-data --- packages/core-data/src/fetch/index.js | 93 ++++++++++++++++++- .../index.js | 87 +---------------- .../provider/use-block-editor-settings.js | 85 +---------------- 3 files changed, 97 insertions(+), 168 deletions(-) diff --git a/packages/core-data/src/fetch/index.js b/packages/core-data/src/fetch/index.js index 8d4d28e3b0db8..74a7aded43a95 100644 --- a/packages/core-data/src/fetch/index.js +++ b/packages/core-data/src/fetch/index.js @@ -1,2 +1,93 @@ -export { default as __experimentalFetchLinkSuggestions } from './__experimental-fetch-link-suggestions'; +/** + * WordPress dependencies + */ +import { useCallback } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { store as coreStore } from '../'; +import { default as fetchLinkSuggestions } from './__experimental-fetch-link-suggestions'; + +export const __experimentalFetchLinkSuggestions = fetchLinkSuggestions; export { default as __experimentalFetchUrlData } from './__experimental-fetch-url-data'; + +export function __experimentalUseLinkControlEntitySearch() { + const settings = useSelect( + ( select ) => select( blockEditorStore ).getSettings(), + [] + ); + // The function should either be undefined or a stable function reference + // throughout the editor lifetime, much like importing a function from a + // module. + const { pageOnFront, pageForPosts } = useSelect( ( select ) => { + const { canUser, getEntityRecord } = select( coreStore ); + + const siteSettings = canUser( 'read', 'settings' ) + ? getEntityRecord( 'root', 'site' ) + : undefined; + + return { + pageOnFront: siteSettings?.page_on_front, + pageForPosts: siteSettings?.page_for_posts, + }; + }, [] ); + + return useCallback( + async ( val, suggestionsQuery, withCreateSuggestion ) => { + const { isInitialSuggestions } = suggestionsQuery; + + const results = await fetchLinkSuggestions( + val, + suggestionsQuery, + settings + ); + + // Identify front page and update type to match. + results.map( ( result ) => { + if ( Number( result.id ) === pageOnFront ) { + result.isFrontPage = true; + return result; + } else if ( Number( result.id ) === pageForPosts ) { + result.isBlogHome = true; + return result; + } + + return result; + } ); + + // If displaying initial suggestions just return plain results. + if ( isInitialSuggestions ) { + return results; + } + + // Here we append a faux suggestion to represent a "CREATE" option. This + // is detected in the rendering of the search results and handled as a + // special case. This is currently necessary because the suggestions + // dropdown will only appear if there are valid suggestions and + // therefore unless the create option is a suggestion it will not + // display in scenarios where there are no results returned from the + // API. In addition promoting CREATE to a first class suggestion affords + // the a11y benefits afforded by `URLInput` to all suggestions (eg: + // keyboard handling, ARIA roles...etc). + // + // Note also that the value of the `title` and `url` properties must correspond + // to the text value of the ``. This is because `title` is used + // when creating the suggestion. Similarly `url` is used when using keyboard to select + // the suggestion (the `onSubmit` handler falls-back to `url`). + return ! withCreateSuggestion + ? results + : results.concat( { + // the `id` prop is intentionally ommitted here because it + // is never exposed as part of the component's public API. + // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. + title: val, // Must match the existing ``s text value. + url: val, // Must match the existing ``s text value. + type: '__CREATE__', + } ); + }, + [ pageOnFront, pageForPosts, settings ] + ); +} diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index 5b4f67c601c3b..24df41c54637a 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -9,13 +9,10 @@ import { useEntityBlockEditor, store as coreStore, useResourcePermissions, - __experimentalFetchLinkSuggestions as fetchLinkSuggestions, + __experimentalUseLinkControlEntitySearch as useLinkControlEntitySearch, } from '@wordpress/core-data'; -import { useMemo, useCallback } from '@wordpress/element'; -import { - privateApis as blockEditorPrivateApis, - store as blockEditorStore, -} from '@wordpress/block-editor'; +import { useMemo } from '@wordpress/element'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -34,84 +31,6 @@ const { ExperimentalBlockEditorProvider, settingsKeys } = unlock( ); const { PatternsMenuItems } = unlock( editPatternsPrivateApis ); -function useLinkControlEntitySearch() { - const settings = useSelect( - ( select ) => select( blockEditorStore ).getSettings(), - [] - ); - // The function should either be undefined or a stable function reference - // throughout the editor lifetime, much like importing a function from a - // module. - const { pageOnFront, pageForPosts } = useSelect( ( select ) => { - const { canUser, getEntityRecord } = select( coreStore ); - - const siteSettings = canUser( 'read', 'settings' ) - ? getEntityRecord( 'root', 'site' ) - : undefined; - - return { - pageOnFront: siteSettings?.page_on_front, - pageForPosts: siteSettings?.page_for_posts, - }; - }, [] ); - - return useCallback( - async ( val, suggestionsQuery, withCreateSuggestion ) => { - const { isInitialSuggestions } = suggestionsQuery; - - const results = await fetchLinkSuggestions( - val, - suggestionsQuery, - settings - ); - - // Identify front page and update type to match. - results.map( ( result ) => { - if ( Number( result.id ) === pageOnFront ) { - result.isFrontPage = true; - return result; - } else if ( Number( result.id ) === pageForPosts ) { - result.isBlogHome = true; - return result; - } - - return result; - } ); - - // If displaying initial suggestions just return plain results. - if ( isInitialSuggestions ) { - return results; - } - - // Here we append a faux suggestion to represent a "CREATE" option. This - // is detected in the rendering of the search results and handled as a - // special case. This is currently necessary because the suggestions - // dropdown will only appear if there are valid suggestions and - // therefore unless the create option is a suggestion it will not - // display in scenarios where there are no results returned from the - // API. In addition promoting CREATE to a first class suggestion affords - // the a11y benefits afforded by `URLInput` to all suggestions (eg: - // keyboard handling, ARIA roles...etc). - // - // Note also that the value of the `title` and `url` properties must correspond - // to the text value of the ``. This is because `title` is used - // when creating the suggestion. Similarly `url` is used when using keyboard to select - // the suggestion (the `onSubmit` handler falls-back to `url`). - return ! withCreateSuggestion - ? results - : results.concat( { - // the `id` prop is intentionally ommitted here because it - // is never exposed as part of the component's public API. - // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. - title: val, // Must match the existing ``s text value. - url: val, // Must match the existing ``s text value. - type: '__CREATE__', - } ); - }, - [ pageOnFront, pageForPosts, settings ] - ); -} - export default function WidgetAreasBlockEditorProvider( { blockEditorSettings, children, 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 88abe920b2f95..c3b4ba7a597c3 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -5,14 +5,11 @@ import { Platform, useMemo, useCallback } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { store as coreStore, - __experimentalFetchLinkSuggestions as fetchLinkSuggestions, + __experimentalUseLinkControlEntitySearch as useLinkControlEntitySearch, __experimentalFetchUrlData as fetchUrlData, } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; -import { - privateApis as blockEditorPrivateApis, - store as blockEditorStore, -} from '@wordpress/block-editor'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; /** * Internal dependencies @@ -85,84 +82,6 @@ const BLOCK_EDITOR_SETTINGS = [ '__experimentalArchiveTitleNameLabel', ]; -function useLinkControlEntitySearch() { - const settings = useSelect( - ( select ) => select( blockEditorStore ).getSettings(), - [] - ); - // The function should either be undefined or a stable function reference - // throughout the editor lifetime, much like importing a function from a - // module. - const { pageOnFront, pageForPosts } = useSelect( ( select ) => { - const { canUser, getEntityRecord } = select( coreStore ); - - const siteSettings = canUser( 'read', 'settings' ) - ? getEntityRecord( 'root', 'site' ) - : undefined; - - return { - pageOnFront: siteSettings?.page_on_front, - pageForPosts: siteSettings?.page_for_posts, - }; - }, [] ); - - return useCallback( - async ( val, suggestionsQuery, withCreateSuggestion ) => { - const { isInitialSuggestions } = suggestionsQuery; - - const results = await fetchLinkSuggestions( - val, - suggestionsQuery, - settings - ); - - // Identify front page and update type to match. - results.map( ( result ) => { - if ( Number( result.id ) === pageOnFront ) { - result.isFrontPage = true; - return result; - } else if ( Number( result.id ) === pageForPosts ) { - result.isBlogHome = true; - return result; - } - - return result; - } ); - - // If displaying initial suggestions just return plain results. - if ( isInitialSuggestions ) { - return results; - } - - // Here we append a faux suggestion to represent a "CREATE" option. This - // is detected in the rendering of the search results and handled as a - // special case. This is currently necessary because the suggestions - // dropdown will only appear if there are valid suggestions and - // therefore unless the create option is a suggestion it will not - // display in scenarios where there are no results returned from the - // API. In addition promoting CREATE to a first class suggestion affords - // the a11y benefits afforded by `URLInput` to all suggestions (eg: - // keyboard handling, ARIA roles...etc). - // - // Note also that the value of the `title` and `url` properties must correspond - // to the text value of the ``. This is because `title` is used - // when creating the suggestion. Similarly `url` is used when using keyboard to select - // the suggestion (the `onSubmit` handler falls-back to `url`). - return ! withCreateSuggestion - ? results - : results.concat( { - // the `id` prop is intentionally ommitted here because it - // is never exposed as part of the component's public API. - // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. - title: val, // Must match the existing ``s text value. - url: val, // Must match the existing ``s text value. - type: '__CREATE__', - } ); - }, - [ pageOnFront, pageForPosts, settings ] - ); -} - /** * React hook used to compute the block editor settings to use for the post editor. * From 17857cd5cdb72407eaedb98578b61381acb8436d Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 13:06:22 +0200 Subject: [PATCH 09/10] Move back creation result --- .../link-control/use-search-handler.js | 52 +++++++++++++++---- packages/core-data/src/fetch/index.js | 45 ++-------------- 2 files changed, 47 insertions(+), 50 deletions(-) diff --git a/packages/block-editor/src/components/link-control/use-search-handler.js b/packages/block-editor/src/components/link-control/use-search-handler.js index ea4f0472d62c9..80cef60f6329b 100644 --- a/packages/block-editor/src/components/link-control/use-search-handler.js +++ b/packages/block-editor/src/components/link-control/use-search-handler.js @@ -9,7 +9,13 @@ import { useSelect } from '@wordpress/data'; * Internal dependencies */ import isURLLike from './is-url-like'; -import { TEL_TYPE, MAILTO_TYPE, INTERNAL_TYPE, URL_TYPE } from './constants'; +import { + CREATE_TYPE, + TEL_TYPE, + MAILTO_TYPE, + INTERNAL_TYPE, + URL_TYPE, +} from './constants'; import { store as blockEditorStore } from '../../store'; import { default as settingsKeys } from '../../private-settings-keys'; @@ -59,14 +65,42 @@ export default function useSearchHandler( const handleEntitySearch = useHandleEntitySearch?.() || handleNoop; return useCallback( - ( val, { isInitialSuggestions } ) => { - return isURLLike( val ) - ? directEntryHandler( val, { isInitialSuggestions } ) - : handleEntitySearch( - val, - { ...suggestionsQuery, isInitialSuggestions }, - withCreateSuggestion - ); + async ( val, { isInitialSuggestions } ) => { + if ( isURLLike( val ) ) { + return directEntryHandler( val, { isInitialSuggestions } ); + } + + const results = await handleEntitySearch( val, suggestionsQuery ); + + // If displaying initial suggestions just return plain results. + if ( isInitialSuggestions ) { + return results; + } + + // Here we append a faux suggestion to represent a "CREATE" option. This + // is detected in the rendering of the search results and handled as a + // special case. This is currently necessary because the suggestions + // dropdown will only appear if there are valid suggestions and + // therefore unless the create option is a suggestion it will not + // display in scenarios where there are no results returned from the + // API. In addition promoting CREATE to a first class suggestion affords + // the a11y benefits afforded by `URLInput` to all suggestions (eg: + // keyboard handling, ARIA roles...etc). + // + // Note also that the value of the `title` and `url` properties must correspond + // to the text value of the ``. This is because `title` is used + // when creating the suggestion. Similarly `url` is used when using keyboard to select + // the suggestion (the `onSubmit` handler falls-back to `url`). + return ! withCreateSuggestion + ? results + : results.concat( { + // the `id` prop is intentionally ommitted here because it + // is never exposed as part of the component's public API. + // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. + title: val, // Must match the existing ``s text value. + url: val, // Must match the existing ``s text value. + type: CREATE_TYPE, + } ); }, [ directEntryHandler, diff --git a/packages/core-data/src/fetch/index.js b/packages/core-data/src/fetch/index.js index 74a7aded43a95..af75a9f5fbaf1 100644 --- a/packages/core-data/src/fetch/index.js +++ b/packages/core-data/src/fetch/index.js @@ -36,17 +36,10 @@ export function __experimentalUseLinkControlEntitySearch() { }, [] ); return useCallback( - async ( val, suggestionsQuery, withCreateSuggestion ) => { - const { isInitialSuggestions } = suggestionsQuery; - - const results = await fetchLinkSuggestions( - val, - suggestionsQuery, - settings - ); - - // Identify front page and update type to match. - results.map( ( result ) => { + async ( val, suggestionsQuery ) => { + return ( + await fetchLinkSuggestions( val, suggestionsQuery, settings ) + ).map( ( result ) => { if ( Number( result.id ) === pageOnFront ) { result.isFrontPage = true; return result; @@ -57,36 +50,6 @@ export function __experimentalUseLinkControlEntitySearch() { return result; } ); - - // If displaying initial suggestions just return plain results. - if ( isInitialSuggestions ) { - return results; - } - - // Here we append a faux suggestion to represent a "CREATE" option. This - // is detected in the rendering of the search results and handled as a - // special case. This is currently necessary because the suggestions - // dropdown will only appear if there are valid suggestions and - // therefore unless the create option is a suggestion it will not - // display in scenarios where there are no results returned from the - // API. In addition promoting CREATE to a first class suggestion affords - // the a11y benefits afforded by `URLInput` to all suggestions (eg: - // keyboard handling, ARIA roles...etc). - // - // Note also that the value of the `title` and `url` properties must correspond - // to the text value of the ``. This is because `title` is used - // when creating the suggestion. Similarly `url` is used when using keyboard to select - // the suggestion (the `onSubmit` handler falls-back to `url`). - return ! withCreateSuggestion - ? results - : results.concat( { - // the `id` prop is intentionally ommitted here because it - // is never exposed as part of the component's public API. - // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. - title: val, // Must match the existing ``s text value. - url: val, // Must match the existing ``s text value. - type: '__CREATE__', - } ); }, [ pageOnFront, pageForPosts, settings ] ); From 91dc7099b61bec6f6f7659380c1ff393f2c4ed85 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 21 Dec 2023 14:30:46 +0200 Subject: [PATCH 10/10] Restore __experimentalFetchLinkSuggestions setting --- packages/edit-widgets/src/index.js | 4 ++++ .../src/components/provider/use-block-editor-settings.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index 768b12a62c031..eb87d22fefef9 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -15,6 +15,7 @@ import { __experimentalGetCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; +import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; import { registerLegacyWidgetBlock, registerLegacyWidgetVariations, @@ -81,6 +82,9 @@ export function initializeEditor( id, settings ) { registerBlock( widgetArea ); registerWidgetGroupBlock(); + settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => + fetchLinkSuggestions( search, searchOptions, settings ); + // As we are unregistering `core/freeform` to avoid the Classic block, we must // replace it with something as the default freeform content handler. Failure to // do this will result in errors in the default block parser. 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 c3b4ba7a597c3..b457962606d57 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -6,6 +6,7 @@ import { useDispatch, useSelect } from '@wordpress/data'; import { store as coreStore, __experimentalUseLinkControlEntitySearch as useLinkControlEntitySearch, + __experimentalFetchLinkSuggestions as fetchLinkSuggestions, __experimentalFetchUrlData as fetchUrlData, } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; @@ -209,6 +210,9 @@ function useBlockEditorSettings( settings, postType, postId ) { __experimentalBlockPatterns: blockPatterns, __experimentalBlockPatternCategories: blockPatternCategories, __experimentalUserPatternCategories: userPatternCategories, + // We still need this for mobile and URLInput. + __experimentalFetchLinkSuggestions: ( search, searchOptions ) => + fetchLinkSuggestions( search, searchOptions, settings ), inserterMediaCategories, __experimentalFetchRichUrlData: fetchUrlData, // Todo: This only checks the top level post, not the post within a template or any other entity that can be edited.