Skip to content

Commit 641bf18

Browse files
fix:cubic-dev-ai comment
1 parent dea731b commit 641bf18

6 files changed

Lines changed: 200 additions & 139 deletions

File tree

frontend/__tests__/unit/components/CardDetailsPage.test.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,31 @@ describe('CardDetailsPage', () => {
809809
expect(screen.getByTestId('metrics-score-circle')).toBeInTheDocument()
810810
})
811811

812+
it('renders Show More button when onLoadMorePullRequests is provided', () => {
813+
render(
814+
<CardDetailsPage
815+
{...defaultProps}
816+
type="module"
817+
pullRequests={mockPullRequests as unknown as PullRequest[]}
818+
onLoadMorePullRequests={jest.fn()}
819+
/>
820+
)
821+
expect(screen.getByRole('button', { name: /Show more/i })).toBeInTheDocument()
822+
})
823+
824+
it('renders Show Less button when onResetPullRequests is provided', () => {
825+
render(
826+
<CardDetailsPage
827+
{...defaultProps}
828+
type="module"
829+
pullRequests={mockPullRequests as unknown as PullRequest[]}
830+
onResetPullRequests={jest.fn()}
831+
onLoadMorePullRequests={undefined}
832+
/>
833+
)
834+
expect(screen.getByRole('button', { name: /Show less/i })).toBeInTheDocument()
835+
})
836+
812837
it('renders social links with correct hrefs and target attributes', () => {
813838
const socialLinks = ['https://github.com/test', 'https://twitter.com/test']
814839
render(<CardDetailsPage {...defaultProps} type="chapter" socialLinks={socialLinks} />)

frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ jest.mock('components/CardDetailsPage', () => (props) => (
2626
<div data-testid="details-card">
2727
<div>{props.title}</div>
2828
<div>{props.summary}</div>
29+
{props.onLoadMorePullRequests && (
30+
<button onClick={props.onLoadMorePullRequests}>Show more</button>
31+
)}
2932
</div>
3033
))
3134

frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useQuery } from '@apollo/client/react'
2-
import { render, screen, fireEvent, waitFor, within } from '@testing-library/react'
2+
import { render, screen, fireEvent, waitFor, within, act } from '@testing-library/react'
33
import { useIssueMutations } from 'hooks/useIssueMutations'
44
import { useParams } from 'next/navigation'
55
import ModuleIssueDetailsPage from 'app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page'
@@ -402,4 +402,60 @@ describe('ModuleIssueDetailsPage', () => {
402402
const unassignButton = screen.getByRole('button', { name: /Unassign @user1/i })
403403
expect(unassignButton).toBeDisabled()
404404
})
405+
it('calls fetchMore when clicking Show More button', async () => {
406+
const fetchMoreMock = jest.fn().mockResolvedValue({
407+
data: {
408+
getModule: {
409+
issueByNumber: {
410+
pullRequests: [
411+
{
412+
id: 'pr-new',
413+
title: 'New PR',
414+
url: 'http://example.com',
415+
state: 'open',
416+
mergedAt: null,
417+
createdAt: new Date().toISOString(),
418+
author: { login: 'user-new', avatarUrl: '' },
419+
},
420+
],
421+
},
422+
},
423+
},
424+
})
425+
426+
mockUseQuery.mockReturnValue({
427+
data: {
428+
getModule: {
429+
issueByNumber: {
430+
...mockIssueData.getModule.issueByNumber,
431+
pullRequests: Array.from({ length: 4 }, (_, i) => ({
432+
...mockIssueData.getModule.issueByNumber.pullRequests[0],
433+
id: `pr-${i}`,
434+
})),
435+
},
436+
},
437+
},
438+
loading: false,
439+
error: undefined,
440+
fetchMore: fetchMoreMock,
441+
})
442+
443+
render(<ModuleIssueDetailsPage />)
444+
445+
const showMoreButton = screen.getByRole('button', { name: /Show more/i })
446+
expect(showMoreButton).toBeInTheDocument()
447+
448+
await act(async () => {
449+
fireEvent.click(showMoreButton)
450+
})
451+
452+
expect(fetchMoreMock).toHaveBeenCalledWith(
453+
expect.objectContaining({
454+
variables: expect.objectContaining({
455+
offset: 4,
456+
limit: 4,
457+
}),
458+
})
459+
)
460+
})
405461
})

frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx

