Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3e9c643
add new darker chip variant
gumaerc Oct 2, 2024
2e28f91
move subtopic display to the banner
gumaerc Oct 2, 2024
e7277f7
remove max width on banner subtitle texts
gumaerc Oct 3, 2024
6ad22eb
query subtopics only on parent topic channels, and on child topic cha…
gumaerc Oct 3, 2024
7f70bad
don't display the current topic in related topics
gumaerc Oct 3, 2024
6d6ee88
show parent topic breadcrumbs on child topic channel pages
gumaerc Oct 4, 2024
9164376
fix tests
gumaerc Oct 4, 2024
71ff509
only do subtopics mocking if necessary
gumaerc Oct 7, 2024
b998fe9
fix editchannelpage test
gumaerc Oct 7, 2024
65f339c
move action buttons under the title on mobile
gumaerc Oct 7, 2024
43a43ea
fix test
gumaerc Oct 7, 2024
abf9bd6
add new default channel background image
gumaerc Oct 7, 2024
6dcc95c
"toopics"
gumaerc Oct 7, 2024
f8e5106
fix background position
gumaerc Oct 7, 2024
c71febb
remove accidentally committed file
gumaerc Oct 7, 2024
3d45b18
fix background size on mobile
gumaerc Oct 7, 2024
306bd00
remove unnecessary order css rule
gumaerc Oct 8, 2024
2262c20
fix banner background on mobile
gumaerc Oct 8, 2024
279b448
fix breadcrumb props
gumaerc Oct 8, 2024
6d7d433
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 8, 2024
a0aa84c
remove unnecessary topic id filter
gumaerc Oct 9, 2024
568dc45
remove unnecessary breadcrumb fallback
gumaerc Oct 9, 2024
0dc711c
set the default banner background image in the banner component itsel…
gumaerc Oct 9, 2024
ad51130
properly set default background image
gumaerc Oct 9, 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
11 changes: 11 additions & 0 deletions frontends/api/src/hooks/learningResources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ const useFeaturedLearningResourcesList = (params: FeaturedListParams = {}) => {
return useQuery(learningResources.featured(params))
}

const useLearningResourceTopic = (
id: number,
opts: Pick<UseQueryOptions, "enabled"> = {},
) => {
return useQuery({
...learningResources.topic(id),
...opts,
})
}

const useLearningResourceTopics = (
params: TopicsListRequest = {},
opts: Pick<UseQueryOptions, "enabled"> = {},
Expand Down Expand Up @@ -486,6 +496,7 @@ export {
useLearningResourcesList,
useFeaturedLearningResourcesList,
useLearningResourcesDetail,
useLearningResourceTopic,
useLearningResourceTopics,
useLearningPathsList,
useLearningPathsDetail,
Expand Down
5 changes: 5 additions & 0 deletions frontends/api/src/hooks/learningResources/keyFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ const learningResources = createQueryKeys("learningResources", {
})
},
}),
topic: (id: number | undefined) => ({
queryKey: [id],
queryFn: () =>
id ? topicsApi.topicsRetrieve({ id }).then((res) => res.data) : null,
}),
topics: (params: TopicsListRequest) => ({
queryKey: [params],
queryFn: () => topicsApi.topicsList(params).then((res) => res.data),
Expand Down
7 changes: 6 additions & 1 deletion frontends/api/src/test-utils/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ const query = (params: any) => {
const queryString = new URLSearchParams()
for (const [key, value] of Object.entries(params)) {
if (Array.isArray(value)) {
value.forEach((v) => queryString.append(key, String(v)))
if (value.length === 0) {
queryString.append(key, "")
} else {
value.forEach((v) => queryString.append(key, String(v)))
}
} else {
queryString.append(key, String(value))
}
Expand Down Expand Up @@ -96,6 +100,7 @@ const platforms = {
}

const topics = {
get: (id: number) => `${API_BASE_URL}/api/v1/topics/${id}/`,
list: (params?: Params<TopicsApi, "topicsList">) =>
`${API_BASE_URL}/api/v1/topics/${query(params)}`,
}
Expand Down
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
136 changes: 109 additions & 27 deletions frontends/mit-learn/src/pages/ChannelPage/ChannelPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,51 @@ const setupApis = (
results: [],
})

if (
channel.channel_type === ChannelTypeEnum.Topic &&
channel.topic_detail.topic
) {
const subTopics = factories.learningResources.topics({ count: 5 })
setMockResponse.get(
urls.topics.list({ parent_topic_id: [channel.topic_detail.topic] }),
subTopics,
)
return {
channel,
subTopics,
}
return {
channel,
}
}

const setupTopicApis = (channel: Channel) => {
invariant(
channel.channel_type === ChannelTypeEnum.Topic,
"Topic channel must have a topic",
)
const topic = factories.learningResources.topic()
channel.channel_url = `/c/${channel.channel_type}/${channel.name.replace(/\s/g, "-")}`
topic.channel_url = channel.channel_url
topic.id = channel.topic_detail.topic ?? 0
const subTopics = factories.learningResources.topics({ count: 5 })
setMockResponse.get(urls.topics.get(topic.id), topic)
setMockResponse.get(
urls.topics.list({ parent_topic_id: [topic.id] }),
subTopics,
)
const subTopicChannels = subTopics.results.map((subTopic) => {
subTopic.parent = topic.id
const subTopicChannel = factories.channels.channel({
channel_type: ChannelTypeEnum.Topic,
name: subTopic.name.replace(/\s/g, "-"),
title: subTopic.name,
topic_detail: { topic: subTopic.id },
})
const channelUrl = `/c/${subTopicChannel.channel_type}/${subTopicChannel.name.replace(/\s/g, "-")}`
subTopic.channel_url = channelUrl
subTopicChannel.channel_url = channelUrl
setMockResponse.get(urls.topics.get(subTopic.id), subTopic)
setMockResponse.get(
urls.channels.details(
subTopicChannel.channel_type,
subTopicChannel.name.replace(/\s/g, "-"),
),
subTopicChannel,
)
return subTopicChannel
})
return {
channel,
topic,
subTopics,
subTopicChannels,
}
}

Expand All @@ -141,6 +169,9 @@ describe.each(ALL_CHANNEL_TYPES)(
"platform=ocw&platform=mitxonline&department=8&department=9",
channel_type: channelType,
})
if (channelType === ChannelTypeEnum.Topic) {
setupTopicApis(channel)
}
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
await screen.findAllByText(channel.title)
const expectedProps = expect.objectContaining({
Expand All @@ -155,23 +186,29 @@ describe.each(ALL_CHANNEL_TYPES)(
expectedProps,
expectedContext,
)
})
}, 10000)
it("Does not display the channel search if search_filter is undefined", async () => {
const { channel } = setupApis({
channel_type: channelType,
})
channel.search_filter = undefined
if (channelType === ChannelTypeEnum.Topic) {
setupTopicApis(channel)
}
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
await screen.findAllByText(channel.title)

expect(mockedChannelSearch).toHaveBeenCalledTimes(0)
})
}, 10000)

