Skip to content

Commit

Permalink
add learning materials tab (#1132)
Browse files Browse the repository at this point in the history
* add learning materials tab

* update navbar links
  • Loading branch information
ChristopherChudzicki authored Jun 21, 2024
1 parent 599654e commit 206823b
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 169 deletions.

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 5 additions & 4 deletions frontends/mit-open/src/page-components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,14 @@ const navData: NavData = {
title: "Courses",
icon: "/static/images/navdrawer/courses.svg",
description: "Learn with MIT instructors",
href: querifiedSearchUrl({ resource_type: "course" }),
href: querifiedSearchUrl({ tab: "courses" }),
},
{
title: "Programs",
icon: "/static/images/navdrawer/programs.svg",
description:
"Learn in-depth from a series of courses and earn a certificate",
href: querifiedSearchUrl({ resource_type: "program" }),
href: querifiedSearchUrl({ tab: "programs" }),
},
{
title: "Pathways",
Expand All @@ -169,10 +169,11 @@ const navData: NavData = {
"Achieve your learning goals with a curated collection of courses",
},
{
title: "Course Materials",
icon: "/static/images/navdrawer/course_materials.svg",
title: "Learning Materials",
icon: "/static/images/navdrawer/learning_materials.svg",
description:
"Free teaching and learning materials including videos, podcasts, lecture notes, etc.",
href: querifiedSearchUrl({ tab: "learning-materials" }),
},
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from "react"
import React, { useMemo } from "react"
import {
TabButton,
TabContext,
TabButtonList,
TabPanel,
styled,
} from "ol-components"
import type { ResourceTypeEnum, LearningResourceSearchResponse } from "api"
import { ResourceTypeEnum, LearningResourceSearchResponse } from "api"

const TabsList = styled(TabButtonList)({
".MuiTabScrollButton-root.Mui-disabled": {
Expand All @@ -19,8 +19,10 @@ const CountSpan = styled.span`
text-align: left;
`
type TabConfig = {
resource_type: ResourceTypeEnum
label: string
name: string
defaultTab?: boolean
resource_type: ResourceTypeEnum[]
}

type Aggregations = LearningResourceSearchResponse["metadata"]["aggregations"]
Expand All @@ -29,14 +31,14 @@ const resourceTypeCounts = (aggregations?: Aggregations) => {
const buckets = aggregations?.resource_type ?? []
const counts = buckets.reduce(
(acc, bucket) => {
acc[bucket.key] = bucket.doc_count
acc[bucket.key as ResourceTypeEnum] = bucket.doc_count
return acc
},
{} as Record<string, number>,
{} as Record<ResourceTypeEnum, number>,
)
return counts
}
const appendCount = (label: string, count?: number) => {
const appendCount = (label: string, count?: number | null) => {
if (Number.isFinite(count)) {
return (
<>
Expand All @@ -51,53 +53,61 @@ const appendCount = (label: string, count?: number) => {
*
*/
const ResourceTypesTabContext: React.FC<{
resourceType?: ResourceTypeEnum
activeTabName: string
children: React.ReactNode
}> = ({ resourceType, children }) => {
const tab = resourceType ?? "all"
return <TabContext value={tab}>{children}</TabContext>
}> = ({ activeTabName, children }) => {
return <TabContext value={activeTabName}>{children}</TabContext>
}

type ResourceTypeTabsProps = {
aggregations?: Aggregations
tabs: TabConfig[]
patchParams: ({
resource_type,
}: {
resource_type: ResourceTypeEnum[]
}) => void
onTabChange?: (tab: ResourceTypeEnum | "all") => void
setSearchParams: (fn: (prev: URLSearchParams) => URLSearchParams) => void
onTabChange?: () => void
className?: string
}
const ResourceTypeTabList: React.FC<ResourceTypeTabsProps> = ({
tabs,
aggregations,
patchParams,
setSearchParams,
onTabChange,
className,
}) => {
const counts = resourceTypeCounts(aggregations)
const allCount = counts
? tabs.reduce((acc, tab) => acc + (counts[tab.resource_type] ?? 0), 0)
: undefined
const withCounts = useMemo(() => {
const counts = resourceTypeCounts(aggregations)
return tabs.map((t) => {
const resourceTypes =
t.resource_type.length === 0
? Object.values(ResourceTypeEnum)
: t.resource_type
const count = counts
? resourceTypes
.map((rt) => counts[rt] ?? 0)
.reduce((acc, c) => acc + c, 0)
: null
return { ...t, label: appendCount(t.label, count) }
})
}, [tabs, aggregations])
return (
<TabsList
className={className}
onChange={(_e, value) => {
patchParams({ resource_type: value === "all" ? [] : [value] })
onTabChange?.(value)
const tab = tabs.find((t) => t.name === value)
if (!tab) return
setSearchParams((prev) => {
const next = new URLSearchParams(prev)
if (tab.defaultTab) {
next.delete("tab")
} else {
next.set("tab", value)
}
return next
})
onTabChange?.()
}}
>
<TabButton value="all" label={appendCount("All", allCount)} />
{tabs.map((t) => {
const count = counts ? counts[t.resource_type] ?? 0 : undefined
return (
<TabButton
key={t.resource_type}
value={t.resource_type}
label={appendCount(t.label, count)}
/>
)
{withCounts.map((t) => {
return <TabButton key={t.name} value={t.name} label={t.label} />
})}
</TabsList>
)
Expand All @@ -109,9 +119,8 @@ const ResourceTypeTabPanels: React.FC<{
}> = ({ tabs, children }) => {
return (
<>
<TabPanel value="all">{children}</TabPanel>
{tabs.map((t) => (
<TabPanel key={t.resource_type} value={t.resource_type}>
<TabPanel key={t.name} value={t.name}>
{children}
</TabPanel>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import ProfessionalToggle from "./ProfessionalToggle"
import type { TabConfig } from "./ResourceTypeTabs"

import { ResourceListCard } from "../ResourceCard/ResourceCard"
import { useSearchParams } from "@mitodl/course-search-utils/react-router"

export const StyledDropdown = styled(SimpleSelect)`
margin-left: 8px;
Expand Down Expand Up @@ -426,20 +427,27 @@ export const getLastPage = (count: number): number => {

export const TABS: TabConfig[] = [
{
label: "Courses",
resource_type: ResourceTypeEnum.Course,
name: "all",
label: "All",
defaultTab: true,
resource_type: [],
},
{
label: "Programs",
resource_type: ResourceTypeEnum.Program,
name: "courses",
label: "Courses",
resource_type: [ResourceTypeEnum.Course],
},
{
label: "Videos",
resource_type: ResourceTypeEnum.Video,
name: "programs",
label: "Programs",
resource_type: [ResourceTypeEnum.Program],
},
{
label: "Podcasts",
resource_type: ResourceTypeEnum.Podcast,
name: "learning-materials",
label: "Learning Materials",
resource_type: Object.values(ResourceTypeEnum).filter(
(v) => v !== ResourceTypeEnum.Course && v !== ResourceTypeEnum.Program,
),
},
]
export const ALL_RESOURCE_TABS = TABS.map((t) => t.resource_type)
Expand Down Expand Up @@ -474,8 +482,24 @@ interface SearchDisplayProps {
setParamValue: UseResourceSearchParamsResult["setParamValue"]
clearAllFacets: UseResourceSearchParamsResult["clearAllFacets"]
toggleParamValue: UseResourceSearchParamsResult["toggleParamValue"]
patchParams: UseResourceSearchParamsResult["patchParams"]
showProfessionalToggle?: boolean
/**
* NOTE: This is passed from parent, rather than obtained via useSearchParams,
* because of quirks with react-router's useSearchParams hook.
*
* Multiple calls to React Router's useSearchParam hook do not use current
* values for the search params.
* See https://github.com/remix-run/react-router/issues/9757 for details.
*
* This is partially addressed by `@mitodl/course-search-utils`, which exports
* a wrapper around `useSearchParams`: subsequent calls to `setSearchParams`
* DO use the current value, with one caveat: The setSearchParams function
* must be from the same "instance" of `useSearchParams`.
*
* Because of this, we pass the setSearchParams function from the parent
* rather than from a new "instance" of `useSearchParams`.
*/
setSearchParams: UseResourceSearchParamsProps["setSearchParams"]
}

const SearchDisplay: React.FC<SearchDisplayProps> = ({
Expand All @@ -489,19 +513,32 @@ const SearchDisplay: React.FC<SearchDisplayProps> = ({
setParamValue,
clearAllFacets,
toggleParamValue,
patchParams,
showProfessionalToggle,
setSearchParams,
}) => {
const [searchParams] = useSearchParams()
const activeTab =
TABS.find((t) => t.name === searchParams.get("tab")) ??
TABS.find((t) => t.defaultTab) ??
TABS[0]
const allParams = useMemo(() => {
return { ...constantSearchParams, ...requestParams }
}, [requestParams, constantSearchParams])

const { data, isLoading } = useLearningResourcesSearch(
{
...(allParams as LRSearchRequest),
return {
...constantSearchParams,
resource_type: activeTab.resource_type,
...requestParams,
aggregations: facetNames as LRSearchRequest["aggregations"],
offset: (page - 1) * PAGE_SIZE,
},
}
}, [
requestParams,
constantSearchParams,
activeTab?.resource_type,
facetNames,
page,
])

const { data, isLoading } = useLearningResourcesSearch(
allParams as LRSearchRequest,
{ keepPreviousData: true },
)

Expand All @@ -518,7 +555,7 @@ const SearchDisplay: React.FC<SearchDisplayProps> = ({
<ProfessionalToggle
professionalSetting={requestParams.professional}
setParamValue={setParamValue}
></ProfessionalToggle>
/>
)}
<AvailableFacets
facetManifest={facetManifest}
Expand Down Expand Up @@ -548,9 +585,7 @@ const SearchDisplay: React.FC<SearchDisplayProps> = ({
return (
<Container>
<StyledGridContainer>
<ResourceTypeTabs.Context
resourceType={requestParams.resource_type?.[0]}
>
<ResourceTypeTabs.Context activeTabName={activeTab.name}>
<DesktopFiltersColumn
variant="sidebar-2"
data-testid="facets-container"
Expand All @@ -576,7 +611,7 @@ const SearchDisplay: React.FC<SearchDisplayProps> = ({
<StyledMainColumn variant="main-2">
<DesktopSortContainer>{sortDropdown}</DesktopSortContainer>
<StyledResourceTabs
patchParams={patchParams}
setSearchParams={setSearchParams}
tabs={TABS}
aggregations={data?.metadata.aggregations}
onTabChange={() => setPage(1)}
Expand Down
3 changes: 1 addition & 2 deletions frontends/mit-open/src/pages/FieldPage/FieldSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ const FieldSearch: React.FC<FieldSearchProps> = ({
setParamValue,
clearAllFacets,
toggleParamValue,
patchParams,
currentText,
setCurrentText,
setCurrentTextAndQuery,
Expand Down Expand Up @@ -194,6 +193,7 @@ const FieldSearch: React.FC<FieldSearchProps> = ({

<SearchDisplay
page={page}
setSearchParams={setSearchParams}
requestParams={params}
setPage={setPage}
facetManifest={facetManifest}
Expand All @@ -203,7 +203,6 @@ const FieldSearch: React.FC<FieldSearchProps> = ({
setParamValue={setParamValue}
clearAllFacets={clearAllFacets}
toggleParamValue={toggleParamValue}
patchParams={patchParams}
showProfessionalToggle={
SHOW_PROFESSIONAL_TOGGLE_BY_CHANNEL_TYPE[channelType]
}
Expand Down
Loading

0 comments on commit 206823b

Please sign in to comment.