From 48b7ee028c89a908c81c8c67e9135bd410307750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:15:26 +0100 Subject: [PATCH 01/12] Deconstruct item --- components/content-picker/PickedItem.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/content-picker/PickedItem.tsx b/components/content-picker/PickedItem.tsx index ab874068..dd4d00cc 100644 --- a/components/content-picker/PickedItem.tsx +++ b/components/content-picker/PickedItem.tsx @@ -168,7 +168,8 @@ interface PickedItemProps { * @returns {*} React JSX */ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { - const decodedTitle = decodeEntities(item.title); + const { title, url, description } = item; + const decodedTitle = decodeEntities(title); return ( <> @@ -176,7 +177,7 @@ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { {decodedTitle} - {item.url && {filterURLForDisplay(safeDecodeURI(item.url)) || ''}} + {url && {filterURLForDisplay(safeDecodeURI(url)) || ''}} ); }; From f32e5cfc43907c8ac17d6c096d8141be0c83f390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:16:23 +0100 Subject: [PATCH 02/12] Move query fields construction to outside items loop --- components/content-picker/SortableList.tsx | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/components/content-picker/SortableList.tsx b/components/content-picker/SortableList.tsx index 5545a841..1bb692cb 100644 --- a/components/content-picker/SortableList.tsx +++ b/components/content-picker/SortableList.tsx @@ -96,18 +96,20 @@ const SortableList: React.FC = ({ // @ts-ignore-next-line - The WordPress types are missing the hasFinishedResolution method. const { getEntityRecord, hasFinishedResolution } = select(coreStore); + let fields = ['link', 'type', 'id']; + + if (mode === 'user') { + fields.push('name'); + } else if (mode === 'post') { + fields.push('title'); + fields.push('url'); + fields.push('subtype'); + } else { + fields.push('name'); + fields.push('taxonomy'); + } + return posts.reduce<{ [key: string]: PickedItemType | null }>((acc, item) => { - const fields = ['link', 'type', 'id']; - if (mode === 'user') { - fields.push('name'); - } else if (mode === 'post') { - fields.push('title'); - fields.push('url'); - fields.push('subtype'); - } else { - fields.push('name'); - fields.push('taxonomy'); - } const getEntityRecordParameters = [ entityKind, item.type, From c0ce0d4b495a0bb0823ff4cf50e67f58699bdae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:17:01 +0100 Subject: [PATCH 03/12] Use const instead of let --- components/content-picker/SortableList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/content-picker/SortableList.tsx b/components/content-picker/SortableList.tsx index 1bb692cb..34d64b6b 100644 --- a/components/content-picker/SortableList.tsx +++ b/components/content-picker/SortableList.tsx @@ -96,7 +96,7 @@ const SortableList: React.FC = ({ // @ts-ignore-next-line - The WordPress types are missing the hasFinishedResolution method. const { getEntityRecord, hasFinishedResolution } = select(coreStore); - let fields = ['link', 'type', 'id']; + const fields = ['link', 'type', 'id']; if (mode === 'user') { fields.push('name'); From d8ee2cb5c19666e7ff38365be44f7ef5ff5e24d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:32:06 +0100 Subject: [PATCH 04/12] Add hook to filter the fields to be fetched from the API --- components/content-picker/SortableList.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/components/content-picker/SortableList.tsx b/components/content-picker/SortableList.tsx index 34d64b6b..14154d66 100644 --- a/components/content-picker/SortableList.tsx +++ b/components/content-picker/SortableList.tsx @@ -16,6 +16,7 @@ import { useCallback, useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { Post, User, store as coreStore } from '@wordpress/core-data'; +import { applyFilters } from '@wordpress/hooks'; import styled from '@emotion/styled'; import PickedItem, { PickedItemType } from './PickedItem'; import { DraggableChip } from './DraggableChip'; @@ -96,7 +97,7 @@ const SortableList: React.FC = ({ // @ts-ignore-next-line - The WordPress types are missing the hasFinishedResolution method. const { getEntityRecord, hasFinishedResolution } = select(coreStore); - const fields = ['link', 'type', 'id']; + let fields = ['link', 'type', 'id']; if (mode === 'user') { fields.push('name'); @@ -109,6 +110,15 @@ const SortableList: React.FC = ({ fields.push('taxonomy'); } + /** + * Filter the fields to be fetched from the API. + * + * @param {string[]} fields - The fields to be fetched. + * @param {ContentSearchMode} mode - The mode of the content picker. + * @returns {string[]} - The filtered fields. + */ + fields = applyFilters('tenup.contentPicker.queryFields', fields, mode) as string[]; + return posts.reduce<{ [key: string]: PickedItemType | null }>((acc, item) => { const getEntityRecordParameters = [ entityKind, From 968bae63d04bc974a2e49c10a9184094a9c53d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:32:43 +0100 Subject: [PATCH 05/12] Add hook to filter the item before it is returned --- components/content-picker/SortableList.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/components/content-picker/SortableList.tsx b/components/content-picker/SortableList.tsx index 14154d66..fe2b02f6 100644 --- a/components/content-picker/SortableList.tsx +++ b/components/content-picker/SortableList.tsx @@ -157,6 +157,19 @@ const SortableList: React.FC = ({ }; } + /** + * Filter the item before it is returned. + * + * @param {PickedItemType} newItem - The item to be returned. + * @param {Post | Term | User} result - The result from the getEntityRecord function. + * @returns {PickedItemType} - The filtered item. + */ + newItem = applyFilters( + 'tenup.contentPicker.pickedItem', + newItem, + result, + ) as Partial; + if (item.uuid) { newItem.uuid = item.uuid; } From 32ad5b07114db15811d22b5974572300fa9d57e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:34:43 +0100 Subject: [PATCH 06/12] Display item details --- components/content-picker/PickedItem.tsx | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/components/content-picker/PickedItem.tsx b/components/content-picker/PickedItem.tsx index dd4d00cc..f671b291 100644 --- a/components/content-picker/PickedItem.tsx +++ b/components/content-picker/PickedItem.tsx @@ -2,6 +2,7 @@ import styled from '@emotion/styled'; import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; +import { safeHTML } from '@wordpress/dom'; import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; import { close, chevronUp, chevronDown } from '@wordpress/icons'; @@ -21,6 +22,7 @@ export type PickedItemType = { uuid: string; title: string; url: string; + details: string; }; const PickedItemContainer = styled.div<{ isDragging?: boolean; isOrderable?: boolean }>` @@ -116,6 +118,17 @@ const ItemURL = styled.span` text-overflow: ellipsis; `; +const ItemDetails = styled('div')` + font-size: 0.75rem; + line-height: 1.4; + color: #757575; + margin-top: 4px; + + & p:first-of-type { + margin-top: 0; + } +`; + const MoveButton = styled(Button)` &.components-button.has-icon { min-width: 20px; @@ -168,7 +181,7 @@ interface PickedItemProps { * @returns {*} React JSX */ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { - const { title, url, description } = item; + const { title, url, details } = item; const decodedTitle = decodeEntities(title); return ( <> @@ -177,6 +190,13 @@ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { {decodedTitle} + {details && ( + + )} {url && {filterURLForDisplay(safeDecodeURI(url)) || ''}} ); From 766037002a1be444c2c5c10f5f4565639b89028f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:35:50 +0100 Subject: [PATCH 07/12] Rename component to ItemInfo --- components/content-picker/PickedItem.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/content-picker/PickedItem.tsx b/components/content-picker/PickedItem.tsx index f671b291..fb8ca3ab 100644 --- a/components/content-picker/PickedItem.tsx +++ b/components/content-picker/PickedItem.tsx @@ -22,7 +22,7 @@ export type PickedItemType = { uuid: string; title: string; url: string; - details: string; + info: string; }; const PickedItemContainer = styled.div<{ isDragging?: boolean; isOrderable?: boolean }>` @@ -118,7 +118,7 @@ const ItemURL = styled.span` text-overflow: ellipsis; `; -const ItemDetails = styled('div')` +const ItemInfo = styled('div')` font-size: 0.75rem; line-height: 1.4; color: #757575; @@ -181,7 +181,7 @@ interface PickedItemProps { * @returns {*} React JSX */ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { - const { title, url, details } = item; + const { title, url, info } = item; const decodedTitle = decodeEntities(title); return ( <> @@ -190,10 +190,10 @@ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { {decodedTitle} - {details && ( - )} From 51e2d8e71a706a31df4f5a3660b5b5b2ecb3f362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Fri, 23 May 2025 00:46:03 +0100 Subject: [PATCH 08/12] Rename component to SearchItemURL --- components/content-search/SearchItem.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/content-search/SearchItem.tsx b/components/content-search/SearchItem.tsx index 1ef7872e..3ef8df5a 100644 --- a/components/content-search/SearchItem.tsx +++ b/components/content-search/SearchItem.tsx @@ -42,7 +42,7 @@ const SearchItemTitle = styled.span<{ showType: boolean }>` padding-right: ${({ showType }) => (showType ? 0 : undefined)}; `; -const SearchItemInfo = styled.span<{ showType: boolean }>` +const SearchItemURL = styled.span<{ showType: boolean }>` padding-right: ${({ showType }) => (showType ? 0 : undefined)}; `; @@ -94,11 +94,11 @@ const SearchItem: React.FC = ({ - + {filterURLForDisplay(safeDecodeURI(suggestion.url)) || ''} - + {showType && {renderType(suggestion)}} From 45c24f0935bc60c9b5dc100ee5155820a6891c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Mon, 26 May 2025 19:43:22 +0100 Subject: [PATCH 09/12] Make URL optional for the picked item --- components/content-picker/PickedItem.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/content-picker/PickedItem.tsx b/components/content-picker/PickedItem.tsx index fb8ca3ab..1c24766d 100644 --- a/components/content-picker/PickedItem.tsx +++ b/components/content-picker/PickedItem.tsx @@ -21,8 +21,8 @@ export type PickedItemType = { type: string; uuid: string; title: string; - url: string; - info: string; + url?: string; + info?: string; }; const PickedItemContainer = styled.div<{ isDragging?: boolean; isOrderable?: boolean }>` From 358a7db34054ff27bba738aac2392daf9cb89471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Mon, 26 May 2025 19:47:26 +0100 Subject: [PATCH 10/12] Change to switch case for eficiency --- components/content-picker/SortableList.tsx | 55 ++++++++++++---------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/components/content-picker/SortableList.tsx b/components/content-picker/SortableList.tsx index fe2b02f6..6396012f 100644 --- a/components/content-picker/SortableList.tsx +++ b/components/content-picker/SortableList.tsx @@ -131,30 +131,37 @@ const SortableList: React.FC = ({ if (result) { let newItem: Partial; - if (mode === 'post') { - const post = result as Post; - newItem = { - title: post.title.rendered, - url: post.link, - id: post.id, - type: post.type, - }; - } else if (mode === 'user') { - const user = result as User; - newItem = { - title: user.name, - url: user.link, - id: user.id, - type: 'user', - }; - } else { - const taxonomy = result as Term; - newItem = { - title: taxonomy.name, - url: taxonomy.link, - id: taxonomy.id, - type: taxonomy.taxonomy, - }; + switch (mode) { + case 'post': { + const post = result as Post; + newItem = { + title: post.title.rendered, + url: post.link, + id: post.id, + type: post.type, + }; + break; + } + case 'user': { + const user = result as User; + newItem = { + title: user.name, + url: user.link, + id: user.id, + type: 'user', + }; + break; + } + default: { + const taxonomy = result as Term; + newItem = { + title: taxonomy.name, + url: taxonomy.link, + id: taxonomy.id, + type: taxonomy.taxonomy, + }; + break; + } } /** From f3ddd4ec728b8e0a69d69b4f6df5b005506d52eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Mon, 26 May 2025 23:36:54 +0100 Subject: [PATCH 11/12] Use DOMpurify before setting HTML --- components/content-picker/PickedItem.tsx | 13 ++++++------- package-lock.json | 18 +++++++++++++++++- package.json | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/components/content-picker/PickedItem.tsx b/components/content-picker/PickedItem.tsx index 1c24766d..8443fcbf 100644 --- a/components/content-picker/PickedItem.tsx +++ b/components/content-picker/PickedItem.tsx @@ -1,8 +1,8 @@ import styled from '@emotion/styled'; import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; +import DOMPurify from 'dompurify'; import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; -import { safeHTML } from '@wordpress/dom'; import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; import { close, chevronUp, chevronDown } from '@wordpress/icons'; @@ -118,15 +118,11 @@ const ItemURL = styled.span` text-overflow: ellipsis; `; -const ItemInfo = styled('div')` +const ItemInfo = styled.span` font-size: 0.75rem; line-height: 1.4; color: #757575; margin-top: 4px; - - & p:first-of-type { - margin-top: 0; - } `; const MoveButton = styled(Button)` @@ -193,7 +189,10 @@ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { {info && ( )} diff --git a/package-lock.json b/package-lock.json index 4196aeb8..87ded426 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@typescript-eslint/parser": "^6.21.0", "@wordpress/icons": "^10.0.0", "array-move": "^4.0.0", + "dompurify": "^3.2.6", "react-window": "^1.8.10", "uuid": "^9.0.1", "wp-types": "^3.65.0" @@ -54,7 +55,8 @@ "license": "ISC", "dependencies": { "@10up/block-components": "file:../dist/index.js", - "@wordpress/icons": "^9.48.0" + "@wordpress/icons": "^9.48.0", + "dompurify": "^3.2.6" }, "devDependencies": { "@wordpress/block-editor": "^12.19.9", @@ -5947,6 +5949,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/uglify-js": { "version": "3.17.5", "dev": true, @@ -13557,6 +13565,14 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "dev": true, diff --git a/package.json b/package.json index c3deb6d7..d8d31918 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@typescript-eslint/parser": "^6.21.0", "@wordpress/icons": "^10.0.0", "array-move": "^4.0.0", + "dompurify": "^3.2.6", "react-window": "^1.8.10", "uuid": "^9.0.1", "wp-types": "^3.65.0" From 1f6daa58e067e619a7c46d2c41404eb39ff9a044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Santos?= Date: Tue, 27 May 2025 00:25:39 +0100 Subject: [PATCH 12/12] Add hooks to filter the fields to be fetched from the search endpoint and to filter the search result before it is returned --- components/content-picker/PickedItem.tsx | 2 +- components/content-search/SearchItem.tsx | 63 ++++++++++++++++++------ components/content-search/utils.ts | 49 ++++++++++++++++-- 3 files changed, 93 insertions(+), 21 deletions(-) diff --git a/components/content-picker/PickedItem.tsx b/components/content-picker/PickedItem.tsx index 8443fcbf..349fedac 100644 --- a/components/content-picker/PickedItem.tsx +++ b/components/content-picker/PickedItem.tsx @@ -186,6 +186,7 @@ const PickedItemPreview: React.FC<{ item: PickedItemType }> = ({ item }) => { {decodedTitle} + {url && {filterURLForDisplay(safeDecodeURI(url)) || ''}} {info && ( = ({ item }) => { }} /> )} - {url && {filterURLForDisplay(safeDecodeURI(url)) || ''}} ); }; diff --git a/components/content-search/SearchItem.tsx b/components/content-search/SearchItem.tsx index 3ef8df5a..e1d440e3 100644 --- a/components/content-search/SearchItem.tsx +++ b/components/content-search/SearchItem.tsx @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; +import DOMPurify from 'dompurify'; import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; import { decodeEntities } from '@wordpress/html-entities'; import { @@ -14,10 +15,10 @@ import { NormalizedSuggestion } from './utils'; const SearchItemWrapper = styled(Button)` &&& { display: flex; + flex-direction: column; text-align: left; width: 100%; - justify-content: space-between; - align-items: center; + align-items: flex-start; border-radius: 2px; box-sizing: border-box; height: auto !important; @@ -32,6 +33,14 @@ const SearchItemWrapper = styled(Button)` } `; +const SearchItemHeaderWrapper = styled.span` + display: flex; + flex-direction: row; + width: 100%; + justify-content: space-between; + align-items: center; +`; + const SearchItemHeader = styled.span` display: flex; flex-direction: column; @@ -46,6 +55,13 @@ const SearchItemURL = styled.span<{ showType: boolean }>` padding-right: ${({ showType }) => (showType ? 0 : undefined)}; `; +const SearchItemInfo = styled.span` + font-size: 0.75rem; + line-height: 1.4; + color: #757575; + margin-top: 4px; +`; + const SearchItemType = styled.span` background-color: rgba(0, 0, 0, 0.05); color: black; @@ -81,26 +97,41 @@ const SearchItem: React.FC = ({ contentTypes, renderType = defaultRenderItemType, }) => { - const showType = !!(suggestion.type && contentTypes.length > 1); + const { type, title, url, info } = suggestion; + const showType = !!(type && contentTypes.length > 1); - const richTextContent = create({ html: suggestion.title }); + const richTextContent = create({ html: title }); const textContent = getTextContent(richTextContent); const titleContent = decodeEntities(textContent); return ( - + - - - - - - - {filterURLForDisplay(safeDecodeURI(suggestion.url)) || ''} - - - - {showType && {renderType(suggestion)}} + + + + + + {url && ( + + + {filterURLForDisplay(safeDecodeURI(url)) || ''} + + + )} + + {showType && {renderType(suggestion)}} + + {info && ( + + )} ); diff --git a/components/content-search/utils.ts b/components/content-search/utils.ts index 6f150c75..6900e74e 100644 --- a/components/content-search/utils.ts +++ b/components/content-search/utils.ts @@ -2,6 +2,7 @@ import type { WP_REST_API_User, WP_REST_API_Search_Result } from 'wp-types'; import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; +import { applyFilters } from '@wordpress/hooks'; import type { ContentSearchMode, QueryFilter } from './types'; interface IdentifiableObject extends Object { @@ -47,11 +48,28 @@ export const prepareSearchQuery = ({ }: PrepareSearchQueryArgs): string => { let searchQuery; + let fields = ['link', 'type', 'id', 'url', 'subtype']; + + if (mode === 'user') { + fields.push('name'); + } else { + fields.push('title'); + } + + /** + * Filter the fields to be fetched from the API. + * + * @param {string[]} fields - The fields to be fetched. + * @param {ContentSearchMode} mode - The mode of the content search. + * @returns {string[]} - The filtered fields. + */ + fields = applyFilters('tenup.contentSearch.queryFields', fields, mode) as string[]; + switch (mode) { case 'user': searchQuery = addQueryArgs('wp/v2/users', { search: keyword, - _fields: ['id', 'link', 'url', 'type', 'name', 'subtype'], + _fields: fields, }); break; default: @@ -62,7 +80,7 @@ export const prepareSearchQuery = ({ _embed: true, per_page: perPage, page, - _fields: ['id', 'link', 'url', 'type', 'title', 'subtype'], + _fields: fields, }); break; @@ -97,29 +115,52 @@ export const normalizeResults = ({ title: string; type: ContentSearchMode | string; url: string; + info?: string; }> => { const filteredResults = filterOutExcludedItems({ results, excludeItems }); return filteredResults.map((item) => { + let newItem: { + id: number; + subtype: ContentSearchMode | string; + title: string; + type: ContentSearchMode | string; + url: string; + info?: string; + }; + switch (mode) { case 'user': const userItem = item as WP_REST_API_User; - return { + newItem = { id: userItem.id, subtype: mode, title: userItem.name, type: mode, url: userItem.link, }; + break; default: const searchItem = item as WP_REST_API_Search_Result; - return { + newItem = { id: searchItem.id as number, subtype: searchItem.subtype, title: searchItem.title, type: searchItem.type, url: searchItem.url, }; + break; } + + /** + * Filter the new item before returning it. + * + * @param {object} newItem - The item to be returned. + * @param {object} item - The original item from the search result. + * @return {object} - The filtered item. + */ + newItem = applyFilters('tenup.contentSearch.searchResult', newItem, item) as typeof newItem; + + return newItem; }); };