it("Includes heading and subheading in banner", async () => {
const { channel } = setupApis({
channel_type: channelType,
})
channel.search_filter = undefined
if (channelType === ChannelTypeEnum.Topic) {
setupTopicApis(channel)
}
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
await screen.findAllByText(channel.title)

Expand All @@ -185,7 +222,7 @@ describe.each(ALL_CHANNEL_TYPES)(
expect(el).toBeInTheDocument()
})
})
})
}, 10000)

it.each([{ isSubscribed: false }, { isSubscribed: true }])(
"Displays the subscribe toggle for authenticated and unauthenticated users",
Expand All @@ -195,10 +232,14 @@ describe.each(ALL_CHANNEL_TYPES)(
{},
{ isSubscribed },
)
if (channelType === ChannelTypeEnum.Topic) {
setupTopicApis(channel)
}
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
const subscribedButton = await screen.findByText("Follow")
expect(subscribedButton).toBeVisible()
const subscribedButton = await screen.findAllByText("Follow")
expect(subscribedButton[0]).toBeVisible()
},
10000,
)
},
)
Expand All @@ -211,18 +252,24 @@ describe.each(NON_UNIT_CHANNEL_TYPES)(
search_filter: "topic=physics",
channel_type: channelType,
})
if (channelType === ChannelTypeEnum.Topic) {
setupTopicApis(channel)
}

renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
await screen.findAllByText(channel.title)
const carousels = screen.queryByText("Featured Courses")
expect(carousels).toBe(null)
})
}, 10000)

it("Displays the title, background, and avatar (channelType: %s)", async () => {
const { channel } = setupApis({
search_filter: "offered_by=ocw",
channel_type: channelType,
})
if (channelType === ChannelTypeEnum.Topic) {
setupTopicApis(channel)
}

renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
const title = await screen.findByRole("heading", { name: channel.title })
Expand All @@ -245,13 +292,16 @@ describe.each(NON_UNIT_CHANNEL_TYPES)(
img.src.includes(channel.configuration.logo),
)
expect(logos.length).toBe(1)
})
}, 10000)

test("headings", async () => {
const { channel } = setupApis({
search_filter: "topic=Physics",
channel_type: channelType,
})
if (channelType === ChannelTypeEnum.Topic) {
setupTopicApis(channel)
}
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })

await waitFor(() => {
Expand All @@ -262,27 +312,59 @@ describe.each(NON_UNIT_CHANNEL_TYPES)(
{ level: 3, name: "Search Results" },
])
})
})
}, 10000)
},
)

