Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4721190
Warn on API calls during initial render not prefetched
jonkafton Nov 6, 2024
96f6c54
Merge branch 'main' into jk/5919-prefetch-mechanism
jonkafton Nov 6, 2024
fc4dcb7
Full prefetch for homepage (commented)
jonkafton Nov 6, 2024
52e6cf7
Prefetch utility
jonkafton Nov 7, 2024
bae8e93
Check for queries prefetched that are not needed during render and warn
jonkafton Nov 7, 2024
73209e0
No need to stringify
jonkafton Nov 7, 2024
9f25822
Replace useQuery overrides with decoupled cache check (wip)
jonkafton Nov 7, 2024
d34bd26
Observer count for unnecessary prefetch warnings
jonkafton Nov 8, 2024
98843c2
Remove useQuery override
jonkafton Nov 8, 2024
23e328e
Add warnings hook
jonkafton Nov 8, 2024
db5916b
Test prefetch warnings
jonkafton Nov 8, 2024
cd9ad5a
Remove inadvertent/unnecessary diff
jonkafton Nov 8, 2024
c41d537
Remove comments
jonkafton Nov 8, 2024
e4c19b7
Prefetch homepage. Invalidate learning resource cache items
jonkafton Nov 12, 2024
573e416
Remove comment
jonkafton Nov 12, 2024
e17f352
Update comment
jonkafton Nov 12, 2024
2cf4ab3
Temp remove featured resource list shuffle
jonkafton Nov 12, 2024
e015ac3
Merge branch 'jk/5919-prefetch-mechanism' into jk/5920-prefetch-homepage
jonkafton Nov 12, 2024
c10f677
Remove unused
jonkafton Nov 12, 2024
af74cb4
Prefetch calls
jonkafton Nov 12, 2024
d3b7fe3
Prefetch topics page
jonkafton Nov 12, 2024
c8cde78
Rename key factory re-exports
jonkafton Nov 13, 2024
12c6955
Merge branch 'main' into jk/5920-prefetch-homepage
jonkafton Nov 13, 2024
0c46451
Units page prefetch
jonkafton Nov 14, 2024
2bdea92
Single request for all unit channel details
jonkafton Nov 14, 2024
84eb78a
Update unit listing page tests
jonkafton Nov 15, 2024
14d49ae
Page arg types
jonkafton Nov 15, 2024
11d76cf
Optional route params
jonkafton Nov 15, 2024
1ac7666
Remove comment
jonkafton Nov 18, 2024
b713b53
Remove unused
jonkafton Nov 18, 2024
546c0ce
Merge branch 'main' into jk/5920-prefetch-homepage
jonkafton Nov 19, 2024
f4b10d6
Remove comment
jonkafton Nov 19, 2024
6a6d160
Merge branch 'main' into jk/5920-prefetch-homepage
jonkafton Nov 22, 2024
b126369
Reinstate featured list shuffle and remove cache invalidation to refe…
jonkafton Nov 22, 2024
25ac469
Add news to server render
jonkafton Nov 22, 2024
1cf3a38
Merge branch 'main' into jk/5920-prefetch-homepage
jonkafton Nov 25, 2024
0b07847
Client only navigation for the resource drawer
jonkafton Dec 3, 2024
be37b33
Merge branch 'main' into jk/5920-prefetch-homepage
jonkafton Dec 3, 2024
301e023
Shallow navigation check and remove redundant condition
jonkafton Dec 5, 2024
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
2 changes: 1 addition & 1 deletion frontends/api/src/hooks/channels/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ export {
useChannelsList,
useChannelPartialUpdate,
useChannelCounts,
channels as channelsKeyFactory,
channels,
}
2 changes: 1 addition & 1 deletion frontends/api/src/hooks/learningResources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,5 @@ export {
useSchoolsList,
useSimilarLearningResources,
useVectorSimilarLearningResources,
learningResources as learningResourcesKeyFactory,
learningResources,
}
2 changes: 1 addition & 1 deletion frontends/api/src/hooks/newsEvents/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ export {
useNewsEventsList,
useNewsEventsDetail,
NewsEventsListFeedTypeEnum,
newsEvents as newsEventsKeyFactory,
newsEvents,
}
6 changes: 1 addition & 5 deletions frontends/api/src/hooks/testimonials/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,4 @@ const useTestimonialDetail = (id: number | undefined) => {
})
}