Lines changed: 36 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { getSimpleDuration } from 'components/ModuleCard'
1515
const ModuleDetailsPage = () => {
1616
const { programKey, moduleKey } = useParams<{ programKey: string; moduleKey: string }>()
1717
const [hasMorePRs, setHasMorePRs] = useState(true)
18+
const [visibleCount, setVisibleCount] = useState(4)
19+
const [isFetchingMore, setIsFetchingMore] = useState(false)
1820
const limit = 4
1921

2022
const {
@@ -87,66 +89,51 @@ const ModuleDetailsPage = () => {
8789
details={moduleDetails}
8890
domains={programModule.domains}
8991
mentors={programModule.mentors}
90-
pullRequests={programModule.recentPullRequests || []}
92+
pullRequests={(programModule.recentPullRequests || []).slice(0, visibleCount)}
9193
summary={programModule.description}
9294
tags={programModule.tags}
9395
title={programModule.name}
9496
type="module"
9597
onLoadMorePullRequests={
96-
hasMorePRs
98+
hasMorePRs || (programModule.recentPullRequests || []).length > visibleCount
9799
? () => {
100+
if (isFetchingMore) return
98101
const currentLength = programModule.recentPullRequests?.length || 0
99-
fetchMore({
100-
variables: {
101-
programKey,
102-
moduleKey,
103-
offset: currentLength,
104-
limit,
105-
},
106-
updateQuery: (prevResult, { fetchMoreResult }) => {
107-
if (!fetchMoreResult) return prevResult
108-
const newPRs = fetchMoreResult.getModule?.recentPullRequests || []
109-
if (newPRs.length < limit) setHasMorePRs(false)
110-
if (newPRs.length === 0) return prevResult
111-
return {
112-
...prevResult,
113-
getModule: {
114-
...prevResult.getModule,
115-
recentPullRequests: [
116-
...(prevResult.getModule?.recentPullRequests || []),
117-
...newPRs,
118-
],
119-
},
120-
}
121-
},
122-
})
102+
if (hasMorePRs && currentLength < visibleCount + limit) {
103+
setIsFetchingMore(true)
104+
fetchMore({
105+
variables: {
106+
programKey,
107+
moduleKey,
108+
offset: currentLength,
109+
limit,
110+
},
111+
updateQuery: (prevResult, { fetchMoreResult }) => {
112+
if (!fetchMoreResult) return prevResult
113+
const newPRs = fetchMoreResult.getModule?.recentPullRequests || []
114+
if (newPRs.length < limit) setHasMorePRs(false)
115+
if (newPRs.length === 0) return prevResult
116+
return {
117+
...prevResult,
118+
getModule: {
119+
...prevResult.getModule,
120+
recentPullRequests: [
121+
...(prevResult.getModule?.recentPullRequests || []),
122+
...newPRs,
123+
],
124+
},
125+
}
126+
},
127+
}).finally(() => setIsFetchingMore(false))
128+
}
129+
setVisibleCount((prev) => prev + limit)
123130
}
124131
: undefined
125132
}
126133
onResetPullRequests={
127-
hasMorePRs
128-
? undefined
129-
: () => {
130-
setHasMorePRs(true)
131-
fetchMore({
132-
variables: {
133-
programKey,
134-
moduleKey,
135-
offset: 0,
136-
limit,
137-
},
138-
updateQuery: (prevResult, { fetchMoreResult }) => {
139-
if (!fetchMoreResult) return prevResult
140-
return {
141-
...prevResult,
142-
getModule: {
143-
...prevResult.getModule,
144-
recentPullRequests: fetchMoreResult.getModule?.recentPullRequests || [],
145-
},
146-
}
147-
},
148-
})
149-
}
134+
visibleCount > limit && (programModule.recentPullRequests || []).length > limit
135+
? () => setVisibleCount(limit)
136+
: undefined
150137
}
151138
/>
152139
)

frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx

Lines changed: 60 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import SecondaryCard from 'components/SecondaryCard'
2929
const ModuleIssueDetailsPage = () => {
3030
const params = useParams<{ programKey: string; moduleKey: string; issueId: string }>()
3131
const [hasMorePRs, setHasMorePRs] = useState(true)
32+
const [visibleCount, setVisibleCount] = useState(4)
33+
const [isFetchingMore, setIsFetchingMore] = useState(false)
3234
const limit = 4
3335
const { programKey, moduleKey, issueId } = params
3436

@@ -341,84 +343,70 @@ const ModuleIssueDetailsPage = () => {
341343

342344
<SecondaryCard icon={FaCodeBranch} title="Pull Requests">
343345
<div className="grid grid-cols-1 gap-3">
344-
{(issue.pullRequests || []).map((pr) => (
346+
{(issue.pullRequests || []).slice(0, visibleCount).map((pr) => (
345347
<MentorshipPullRequest key={pr.id} pr={pr} />
346348
))}
347349

348-
{hasMorePRs && (
350+
{(hasMorePRs ||
351+
(issue.pullRequests || []).length > visibleCount ||
352+
(visibleCount > limit && (issue.pullRequests || []).length > limit)) && (
349353
<div className="mt-4 flex justify-start gap-4">
350-
<button
351-
onClick={() => {
352-
const currentLength = issue.pullRequests?.length || 0
353-
fetchMore({
354-
variables: {
355-
programKey,
356-
moduleKey,
357-
number: Number(issueId),
358-
offset: currentLength,
359-
limit,
360-
},
361-
updateQuery: (prevResult, { fetchMoreResult }) => {
362-
if (!fetchMoreResult) return prevResult
363-
const newPRs = fetchMoreResult.getModule?.issueByNumber?.pullRequests || []
364-
if (newPRs.length < limit) setHasMorePRs(false)
365-
if (newPRs.length === 0) return prevResult
366-
return {
367-
...prevResult,
368-
getModule: {
369-
...prevResult.getModule,
370-
issueByNumber: {
371-
...prevResult.getModule?.issueByNumber,
372-
pullRequests: [
373-
...(prevResult.getModule?.issueByNumber?.pullRequests || []),
374-
...newPRs,
375-
],
376-
},
354+
{(hasMorePRs || (issue.pullRequests || []).length > visibleCount) && (
355+
<button
356+
onClick={() => {
357+
if (isFetchingMore) return
358+
const currentLength = issue.pullRequests?.length || 0
359+
// If we need more data than we have, fetch it
360+
if (hasMorePRs && currentLength < visibleCount + limit) {
361+
setIsFetchingMore(true)
362+
fetchMore({
363+
variables: {
364+
programKey,
365+
moduleKey,
366+
number: Number(issueId),
367+
offset: currentLength,
368+
limit,
377369
},
378-
}
379-
},
380-
})
381-
}}
382-
className="flex items-center bg-transparent px-2 py-1 text-blue-400 hover:underline focus-visible:rounded focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
383-
>
384-
Show more <FaChevronDown aria-hidden="true" className="ml-2 text-sm" />
385-
</button>
386-
</div>
387-
)}
388-
389-
{!hasMorePRs && (issue.pullRequests || []).length > 4 && (
390-
<div className="mt-4 flex justify-start gap-4">
391-
<button
392-
onClick={() => {
393-
setHasMorePRs(true)
394-
fetchMore({
395-
variables: {
396-
programKey,
397-
moduleKey,
398-
number: Number(issueId),
399-
offset: 0,
400-
limit,
401-
},
402-
updateQuery: (prevResult, { fetchMoreResult }) => {
403-
if (!fetchMoreResult) return prevResult
404-
return {
405-
...prevResult,
406-
getModule: {
407-
...prevResult.getModule,
408-
issueByNumber: {
409-
...prevResult.getModule?.issueByNumber,
410-
pullRequests:
411-
fetchMoreResult.getModule?.issueByNumber?.pullRequests || [],
412-
},
370+
updateQuery: (prevResult, { fetchMoreResult }) => {
371+
if (!fetchMoreResult) return prevResult
372+
const newPRs =
373+
fetchMoreResult.getModule?.issueByNumber?.pullRequests || []
374+
if (newPRs.length < limit) setHasMorePRs(false)
375+
if (newPRs.length === 0) return prevResult
376+
return {
377+
...prevResult,
378+
getModule: {
379+
...prevResult.getModule,
380+
issueByNumber: {
381+
...prevResult.getModule?.issueByNumber,
382+
pullRequests: [
383+
...(prevResult.getModule?.issueByNumber?.pullRequests || []),
384+
...newPRs,
385+
],
386+
},
387+
},
388+
}
413389
},
414-
}
415-
},
416-
})
417-
}}
418-
className="flex items-center bg-transparent px-2 py-1 text-blue-400 hover:underline focus-visible:rounded focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
419-
>
420-
Show less <FaChevronUp aria-hidden="true" className="ml-2 text-sm" />
421-
</button>
390+
}).finally(() => setIsFetchingMore(false))
391+
}
392+
setVisibleCount((prev) => prev + limit)
393+
}}
394+
type="button"
395+
className="flex items-center bg-transparent px-2 py-1 text-blue-400 hover:underline focus-visible:rounded focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
396+
>
397+
Show more <FaChevronDown aria-hidden="true" className="ml-2 text-sm" />
398+
</button>
399+
)}
400+
401+
{visibleCount > limit && (issue.pullRequests || []).length > limit && (
402+
<button
403+
onClick={() => setVisibleCount(limit)}
404+
type="button"
405+
className="flex items-center bg-transparent px-2 py-1 text-blue-400 hover:underline focus-visible:rounded focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
406+
>
407+
Show less <FaChevronUp aria-hidden="true" className="ml-2 text-sm" />
408+
</button>
409+
)}
422410
</div>
423411
)}
424412

0 commit comments

Comments
 (0)