mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(site): search (#165)
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
findFirstGuide,
|
||||
findGuideBySlug,
|
||||
getAllGuideSlugs,
|
||||
getSectionsForGuide,
|
||||
getValidStylesForGuide,
|
||||
} from '../sidebar';
|
||||
|
||||
@@ -254,6 +255,95 @@ describe('sidebar utilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSectionsForGuide', () => {
|
||||
it('should return empty array for top-level guide', () => {
|
||||
const result = getSectionsForGuide('guide-3', mockSidebar);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return section label for guide nested in one section', () => {
|
||||
const result = getSectionsForGuide('guide-1', mockSidebar);
|
||||
|
||||
expect(result).toEqual(['Section 1']);
|
||||
});
|
||||
|
||||
it('should return all ancestor section labels in order', () => {
|
||||
const nestedSidebar: Sidebar = [
|
||||
{
|
||||
sidebarLabel: 'Parent',
|
||||
contents: [
|
||||
{
|
||||
sidebarLabel: 'Child',
|
||||
contents: [mockGuide1],
|
||||
},
|
||||
mockGuide2,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = getSectionsForGuide('guide-1', nestedSidebar);
|
||||
expect(result).toEqual(['Parent', 'Child']);
|
||||
|
||||
const result2 = getSectionsForGuide('guide-2', nestedSidebar);
|
||||
expect(result2).toEqual(['Parent']);
|
||||
});
|
||||
|
||||
it('should return empty array if guide is not found', () => {
|
||||
const result = getSectionsForGuide('non-existent', mockSidebar);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle deeply nested guides', () => {
|
||||
const deeplyNestedSidebar: Sidebar = [
|
||||
{
|
||||
sidebarLabel: 'Level 1',
|
||||
contents: [
|
||||
{
|
||||
sidebarLabel: 'Level 2',
|
||||
contents: [
|
||||
{
|
||||
sidebarLabel: 'Level 3',
|
||||
contents: [mockGuide1],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = getSectionsForGuide('guide-1', deeplyNestedSidebar);
|
||||
expect(result).toEqual(['Level 1', 'Level 2', 'Level 3']);
|
||||
});
|
||||
|
||||
it('should return empty array for empty sidebar', () => {
|
||||
const result = getSectionsForGuide('guide-1', []);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should work with sidebar containing mixed top-level and nested guides', () => {
|
||||
const mixedSidebar: Sidebar = [
|
||||
mockGuide3, // top level
|
||||
{
|
||||
sidebarLabel: 'Getting Started',
|
||||
contents: [
|
||||
mockGuide1,
|
||||
{
|
||||
sidebarLabel: 'Advanced',
|
||||
contents: [mockGuide2],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
expect(getSectionsForGuide('guide-3', mixedSidebar)).toEqual([]);
|
||||
expect(getSectionsForGuide('guide-1', mixedSidebar)).toEqual(['Getting Started']);
|
||||
expect(getSectionsForGuide('guide-2', mixedSidebar)).toEqual(['Getting Started', 'Advanced']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getValidStylesForGuide', () => {
|
||||
it('should return framework styles that guide supports', () => {
|
||||
const result = getValidStylesForGuide(mockGuide1, 'html');
|
||||
|
||||
@@ -32,6 +32,25 @@ export function getPreferencesServer(cookies: AstroCookies): Preference {
|
||||
* Client-side API: Works with document.cookie
|
||||
*/
|
||||
|
||||
export function getPreferenceClient(): Preference {
|
||||
if (typeof document === 'undefined') return { framework: null, style: null };
|
||||
|
||||
const cookies = document.cookie.split(';').reduce((acc, cookie) => {
|
||||
const [key, value] = cookie.trim().split('=');
|
||||
if (key) acc[key] = value;
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
const framework = cookies[FRAMEWORK_COOKIE] && isValidFramework(cookies[FRAMEWORK_COOKIE])
|
||||
? cookies[FRAMEWORK_COOKIE]
|
||||
: null;
|
||||
const style = framework && cookies[STYLE_COOKIE] && isValidStyleForFramework(framework, cookies[STYLE_COOKIE])
|
||||
? cookies[STYLE_COOKIE]
|
||||
: null;
|
||||
|
||||
return { framework, style } as Preference;
|
||||
}
|
||||
|
||||
export function setPreferenceClient<T extends SupportedFramework>(framework: T, style: SupportedStyle<T>) {
|
||||
if (typeof document === 'undefined') return;
|
||||
if (!isValidFramework(framework)) throw new Error(`Invalid framework: ${framework}`);
|
||||
|
||||
@@ -158,6 +158,34 @@ export function findGuideBySlug(slug: string, sidebarToSearch: Sidebar = sidebar
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ancestor section labels for a guide by its slug.
|
||||
* Returns an array of sidebarLabel strings for all ancestor sections,
|
||||
* in order from outermost to innermost. If the guide has no ancestors
|
||||
* (i.e., it's at the top level) or if the guide is not found, returns an empty array.
|
||||
*
|
||||
* @param slug - The slug of the guide to find
|
||||
* @param sidebarToSearch - Optional sidebar to search (defaults to main sidebar config)
|
||||
* @returns An array of ancestor section labels, or empty array if none or guide not found
|
||||
*/
|
||||
export function getSectionsForGuide(slug: string, sidebarToSearch: Sidebar = sidebar): string[] {
|
||||
function findInSidebar(items: Sidebar, path: string[]): string[] | null {
|
||||
for (const item of items) {
|
||||
if (isSection(item)) {
|
||||
// Recursively search section contents with updated path
|
||||
const result = findInSidebar(item.contents, [...path, item.sidebarLabel]);
|
||||
if (result !== null) return result;
|
||||
} else if (item.slug === slug) {
|
||||
// Found the guide, return the accumulated path
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return findInSidebar(sidebarToSearch, []) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get valid styles for a guide, optionally filtered by framework.
|
||||
*
|
||||
|
||||
@@ -25,6 +25,7 @@ export function createTechArticleSchema(params: {
|
||||
updatedDate?: Date;
|
||||
wordCount?: number;
|
||||
readingTime?: number;
|
||||
articleSection?: string;
|
||||
}): WithContext<TechArticle> {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
@@ -35,6 +36,7 @@ export function createTechArticleSchema(params: {
|
||||
...(params.updatedDate && { dateModified: params.updatedDate.toISOString() }),
|
||||
...(params.wordCount && { wordCount: params.wordCount }),
|
||||
...(params.readingTime && { timeRequired: `PT${params.readingTime}M` }),
|
||||
...(params.articleSection && { articleSection: params.articleSection }),
|
||||
author: {
|
||||
'@type': 'Organization',
|
||||
name: 'Video.js',
|
||||
|
||||
Reference in New Issue
Block a user