Skip to content

Commit e0b878b

Browse files
rseseCopilot
andauthored
carousel component fixes 🎠 (#58285)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 7914ebe commit e0b878b

File tree

2 files changed

+41
-110
lines changed

2 files changed

+41
-110
lines changed

‎src/frame/middleware/resolve-recommended.ts‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,17 @@ async function resolveRecommended(
105105
const page = req.context?.page
106106
const rawRecommended = (page as any)?.rawRecommended
107107
const spotlight = (page as any)?.spotlight
108-
109-
// Collect article paths from both rawRecommended and spotlight
110-
const articlePaths: string[] = []
108+
// Collect article paths from rawRecommended or spotlight if there are no
109+
// recommended articles
110+
let articlePaths: string[] = []
111111

112112
// Add paths from rawRecommended
113113
if (rawRecommended && Array.isArray(rawRecommended)) {
114114
articlePaths.push(...rawRecommended)
115115
}
116116

117-
// Add paths from spotlight (legacy field)
118-
if (spotlight && Array.isArray(spotlight)) {
117+
// Add paths from spotlight (legacy field) if no recommended articles
118+
if (articlePaths.length === 0 && spotlight && Array.isArray(spotlight)) {
119119
const spotlightPaths = spotlight
120120
.filter((item: any) => item && typeof item.article === 'string')
121121
.map((item: any) => item.article)
@@ -126,6 +126,8 @@ async function resolveRecommended(
126126
return next()
127127
}
128128

129+
// remove duplicate articles
130+
articlePaths = [...new Set(articlePaths)]
129131
const resolved: ResolvedArticle[] = []
130132

131133
for (const rawPath of articlePaths) {

‎src/frame/tests/resolve-recommended.test.ts‎

Lines changed: 34 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -80,31 +80,10 @@ describe('resolveRecommended middleware', () => {
8080

8181
test('should resolve recommended articles when they exist', async () => {
8282
const testPage: Partial<import('@/types').Page> = {
83-
mtime: Date.now(),
8483
title: 'Test Article',
85-
rawTitle: 'Test Article',
8684
intro: 'Test intro',
87-
rawIntro: 'Test intro',
8885
relativePath: 'copilot/tutorials/article.md',
89-
fullPath: '/full/path/copilot/tutorials/article.md',
90-
languageCode: 'en',
91-
documentType: 'article',
92-
markdown: 'Test content',
93-
versions: {},
9486
applicableVersions: ['free-pro-team@latest'],
95-
permalinks: [
96-
{
97-
languageCode: 'en',
98-
pageVersion: 'free-pro-team@latest',
99-
title: 'Test Article',
100-
href: '/en/copilot/tutorials/article',
101-
hrefWithoutLanguage: '/copilot/tutorials/article',
102-
},
103-
],
104-
renderProp: vi.fn().mockResolvedValue('rendered'),
105-
renderTitle: vi.fn().mockResolvedValue('Test Article'),
106-
render: vi.fn().mockResolvedValue('rendered content'),
107-
buildRedirects: vi.fn().mockReturnValue({}),
10887
}
10988

11089
mockFindPage.mockReturnValue(testPage as any)
@@ -129,6 +108,40 @@ describe('resolveRecommended middleware', () => {
129108
expect(mockNext).toHaveBeenCalled()
130109
})
131110

111+
test('should not resolve spotlight articles when there are recommended articles', async () => {
112+
const testPage: Partial<import('@/types').Page> = {
113+
title: 'Test Article',
114+
intro: 'Test intro',
115+
relativePath: 'copilot/tutorials/article.md',
116+
applicableVersions: ['free-pro-team@latest'],
117+
}
118+
119+
mockFindPage.mockReturnValueOnce(testPage as any)
120+
121+
const req = createMockRequest({
122+
rawRecommended: ['/copilot/tutorials/article'],
123+
spotlight: [{ article: '/copilot/tutorials/spotlight-article' }],
124+
})
125+
126+
await resolveRecommended(req, mockRes, mockNext)
127+
128+
expect(mockFindPage).toHaveBeenCalledTimes(1)
129+
expect(mockFindPage).toHaveBeenCalledWith(
130+
'/en/copilot/tutorials/article',
131+
req.context!.pages,
132+
req.context!.redirects,
133+
)
134+
expect((req.context!.page as any).recommended).toEqual([
135+
{
136+
title: 'Test Article',
137+
intro: '<p>Test intro</p>',
138+
href: '/copilot/tutorials/article',
139+
category: ['copilot', 'tutorials'],
140+
},
141+
])
142+
expect(mockNext).toHaveBeenCalled()
143+
})
144+
132145
test('should handle articles not found', async () => {
133146
mockFindPage.mockReturnValue(undefined)
134147

@@ -160,31 +173,10 @@ describe('resolveRecommended middleware', () => {
160173

161174
test('should handle mixed valid and invalid articles', async () => {
162175
const testPage: Partial<import('@/types').Page> = {
163-
mtime: Date.now(),
164176
title: 'Valid Article',
165-
rawTitle: 'Valid Article',
166177
intro: 'Valid intro',
167-
rawIntro: 'Valid intro',
168178
relativePath: 'test/valid.md',
169-
fullPath: '/full/path/test/valid.md',
170-
languageCode: 'en',
171-
documentType: 'article',
172-
markdown: 'Valid content',
173-
versions: {},
174179
applicableVersions: ['free-pro-team@latest'],
175-
permalinks: [
176-
{
177-
languageCode: 'en',
178-
pageVersion: 'free-pro-team@latest',
179-
title: 'Valid Article',
180-
href: '/en/test/valid',
181-
hrefWithoutLanguage: '/test/valid',
182-
},
183-
],
184-
renderProp: vi.fn().mockResolvedValue('rendered'),
185-
renderTitle: vi.fn().mockResolvedValue('Valid Article'),
186-
render: vi.fn().mockResolvedValue('rendered content'),
187-
buildRedirects: vi.fn().mockReturnValue({}),
188180
}
189181

190182
mockFindPage.mockReturnValueOnce(testPage as any).mockReturnValueOnce(undefined)
@@ -206,31 +198,10 @@ describe('resolveRecommended middleware', () => {
206198

207199
test('should try page-relative path when content-relative fails', async () => {
208200
const testPage: Partial<import('@/types').Page> = {
209-
mtime: Date.now(),
210201
title: 'Relative Article',
211-
rawTitle: 'Relative Article',
212202
intro: 'Relative intro',
213-
rawIntro: 'Relative intro',
214203
relativePath: 'copilot/relative-article.md',
215-
fullPath: '/full/path/copilot/relative-article.md',
216-
languageCode: 'en',
217-
documentType: 'article',
218-
markdown: 'Relative content',
219-
versions: {},
220204
applicableVersions: ['free-pro-team@latest'],
221-
permalinks: [
222-
{
223-
languageCode: 'en',
224-
pageVersion: 'free-pro-team@latest',
225-
title: 'Relative Article',
226-
href: '/en/copilot/relative-article',
227-
hrefWithoutLanguage: '/copilot/relative-article',
228-
},
229-
],
230-
renderProp: vi.fn().mockResolvedValue('rendered'),
231-
renderTitle: vi.fn().mockResolvedValue('Relative Article'),
232-
render: vi.fn().mockResolvedValue('rendered content'),
233-
buildRedirects: vi.fn().mockReturnValue({}),
234205
}
235206

236207
// Mock findPage to fail on first call (content-relative) and succeed on second (page-relative)
@@ -267,31 +238,10 @@ describe('resolveRecommended middleware', () => {
267238

268239
test('returns paths without language or version prefixes', async () => {
269240
const testPage: Partial<import('@/types').Page> = {
270-
mtime: Date.now(),
271241
title: 'Tutorial Page',
272-
rawTitle: 'Tutorial Page',
273242
intro: 'Tutorial intro',
274-
rawIntro: 'Tutorial intro',
275243
relativePath: 'copilot/tutorials/tutorial-page/index.md',
276-
fullPath: '/full/path/copilot/tutorials/tutorial-page/index.md',
277-
languageCode: 'en',
278-
documentType: 'article',
279-
markdown: 'Tutorial content',
280-
versions: {},
281244
applicableVersions: ['free-pro-team@latest'],
282-
permalinks: [
283-
{
284-
languageCode: 'en',
285-
pageVersion: 'free-pro-team@latest',
286-
title: 'Tutorial Page',
287-
href: '/en/copilot/tutorials/tutorial-page',
288-
hrefWithoutLanguage: '/copilot/tutorials/tutorial-page',
289-
},
290-
],
291-
renderProp: vi.fn().mockResolvedValue('rendered'),
292-
renderTitle: vi.fn().mockResolvedValue('Tutorial Page'),
293-
render: vi.fn().mockResolvedValue('rendered content'),
294-
buildRedirects: vi.fn().mockReturnValue({}),
295245
}
296246

297247
mockFindPage.mockReturnValue(testPage as any)
@@ -322,31 +272,10 @@ describe('resolveRecommended middleware', () => {
322272
test('should filter out articles not available in current version', async () => {
323273
// Create a test page that is only available in fpt, not ghec
324274
const fptOnlyPage: Partial<import('@/types').Page> = {
325-
mtime: Date.now(),
326275
title: 'FPT Only Article',
327-
rawTitle: 'FPT Only Article',
328276
intro: 'This article is only for FPT',
329-
rawIntro: 'This article is only for FPT',
330277
relativePath: 'test/fpt-only.md',
331-
fullPath: '/full/path/test/fpt-only.md',
332-
languageCode: 'en',
333-
documentType: 'article',
334-
markdown: 'FPT only content',
335-
versions: { fpt: '*' }, // Only available in free-pro-team
336278
applicableVersions: ['free-pro-team@latest'], // Not available in ghec
337-
permalinks: [
338-
{
339-
languageCode: 'en',
340-
pageVersion: 'free-pro-team@latest',
341-
title: 'FPT Only Article',
342-
href: '/en/test/fpt-only',
343-
hrefWithoutLanguage: '/test/fpt-only',
344-
},
345-
],
346-
renderProp: vi.fn().mockResolvedValue('rendered'),
347-
renderTitle: vi.fn().mockResolvedValue('FPT Only Article'),
348-
render: vi.fn().mockResolvedValue('rendered content'),
349-
buildRedirects: vi.fn().mockReturnValue({}),
350279
}
351280

352281
mockFindPage.mockReturnValue(fptOnlyPage as any)

0 commit comments

Comments
 (0)