Skip to content

Commit

Permalink
Merge pull request #469 from Sitecore/feature/search-improvements
Browse files Browse the repository at this point in the history
Search improvements
  • Loading branch information
markvanaalst authored Jun 6, 2023
2 parents 1982e83 + 85dbc12 commit 51a84b3
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { GetProductLogo } from '@/../../packages/ui/common/assets';
import { truncateString } from '@/../../packages/ui/common/text-util';
import { toClass } from '@/../../packages/ui/common/text-util';
import { ActionPropPayload, ItemIndexActionPayload, PreviewSearchSuggestionQuery, SearchResponseSuggestion, WidgetAction, WidgetDataType, usePreviewSearch, widget } from '@sitecore-search/react';
import { ArticleCard, NavMenu, Presence } from '@sitecore-search/ui';
import type { PreviewSearchActionProps } from '@sitecore-search/widgets';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { SyntheticEvent, useCallback, useState } from 'react';
import Loader from './Loader';
import { Loading } from 'ui/components/common/Loading';

type ArticleModel = {
id: string;
Expand All @@ -20,15 +20,15 @@ type ArticleModel = {
site_name: string;
highlight: {
description: string;
}
};
};

const Articles = ({ loading = false, articles, onItemClick, suggestionsReturned }: { loading?: boolean; articles: Array<ArticleModel>; onItemClick: PreviewSearchActionProps['onItemClick']; suggestionsReturned?: boolean}) => (
<NavMenu.Content className={'bg-theme-bg text-theme-text border-theme-border absolute right-0 top-0 inline-block overflow-y-auto border-b border-r ' + (suggestionsReturned ? ' w-4/5 h-full' : ' w-5/5')}>
const Articles = ({ loading = false, articles, onItemClick, suggestionsReturned }: { loading?: boolean; articles: Array<ArticleModel>; onItemClick: PreviewSearchActionProps['onItemClick']; suggestionsReturned?: boolean }) => (
<NavMenu.Content className={'bg-theme-bg text-theme-text absolute right-0 top-0 hidden overflow-hidden overflow-y-auto md:inline-block ' + (suggestionsReturned ? 'h-full w-4/5' : ' w-5/5')}>
<Presence present={loading}>
<Loader />
<Loading />
</Presence>
<NavMenu.List className="mr-0 grid list-none grid-cols-3 gap-0 p-2">
<NavMenu.List className="mr-0 grid list-none gap-0 p-2 md:grid-cols-3">
{!loading &&
articles.map((article, index) => (
<NavMenu.Item key={`${article.id}@${article.source_id}`} className="mx-2 my-4 inline">
Expand All @@ -41,11 +41,11 @@ const Articles = ({ loading = false, articles, onItemClick, suggestionsReturned
window.open(article.url, '_blank');
}}
>
<ArticleCard.Root className="bg-theme-bg grid grid-cols-4 items-center">
<ArticleCard.Root className="grid items-center sm:grid-cols-1 md:grid-cols-4">
<div className={`${article.type == 'Video' ? 'col-span-3' : 'col-span-4'} pr-2`}>
<ArticleCard.Title className="text-base font-bold group-hover:underline">{article.name}</ArticleCard.Title>
<ArticleCard.Title className="line-clamp-2 text-base font-bold group-hover:underline">{article.name}</ArticleCard.Title>
<div className="my-2">
{article.type && <span className="bg-primary-500 text-2xs px-2.5 py-1 uppercase text-white dark:bg-teal-500">{article.type}</span>}
{article.type && <span className={`result-type-${toClass(article.type)} bg-primary-500 text-2xs px-2.5 py-1 uppercase text-white dark:bg-teal-500`}>{article.type}</span>}
{article.index_name && <span className="text-2xs mr-2 w-full px-2.5 py-1 uppercase">{article.site_name}</span>}
</div>
{article.type == 'Video' && (
Expand All @@ -54,8 +54,8 @@ const Articles = ({ loading = false, articles, onItemClick, suggestionsReturned
{!article.image_url && <Image width={256} height={144} src="/images/social/social-card-default.jpeg" alt={article.index_name} className="object-scale-down" />}
</div>
)}
{article.type != 'Video' && article?.highlight?.description && <p className="text-xs" dangerouslySetInnerHTML={{ __html: truncateString(article.highlight.description, 300, true) }} />}
{article.type != 'Video' && !article.highlight && article.description && <p className="text-xs">{truncateString(article.description, 300, true)}</p>}
{article.type != 'Video' && article?.highlight?.description && <p className="line-clamp-5 text-xs" dangerouslySetInnerHTML={{ __html: article.highlight.description }} />}
{article.type != 'Video' && !article.highlight && article.description && <p className="line-clamp-5 text-xs">{article.description}</p>}
</div>
</ArticleCard.Root>
</NavMenu.Link>
Expand All @@ -78,6 +78,7 @@ const Group = ({
activeItem,
onActiveItem,
onItemClick,
onGroupTitleClick,
}: {
groupTitle: string;
groupId: string;
Expand All @@ -86,22 +87,25 @@ const Group = ({
activeItem: string;
onActiveItem: (arg: string) => void;
onItemClick: (payload: ActionPropPayload<SearchItemClickedAction>) => void;
onGroupTitleClick: (arg: string) => void;
}) => {
return (
<div className="bg-primary-100 border-theme-border h-96 w-1/5 border-b border-l pt-2 dark:bg-teal-900">
<h2 className="m-4 box-border text-left font-semibold uppercase">{groupTitle}</h2>
<div className="md:bg-primary-100 sm:bg-theme-bg border-theme-border sm:1/3 border-b border-l pt-2 dark:bg-teal-900 md:w-1/5">
<h2 className="ml-4 mt-2 box-border text-left font-semibold uppercase">{groupTitle}</h2>
{articles.map(({ text }) => (
<NavMenu.Item value={getGroupId(groupId, text)} key={text} className=" hover:text-primary-900 overflow-hidden pl-4 hover:bg-white dark:bg-teal-900 dark:hover:bg-teal-700 dark:hover:text-white">
<NavMenu.Item value={getGroupId(groupId, text)} key={text} className="hover:text-primary-900 overflow-hidden pl-4 text-xs hover:bg-white dark:bg-teal-900 dark:hover:bg-teal-700 dark:hover:text-white">
<NavMenu.Trigger
className="relative inline-block py-1 text-left"
className=" py-1 text-left"
onMouseOver={(e) => {
const target = e.target as HTMLLinkElement;
target.focus();
}}
onFocus={() => onActiveItem(getGroupId(groupId, text))}
onClick={() => onGroupTitleClick(text)}
>
{text}
</NavMenu.Trigger>

<PreviewSearchSuggestionQuery<ArticleModel> active={activeItem === getGroupId(groupId, text)} value={text} filterAttribute={filterAttribute}>
{({ queryResult: { isFetching, data: { content: articles = [] } = {} } }) => <Articles loading={isFetching} articles={articles} onItemClick={onItemClick} suggestionsReturned={true} />}
</PreviewSearchSuggestionQuery>
Expand All @@ -116,32 +120,24 @@ const getGroupId = (name: string, value: string) => `${name}@${value}`;
const PreviewSearchInput = ({ defaultProductsPerPage = 6 }) => {
const router = useRouter();
const indexSources = process.env.NEXT_PUBLIC_SEARCH_SOURCES?.split(',') || [];
const { q } = router.query
const { q } = router.query;
const {
context: { keyphrase = q || '' },
actions: { onItemClick, onKeyphraseChange },
queryResult: {
isFetching,
isLoading,
data: {
content: articles = [],
suggestion: {
name_suggester: articleSuggestions = [],
} = {},
} = {},
},
queryResult: { isFetching, isLoading, data: { content: articles = [], suggestion: { name_suggester: articleSuggestions = [] } = {} } = {} },
} = usePreviewSearch<ArticleModel>((query) => {
query.getRequest().setSearchQueryHighlight({
fields: ['description'],
fragment_size: 100,
pre_tag: '<strong>',
post_tag: '</strong>',
}).setSources(indexSources);

query
.getRequest()
.setSearchQueryHighlight({
fields: ['description'],
fragment_size: 100,
pre_tag: '<strong>',
post_tag: '</strong>',
})
.setSources(indexSources);

return {
suggestionsList: [
{ suggestion: 'name_suggester', max: 10 },
],
suggestionsList: [{ suggestion: 'name_suggester', max: 10 }],
itemsPerPage: defaultProductsPerPage,
};
});
Expand Down Expand Up @@ -172,6 +168,11 @@ const PreviewSearchInput = ({ defaultProductsPerPage = 6 }) => {
router.push('/search?q=' + encodeURIComponent(target.value)).then(() => router.reload());
};

function onGroupTitleClick(arg: string): void {
setValue('');
router.push('/search?q=' + encodeURIComponent(arg)).then(() => router.reload());
}

return (
<NavMenu.Root onValueChange={onValueChange} value={value}>
<NavMenu.List>
Expand Down Expand Up @@ -208,15 +209,17 @@ const PreviewSearchInput = ({ defaultProductsPerPage = 6 }) => {
</div>
</form>

<NavMenu.Content className="bg-theme-bg text-theme-text absolute left-0 top-10 inline-block h-fit w-full justify-center pt-0 shadow-md">
<NavMenu.Content className="bg-theme-bg text-theme-text border-theme-border relative left-0 inline-block w-full justify-center border-b border-r pt-0 shadow-md">
<Presence present={loading}>
<Loader />
<Loading />
</Presence>
{!loading && (
<NavMenu.SubContent orientation="vertical" value={activeItem} className="box-border block w-full">
<NavMenu.List className="w-full">
{articleSuggestions.length > 0 && <Group groupTitle="Suggested Terms" groupId="keyphrase" articles={articleSuggestions} onItemClick={onItemClick} activeItem={activeItem} onActiveItem={setActiveItem} />}
<NavMenu.Item value="defaultArticlesResults" key="defaultArticlesResults" className="b-0 bg-none">
<NavMenu.List className="">
{articleSuggestions.length > 0 && (
<Group groupTitle="Suggested Terms" groupId="keyphrase" articles={articleSuggestions} onItemClick={onItemClick} onGroupTitleClick={onGroupTitleClick} activeItem={activeItem} onActiveItem={setActiveItem} />
)}
<NavMenu.Item value="defaultArticlesResults" key="defaultArticlesResults" className="b-0 bg-none">
<NavMenu.Trigger aria-hidden className="hidden" />
<Articles articles={articles} onItemClick={onItemClick} suggestionsReturned={articleSuggestions.length > 0} />
</NavMenu.Item>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Search results page
*/

.result-type-base {
@apply text-white;
}

.result-type-article {
@apply bg-primary-500 dark:bg-primary-900 result-type-base;
}
.result-type-forum {
@apply result-type-base bg-teal-500 dark:bg-teal-500;
}
.result-type-repository {
@apply result-type-base bg-gray-500;
}
.result-type-video {
@apply result-type-base bg-red-500;
}
.result-type-changelog {
@apply result-type-base bg-orange-500;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useSearchResults, widget, WidgetDataType } from '@sitecore-search/react
import { WidgetComponentProps } from '@sitecore-search/react/types';
import Image from 'next/image';
import { ComponentType } from 'react';
import { truncateString } from 'ui/common/text-util';
import Loader from './Loader';
import { toClass, truncateString } from 'ui/common/text-util';
import { Loading } from 'ui/components/common/Loading';
import QuerySummary from './QuerySummary';
import SearchFacets from './SearchFacets';
import SearchPagination from './SearchPagination';
Expand All @@ -24,12 +24,15 @@ export const SearchResults = (props: SearchResultsType) => {
context: { page = currentPage, itemsPerPage = initialArticlesPerPage, sortType = defaultSortType },
queryResult: { isLoading, data: { sort: { choices: sortChoices = [] } = {}, total_item: totalItems = 0, content: articles = [], facet: facets = [] } = {} },
} = useSearchResults((query) => {
query.getRequest().setSearchQueryHighlight({
fields: ['description'],
fragment_size: 100,
pre_tag: '<strong>',
post_tag: '</strong>',
}).setSources(indexSources);
query
.getRequest()
.setSearchQueryHighlight({
fields: ['description'],
fragment_size: 100,
pre_tag: '<strong>',
post_tag: '</strong>',
})
.setSources(indexSources);

return {
itemsPerPage: initialArticlesPerPage,
Expand All @@ -43,12 +46,12 @@ export const SearchResults = (props: SearchResultsType) => {
<>
{isLoading && (
<div className="pt-10">
<Loader />
<Loading />
</div>
)}
{!isLoading && (
<div className="mt-6 grid gap-6 md:grid-cols-3">
{articles.length > 0 &&
{articles.length > 0 && (
<>
<div className="md:col-span-1">
<SearchFacets onFacetClick={onFacetClick} facets={facets} />
Expand All @@ -63,44 +66,48 @@ export const SearchResults = (props: SearchResultsType) => {
</div>

<ul className="border-theme-border mt-2 border-t">
{articles.length > 0 && articles.map((result, index) => (
<li key={index} className="mt-2 py-4">
<a href={result.url} className="group" onClick={(e) => {
{articles.length > 0 &&
articles.map((result, index) => (
<li key={index} className="mt-2 py-4">
<a
href={result.url}
className="group"
onClick={(e) => {
e.preventDefault();
onItemClick({ id: result.id || '', index });
window.open(result.url, '_blank');
}}>
<div className="bg-theme-bg grid grid-cols-4 items-center md:flex-row">
<div className={`${result.type == 'Video' ? 'col-span-3' : 'col-span-4'} pr-2`}>
{result.type && <span className="bg-primary-500 text-2xs px-2.5 py-1 uppercase text-white dark:bg-teal-500">{result.type}</span>}
{result.index_name && <span className="text-2xs mr-2 px-2.5 py-1 uppercase">{result.site_name}</span>}
<h3 className="mt-2 text-base font-bold group-hover:underline">{result.name}</h3>
}}
>
<div className="bg-theme-bg grid grid-cols-4 items-center md:flex-row">
<div className={`${result.type == 'Video' ? 'col-span-3' : 'col-span-4'} pr-2`}>
{result.type && <span className={`result-type-${toClass(result.type)} text-2xs px-2.5 py-1 uppercase`}>{result.type}</span>}
{result.index_name && <span className="text-2xs mr-2 px-2.5 py-1 uppercase">{result.site_name}</span>}
<h3 className="mt-2 text-base font-bold group-hover:underline">{result.name}</h3>

{result?.highlight?.description && <p className="text-sm" dangerouslySetInnerHTML={{ __html: truncateString(result.highlight.description, 300, true) }} />}
{!result.highlight && result.description && <p className="text-sm">{truncateString(result.description, 300, true)}</p>}
</div>
{result.type == 'Video' && (
<div className="col-span-1">
{result.image_url && <Image width={256} height={144} src={result.image_url} alt={result.index_name} className="mt-20 object-scale-down" />}
{!result.image_url && <Image width={256} height={144} src="/images/social/social-card-default.jpeg" alt={result.index_name} className="mt-20 object-scale-down" />}
{result?.highlight?.description && <p className="text-sm" dangerouslySetInnerHTML={{ __html: truncateString(result.highlight.description, 300, true) }} />}
{!result.highlight && result.description && <p className="text-sm">{truncateString(result.description, 300, true)}</p>}
</div>
)}
</div>
<span className="text-violet mt-1 block break-words text-xs italic dark:text-white">{result.url}</span>
</a>
</li>
))}

{result.type == 'Video' && (
<div className="col-span-1">
{result.image_url && <Image width={256} height={144} src={result.image_url} alt={result.index_name} className="mt-20 object-scale-down" />}
{!result.image_url && <Image width={256} height={144} src="/images/social/social-card-default.jpeg" alt={result.index_name} className="mt-20 object-scale-down" />}
</div>
)}
</div>
<span className="text-violet mt-1 block break-words text-xs italic dark:text-white">{result.url}</span>
</a>
</li>
))}
</ul>

<SearchPagination defaultCurrentPage={1} onPageNumberChange={(v) => onPageNumberChange({ page: v })} page={page} pageSize={itemsPerPage} totalItems={totalItems} />
</div>
</>
}
{articles.length === 0 &&
<p className="md:col-span-3">Your search terms did not return any results, please use the input above to try again.</p>
}
)}
{articles.length === 0 && <p className="md:col-span-3">Your search terms did not return any results, please use the input above to try again.</p>}
</div>
)}
)}
</>
);
};
Expand Down
1 change: 1 addition & 0 deletions apps/devportal/src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
@import 'ui/styles/global.css';

@import '../components/changelog/Changelog.styles.css';
@import '../components/integrations/sitecore-search/Search.styles.css';
8 changes: 8 additions & 0 deletions packages/ui/common/text-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ export function truncateString(str: string, maxLength: number, appendMoreIndiati

return returnValue;
}

export function toClass(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-.]/g, '') // remove non-alphanumeric characters except the period
.replace(/[\s_-]+/g, '-') // replace spaces, underscores, or hyphens with a single hyphen
.trim();
}

1 comment on commit 51a84b3

@vercel
Copy link

@vercel vercel bot commented on 51a84b3 Jun 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.