Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/constants/algolia.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
// setup (src/theme/SearchBar/algoliaInsights.ts, src/pages/search.tsx).
module.exports = {
ALGOLIA_APP_ID: 'T5D6KNJCQS',
ALGOLIA_SEARCH_API_KEY: '4a2fa646f476d7756a7cdc599b625bec',
ALGOLIA_INDEX_NAME: 'temporal',
ALGOLIA_SEARCH_API_KEY: '7f9927fa05ed55464439db9097050857',
ALGOLIA_INDEX_NAME: 'temporal-search-experiment',
};
136 changes: 88 additions & 48 deletions src/theme/SearchBar/GroupedHits.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { useHits } from 'react-instantsearch';
import { Hit } from './Hit';
import { SDK_LANGUAGES } from './SDKLanguageFilter';

interface GroupedHitsProps {
selectedIndex: number;
Expand All @@ -9,6 +10,13 @@ interface GroupedHitsProps {

export function GroupedHits({ selectedIndex, onNavigate }: GroupedHitsProps) {
const { items, sendEvent } = useHits();
const titleCounts = items.reduce((counts: Map<string, number>, hit: any) => {
const title = hit.hierarchy?.[hit.type] || hit.hierarchy?.lvl1;
if (title) {
counts.set(title, (counts.get(title) || 0) + 1);
}
return counts;
}, new Map<string, number>());

// Group hits by their top-level category (hierarchy.lvl0)
const groupedByCategory = items.reduce((acc: any, hit: any) => {
Expand Down Expand Up @@ -47,61 +55,93 @@ export function GroupedHits({ selectedIndex, onNavigate }: GroupedHitsProps) {
return acc;
}, {});

const pageUrls = Object.keys(hitsByPage);
const pageGroups = Object.entries(hitsByPage).map(([pageUrl, { page, anchors }]: [string, any]) => ({
pageUrl,
page,
anchors,
sdkLanguage: page?.sdk_language || anchors[0]?.sdk_language,
}));
const languageGroups = pageGroups.reduce((groups: Map<string | null, any[]>, pageGroup) => {
const language = pageGroup.sdkLanguage || null;
groups.set(language, [...(groups.get(language) || []), pageGroup]);
return groups;
}, new Map<string | null, any[]>());
const languageLabel = (language: string) => SDK_LANGUAGES.find((sdk) => sdk.id === language)?.label || language;

return (
<div key={category} className="custom-search-section">
<div className="custom-search-section-header">
{category}
</div>
<div className="custom-search-section-header">{category}</div>
<div className="custom-search-section-hits">
{pageUrls.map((pageUrl) => {
const { page, anchors } = hitsByPage[pageUrl];
const pageTitle = page?.hierarchy?.lvl1 || anchors[0]?.hierarchy?.lvl1 || 'Untitled';
{[null, ...[...languageGroups.keys()].filter(Boolean)].map((language) => {
const pages = languageGroups.get(language) || [];

// If there's no page hit but there are anchors, treat the first anchor as the "page"
const hasPageHit = !!page;
const firstAnchorAsPage = !hasPageHit && anchors.length > 0 ? anchors[0] : null;
const remainingAnchors = firstAnchorAsPage ? anchors.slice(1) : anchors;
if (pages.length === 0) {
return null;
}

return (
<div key={pageUrl} className="custom-search-page-group">
{/* Render page hit if it exists */}
{page && (
<Hit
key={page.objectID}
hit={page}
isSelected={hitIndex++ === selectedIndex}
onNavigate={onNavigate}
isAnchor={false}
sendEvent={sendEvent}
/>
)}
{/* If no page hit, render first anchor as page-level item */}
{firstAnchorAsPage && (
<Hit
key={firstAnchorAsPage.objectID}
hit={firstAnchorAsPage}
isSelected={hitIndex++ === selectedIndex}
onNavigate={onNavigate}
isAnchor={false}
sendEvent={sendEvent}
/>
)}
{/* Render remaining anchor hits as tree children */}
{remainingAnchors.map((anchor: any, anchorIndex: number) => (
<Hit
key={anchor.objectID}
hit={anchor}
isSelected={hitIndex++ === selectedIndex}
onNavigate={onNavigate}
isAnchor={true}
isLastAnchor={anchorIndex === remainingAnchors.length - 1}
parentTitle={pageTitle}
sendEvent={sendEvent}
/>
))}
</div>
<React.Fragment key={language || 'general'}>
{pages.map(({ pageUrl, page, anchors }: any) => {
const pageTitle = page?.hierarchy?.lvl1 || anchors[0]?.hierarchy?.lvl1 || 'Untitled';

// If there's no page hit but there are anchors, treat the first anchor as the "page"
const hasPageHit = !!page;
const firstAnchorAsPage = !hasPageHit && anchors.length > 0 ? anchors[0] : null;
const remainingAnchors = firstAnchorAsPage ? anchors.slice(1) : anchors;

return (
<div key={pageUrl} className="custom-search-page-group">
{page && (
<Hit
key={page.objectID}
hit={page}
isSelected={hitIndex++ === selectedIndex}
onNavigate={onNavigate}
isAnchor={false}
languageLabel={language ? languageLabel(language) : undefined}
showSinglePath={
(titleCounts.get(page.hierarchy?.[page.type] || page.hierarchy?.lvl1) || 0) > 1
}
sendEvent={sendEvent}
/>
)}
{firstAnchorAsPage && (
<Hit
key={firstAnchorAsPage.objectID}
hit={firstAnchorAsPage}
isSelected={hitIndex++ === selectedIndex}
onNavigate={onNavigate}
isAnchor={false}
languageLabel={language ? languageLabel(language) : undefined}
showSinglePath={
(titleCounts.get(
firstAnchorAsPage.hierarchy?.[firstAnchorAsPage.type] ||
firstAnchorAsPage.hierarchy?.lvl1
) || 0) > 1
}
sendEvent={sendEvent}
/>
)}
{remainingAnchors.map((anchor: any, anchorIndex: number) => (
<Hit
key={anchor.objectID}
hit={anchor}
isSelected={hitIndex++ === selectedIndex}
onNavigate={onNavigate}
isAnchor={true}
isLastAnchor={anchorIndex === remainingAnchors.length - 1}
parentTitle={pageTitle}
languageLabel={language ? languageLabel(language) : undefined}
showSinglePath={
(titleCounts.get(anchor.hierarchy?.[anchor.type] || anchor.hierarchy?.lvl1) || 0) > 1
}
sendEvent={sendEvent}
/>
))}
</div>
);
})}
</React.Fragment>
);
})}
</div>
Expand Down
51 changes: 43 additions & 8 deletions src/theme/SearchBar/Hit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface HitProps {
isAnchor?: boolean;
isLastAnchor?: boolean;
parentTitle?: string;
languageLabel?: string;
showSinglePath?: boolean;
sendEvent: (eventType: string, hit: any, eventName: string) => void;
}

Expand All @@ -30,11 +32,45 @@ function getHierarchyAttribute(hit: any): string {
return 'hierarchy.lvl1';
}

export function Hit({ hit, isSelected, onNavigate, isAnchor, isLastAnchor, parentTitle, sendEvent }: HitProps) {
function getHierarchyPath(
hit: any,
hierarchyAttribute: string,
parentTitle?: string,
showSinglePath = false
): string[] {
const currentLevel = hierarchyAttribute.replace('hierarchy.', '');
const resultTitle = hit.hierarchy?.[currentLevel];
const levels = ['lvl0', 'lvl1', 'lvl2', 'lvl3', 'lvl4', 'lvl5', 'lvl6'];
const path = levels
.slice(0, levels.indexOf(currentLevel))
.map((level) => hit.hierarchy?.[level])
.filter((value) => value && value !== resultTitle);

if (path.length > 1 || showSinglePath) {
return path;
}

return parentTitle && parentTitle !== resultTitle ? [parentTitle] : [];
}

export function Hit({
hit,
isSelected,
onNavigate,
isAnchor,
isLastAnchor,
parentTitle,
languageLabel,
showSinglePath,
sendEvent,
}: HitProps) {
const history = useHistory();
const hitRef = useRef<HTMLAnchorElement>(null);

const hierarchyAttribute = getHierarchyAttribute(hit);
const hierarchyPath = getHierarchyPath(hit, hierarchyAttribute, parentTitle, showSinglePath);
const resultTitle = hit.hierarchy?.[hierarchyAttribute.replace('hierarchy.', '')] || '';
const showLanguageLabel = languageLabel && !resultTitle.toLowerCase().includes(languageLabel.toLowerCase());

const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
Expand Down Expand Up @@ -68,7 +104,9 @@ export function Hit({ hit, isSelected, onNavigate, isAnchor, isLastAnchor, paren
'custom-search-hit',
isSelected ? 'custom-search-hit--selected' : '',
isAnchor ? 'custom-search-hit--anchor' : 'custom-search-hit--page',
].filter(Boolean).join(' ');
]
.filter(Boolean)
.join(' ');

return (
<a
Expand All @@ -93,13 +131,10 @@ export function Hit({ hit, isSelected, onNavigate, isAnchor, isLastAnchor, paren
<div className="custom-search-hit-content">
<div className="custom-search-hit-title">
<Highlight attribute={hierarchyAttribute} hit={hit} />
{showLanguageLabel && <span className="custom-search-hit-language"> - {languageLabel} SDK</span>}
</div>
{isAnchor && parentTitle && (
<div className="custom-search-hit-path">
{parentTitle}
</div>
)}
{!isAnchor && hit.content && (
{hierarchyPath.length > 0 && <div className="custom-search-hit-path">{hierarchyPath.join(' > ')}</div>}
{hit.content && (
<div className="custom-search-hit-text">
<Snippet attribute="content" hit={hit} />
</div>
Expand Down
13 changes: 11 additions & 2 deletions src/theme/SearchBar/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@
color: var(--temporal-dark);
}

.custom-search-hit-language {
color: var(--temporal-text-muted);
font-weight: 400;
}

.custom-search-hit-path {
font-size: 12px;
color: var(--temporal-text-muted);
Expand Down Expand Up @@ -505,7 +510,9 @@
.custom-search-language-filter-content {
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows 0.25s ease, opacity 0.2s ease;
transition:
grid-template-rows 0.25s ease,
opacity 0.2s ease;
opacity: 1;
}

Expand Down Expand Up @@ -745,7 +752,9 @@
color: var(--ifm-font-color-base);
outline: none;
font-family: inherit;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
transition:
border-color 0.2s ease,
box-shadow 0.2s ease;
}

.search-page-input:focus {
Expand Down