describe("Channel Pages, Topic only", () => {
test("Subtopics display", async () => {
const { channel, subTopics } = setupApis({
const { channel } = setupApis({
search_filter: "topic=Physics",
channel_type: ChannelTypeEnum.Topic,
})
renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` })
const { topic, subTopics } = setupTopicApis(channel)
invariant(topic)
renderTestApp({
url: `/c/${channel.channel_type}/${channel.name.replace(/\s/g, "-")}`,
})

invariant(subTopics)
const subTopicsTitle = await screen.findByText("Subtopics")
expect(subTopicsTitle).toBeInTheDocument()
const links = await screen.findAllByRole("link", {
// name arg can be string, regex, or function
name: (name) => subTopics?.results.map((t) => t.name).includes(name),
})
links.forEach((link, i) => {
links.forEach(async (link, i) => {
expect(link).toHaveAttribute("href", subTopics.results[i].channel_url)
})
})
}, 10000)

test("Related topics display", async () => {
const { channel } = setupApis({
search_filter: "topic=Physics",
channel_type: ChannelTypeEnum.Topic,
})
const { subTopics, subTopicChannels } = setupTopicApis(channel)
invariant(subTopicChannels)
const subTopicChannel = subTopicChannels[0]
const filteredSubTopics = subTopics?.results.filter(
(t) =>
t.name.replace(/\s/g, "-") !== subTopicChannel.name.replace(/\s/g, "-"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be t.name !== subtopicChannel.title ... topic name and channel title are equivalent. (Topic model should use title, I guess. Oh well).

)
renderTestApp({
url: `/c/${subTopicChannel.channel_type}/${subTopicChannel.name.replace(/\s/g, "-")}`,
})

const relatedTopicsTitle = await screen.findByText("Related Topics")
expect(relatedTopicsTitle).toBeInTheDocument()
const links = await screen.findAllByRole("link", {
// name arg can be string, regex, or function
name: (name) => filteredSubTopics?.map((t) => t.name).includes(name),
})
links.forEach(async (link, i) => {
expect(link).toHaveAttribute("href", filteredSubTopics[i].channel_url)
})
}, 10000)
})

describe("Channel Pages, Unit only", () => {
Expand Down
61 changes: 1 addition & 60 deletions frontends/mit-learn/src/pages/ChannelPage/ChannelPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,66 +9,13 @@ import type {
BooleanFacets,
} from "@mitodl/course-search-utils"
import { ChannelTypeEnum } from "api/v0"
import { useLearningResourceTopics } from "api/hooks/learningResources"
import { ChipLink, Container, styled, Typography } from "ol-components"
import { propsNotNil } from "ol-utilities"

const SubTopicsContainer = styled(Container)(({ theme }) => ({
marginBottom: "60px",
[theme.breakpoints.down("sm")]: {
marginBottom: "24px",
},
}))

const SubTopicsHeader = styled(Typography)(({ theme }) => ({
marginBottom: "10px",
...theme.typography.subtitle1,
}))

const ChipsContainer = styled.div({
display: "flex",
flexWrap: "wrap",
gap: "12px",
})
import { Typography } from "ol-components"

type RouteParams = {
channelType: ChannelTypeEnum
name: string
}

type SubTopicDisplayProps = {
parentTopicId: number
}

const SubTopicsDisplay: React.FC<SubTopicDisplayProps> = (props) => {
const { parentTopicId } = props
const topicsQuery = useLearningResourceTopics({
parent_topic_id: [parentTopicId],
})
const totalSubtopics = topicsQuery.data?.results.length ?? 0
const subTopics = topicsQuery.data?.results.filter(
propsNotNil(["channel_url"]),
)
return (
totalSubtopics > 0 && (
<SubTopicsContainer>
<SubTopicsHeader>Related Topics</SubTopicsHeader>
<ChipsContainer>
{subTopics?.map((topic) => (
<ChipLink
size="large"
variant="outlinedWhite"
key={topic.id}
href={topic.channel_url}
label={topic.name}
/>
))}
</ChipsContainer>
</SubTopicsContainer>
)
)
}

const ChannelPage: React.FC = () => {
const { channelType, name } = useParams<RouteParams>()
const channelQuery = useChannelDetail(String(channelType), String(name))
Expand All @@ -95,12 +42,6 @@ const ChannelPage: React.FC = () => {
{publicDescription && (
<Typography variant="body1">{publicDescription}</Typography>
)}
{channelQuery.data?.channel_type === ChannelTypeEnum.Topic &&
channelQuery.data?.topic_detail?.topic ? (
<SubTopicsDisplay
parentTopicId={channelQuery.data?.topic_detail?.topic}
/>
) : null}
{channelQuery.data?.search_filter && (
<ChannelSearch
channelTitle={channelQuery.data.title}
Expand Down
Loading
Loading