export {
useTestimonialDetail,
useTestimonialList,
testimonials as testimonialsKeyFactory,
}
export { useTestimonialDetail, useTestimonialList, testimonials }
14 changes: 10 additions & 4 deletions frontends/api/src/ssr/prefetch.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { QueryClient, dehydrate } from "@tanstack/react-query"
import type { Query } from "@tanstack/react-query"

// Utility to avoid repetition in server components
export const prefetch = async (queries: (Query | unknown)[]) => {
const queryClient = new QueryClient()
/* Utility to avoid repetition in server components
* Optionally pass the queryClient returned from a previous prefetch
* where queries are dependent on previous results
*/
export const prefetch = async (
queries: (Query | unknown)[],
queryClient?: QueryClient,
) => {
queryClient = queryClient || new QueryClient()

await Promise.all(
queries.map((query) => queryClient.prefetchQuery(query as Query)),
)

return dehydrate(queryClient)
return { dehydratedState: dehydrate(queryClient), queryClient }
}
12 changes: 6 additions & 6 deletions frontends/api/src/ssr/usePrefetchWarnings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { usePrefetchWarnings } from "./usePrefetchWarnings"
import { setupReactQueryTest } from "../hooks/test-utils"
import { urls, factories, setMockResponse } from "../test-utils"
import {
learningResourcesKeyFactory,
learningResources,
useLearningResourcesDetail,
} from "../hooks/learningResources"

Expand Down Expand Up @@ -45,7 +45,7 @@ describe("SSR prefetch warnings", () => {
expect.objectContaining({
disabled: false,
initialStatus: "loading",
key: learningResourcesKeyFactory.detail(1).queryKey,
key: learningResources.detail(1).queryKey,
observerCount: 1,
}),
],
Expand All @@ -65,7 +65,7 @@ describe("SSR prefetch warnings", () => {
wrapper,
initialProps: {
queryClient,
exemptions: [learningResourcesKeyFactory.detail(1).queryKey],
exemptions: [learningResources.detail(1).queryKey],
},
})

Expand All @@ -83,7 +83,7 @@ describe("SSR prefetch warnings", () => {
const { unmount } = renderHook(
() =>
useQuery({
...learningResourcesKeyFactory.detail(1),
...learningResources.detail(1),
initialData: data,
}),
{ wrapper },
Expand All @@ -105,9 +105,9 @@ describe("SSR prefetch warnings", () => {
[
{
disabled: false,
hash: JSON.stringify(learningResourcesKeyFactory.detail(1).queryKey),
hash: JSON.stringify(learningResources.detail(1).queryKey),
initialStatus: "success",
key: learningResourcesKeyFactory.detail(1).queryKey,
key: learningResources.detail(1).queryKey,
observerCount: 0,
status: "success",
},
Expand Down
1 change: 1 addition & 0 deletions frontends/api/src/test-utils/factories/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ const _channelShared = (): Partial<Omit<Channel, "channel_type">> => {
key: faker.lorem.slug(),
value: faker.lorem.slug(),
}),
channel_url: `${faker.internet.url({ appendSlash: false })}${faker.system.directoryPath()}`,
}
}

Expand Down
3 changes: 3 additions & 0 deletions frontends/api/src/test-utils/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type {
NewsEventsApiNewsEventsListRequest,
TestimonialsApi,
ChannelsApi,
} from "../generated/v0"
import type {
LearningResourcesApi as LRApi,
Expand Down Expand Up @@ -186,6 +187,8 @@ const channels = {
details: (channelType: string, name: string) =>
`${API_BASE_URL}/api/v0/channels/type/${channelType}/${name}/`,
patch: (id: number) => `${API_BASE_URL}/api/v0/channels/${id}/`,
list: (params?: Paramsv0<ChannelsApi, "channelsList">) =>
`${API_BASE_URL}/api/v0/channels/${query(params)}`,
}

const widgetLists = {
Expand Down
29 changes: 13 additions & 16 deletions frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react"
import { LearningResourceOfferorDetail, OfferedByEnum } from "api"
import type { OfferedByEnum } from "api"
import type { UnitChannel } from "api/v0"
import {
Card,
Skeleton,
Expand All @@ -8,7 +9,6 @@ import {
theme,
UnitLogo,
} from "ol-components"
import { useChannelDetail } from "api/hooks/channels"
import Link from "next/link"

const CardStyled = styled(Card)({
Expand Down Expand Up @@ -102,25 +102,23 @@ const CountsText = styled(Typography)(({ theme }) => ({
}))

interface UnitCardsProps {
units: LearningResourceOfferorDetail[] | undefined
channels: UnitChannel[] | undefined
courseCounts: Record<string, number>
programCounts: Record<string, number>
}

interface UnitCardProps {
unit: LearningResourceOfferorDetail
channel: UnitChannel
courseCount: number
programCount: number
}

const UnitCard: React.FC<UnitCardProps> = (props) => {
const { unit, courseCount, programCount } = props
const channelDetailQuery = useChannelDetail("unit", unit.code)
const channelDetail = channelDetailQuery.data
const unitUrl = channelDetail?.channel_url
const { channel, courseCount, programCount } = props
const unit = channel.unit_detail.unit

if (!unitUrl) return null
const href = unitUrl && new URL(unitUrl).pathname
if (!channel.channel_url) return null
const href = new URL(channel.channel_url).pathname

return (
<CardStyled forwardClicksToLink data-testid={`unit-card-${unit.code}`}>
Expand All @@ -134,9 +132,7 @@ const UnitCard: React.FC<UnitCardProps> = (props) => {
</LogoContainer>
<CardBottom>
<ValuePropContainer>
<HeadingText>
{channelDetail?.configuration?.heading}
</HeadingText>
<HeadingText>{channel?.configuration?.heading}</HeadingText>
</ValuePropContainer>
<CountsTextContainer>
<CountsText data-testid={`course-count-${unit.code}`}>
Expand Down Expand Up @@ -174,17 +170,18 @@ export const UnitCardLoading = () => {
}

export const UnitCards: React.FC<UnitCardsProps> = (props) => {
const { units, courseCounts, programCounts } = props
const { channels, courseCounts, programCounts } = props
return (
<>
{units?.map((unit) => {
{channels?.map((channel) => {
const unit = channel.unit_detail.unit
const courseCount = courseCounts[unit.code] || 0
const programCount = programCounts[unit.code] || 0

return unit.value_prop ? (
<UnitCard
key={unit.code}
unit={unit}
channel={channel}
courseCount={courseCount}
programCount={programCount}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,71 @@ import React from "react"
import { renderWithProviders, screen, waitFor, within } from "@/test-utils"
import UnitsListingPage from "./UnitsListingPage"
import { factories, setMockResponse, urls } from "api/test-utils"
import { ChannelTypeEnum } from "api/v0"
import type { UnitChannel } from "api/v0"
import { assertHeadings } from "ol-test-utilities"

describe("DepartmentListingPage", () => {
describe("UnitListingPage", () => {
const setupApis = () => {
const make = factories.learningResources
const academicUnit1 = make.offeror({
code: "academicUnit1",
name: "Academic Unit 1",
value_prop: "Academic Unit 1 value prop",
professional: false,
const make = factories.channels
const academicUnit1 = make.channel({
channel_type: ChannelTypeEnum.Unit,
name: "academicUnit1",
title: "Academic Unit 1",
unit_detail: {
unit: {
value_prop: "Academic Unit 1 value prop",
professional: false,
},
},
})
const academicUnit2 = make.offeror({
code: "academicUnit2",
name: "Academic Unit 2",
value_prop: "Academic Unit 2 value prop",
professional: false,
const academicUnit2 = make.channel({
channel_type: ChannelTypeEnum.Unit,
name: "academicUnit2",
title: "Academic Unit 2",
unit_detail: {
unit: {
value_prop: "Academic Unit 2 value prop",
professional: false,
},
},
})
const academicUnit3 = make.offeror({
code: "academicUnit3",
name: "Academic Unit 3",
value_prop: "Academic Unit 3 value prop",
professional: false,
const academicUnit3 = make.channel({
channel_type: ChannelTypeEnum.Unit,
name: "academicUnit3",
title: "Academic Unit 3",
unit_detail: {
unit: {
value_prop: "Academic Unit 3 value prop",
professional: false,
},
},
})

const professionalUnit1 = make.offeror({
code: "professionalUnit1",
name: "Professional Unit 1",
value_prop: "Professional Unit 1 value prop",
professional: true,
const professionalUnit1 = make.channel({
channel_type: ChannelTypeEnum.Unit,
name: "professionalUnit1",
title: "Professional Unit 1",
unit_detail: {
unit: {
value_prop: "Professional Unit 1 value prop",
professional: true,
},
},
})
const professionalUnit2 = make.offeror({
code: "professionalUnit2",
name: "Professional Unit 2",
value_prop: "Professional Unit 2 value prop",
professional: true,
const professionalUnit2 = make.channel({
channel_type: ChannelTypeEnum.Unit,
name: "professionalUnit2",
title: "Professional Unit 2",
unit_detail: {
unit: {
value_prop: "Professional Unit 2 value prop",
professional: true,
},
},
})

const units = [
const unitChannels = [
academicUnit1,
academicUnit2,
academicUnit3,
Expand All @@ -63,29 +90,23 @@ describe("DepartmentListingPage", () => {

setMockResponse.get(
urls.channels.counts("unit"),
units.map((unit) => {
unitChannels.map((channel) => {
return {
name: unit.code,
name: channel.name,
counts: {
courses: courseCounts[unit.code],
programs: programCounts[unit.code],
courses: courseCounts[channel.name],
programs: programCounts[channel.name],
},
}
}),
)
setMockResponse.get(urls.offerors.list(), {
count: units.length,
results: units,
})

units.forEach((unit) => {
setMockResponse.get(urls.channels.details("unit", unit.code), {
channel_url: `${window.location.origin}/units/${unit.code}`,
})
setMockResponse.get(urls.channels.list({ channel_type: "unit" }), {
count: unitChannels.length,
results: unitChannels,
})

return {
units,
unitChannels,
courseCounts,
programCounts,
}
Expand All @@ -98,7 +119,7 @@ describe("DepartmentListingPage", () => {
})

it("Shows unit properties within the proper section", async () => {
const { units, courseCounts, programCounts } = setupApis()
const { unitChannels, courseCounts, programCounts } = setupApis()

renderWithProviders(<UnitsListingPage />)

Expand All @@ -116,11 +137,15 @@ describe("DepartmentListingPage", () => {
return links
})

units.forEach((unit) => {
unitChannels.forEach((channel) => {
const { unit } = (channel as UnitChannel).unit_detail
const section = unit.professional ? professionalSection : academicSection
const card = within(section).getByTestId(`unit-card-${unit.code}`)
const link = within(card).getByRole("link")
expect(link).toHaveAttribute("href", `/units/${unit.code}`)
expect(link).toHaveAttribute(
"href",
new URL(channel.channel_url!).pathname,
)

const courseCount = courseCounts[unit.code]
const programCount = programCounts[unit.code]
Expand Down
Loading
Loading