Skip to content

Commit

Permalink
add learning materials tab
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherChudzicki committed Jun 20, 2024
1 parent 46a881c commit 744383a
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 156 deletions.
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
1 change: 1 addition & 0 deletions frontends/mit-open/src/pages/FieldPage/FieldSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ const FieldSearch: React.FC<FieldSearchProps> = ({

<SearchDisplay
page={page}
setSearchParams={setSearchParams}
requestParams={params}
setPage={setPage}
facetManifest={facetManifest}
Expand Down
Loading

0 comments on commit 744383a

Please sign in to comment.