11import React from "react"
22import {
33 useFeaturedLearningResourcesList ,
4- useLearningResourcesList ,
5- useLearningResourcesSearch ,
4+ learningResourcesKeyFactory ,
65} from "api/hooks/learningResources"
6+
77import {
88 Carousel ,
99 TabButton ,
@@ -13,14 +13,10 @@ import {
1313 styled ,
1414 Typography ,
1515} from "ol-components"
16- import type {
17- TabConfig ,
18- ResourceDataSource ,
19- SearchDataSource ,
20- FeaturedDataSource ,
21- } from "./types"
16+ import type { TabConfig , FeaturedDataSource } from "./types"
2217import { LearningResource } from "api"
2318import { ResourceCard } from "../ResourceCard/ResourceCard"
19+ import { useQueries } from "@tanstack/react-query"
2420
2521const StyledCarousel = styled ( Carousel ) ( {
2622 /**
@@ -40,92 +36,13 @@ const StyledCarousel = styled(Carousel)({
4036 } ,
4137} )
4238
43- type DataPanelProps < T extends TabConfig [ "data" ] = TabConfig [ "data" ] > = {
44- dataConfig : T
45- isLoading ?: boolean
46- children : ( {
47- resources,
48- childrenLoading,
49- } : {
50- resources : LearningResource [ ]
51- childrenLoading : boolean
52- } ) => React . ReactNode
53- }
54-
5539type LoadTabButtonProps = {
5640 config : FeaturedDataSource
5741 label : React . ReactNode
5842 key : number
5943 value : string
6044}
6145
62- const ResourcesData : React . FC < DataPanelProps < ResourceDataSource > > = ( {
63- dataConfig,
64- children,
65- } ) => {
66- const { data, isLoading } = useLearningResourcesList ( dataConfig . params )
67- return children ( {
68- resources : data ?. results ?? [ ] ,
69- childrenLoading : isLoading ,
70- } )
71- }
72-
73- const SearchData : React . FC < DataPanelProps < SearchDataSource > > = ( {
74- dataConfig,
75- children,
76- } ) => {
77- const { data, isLoading } = useLearningResourcesSearch ( dataConfig . params )
78- return children ( {
79- resources : data ?. results ?? [ ] ,
80- childrenLoading : isLoading ,
81- } )
82- }
83-
84- const FeaturedData : React . FC < DataPanelProps < FeaturedDataSource > > = ( {
85- dataConfig,
86- children,
87- } ) => {
88- const { data, isLoading } = useFeaturedLearningResourcesList (
89- dataConfig . params ,
90- )
91- return children ( {
92- resources : data ?. results ?? [ ] ,
93- childrenLoading : isLoading ,
94- } )
95- }
96-
97- /**
98- * A wrapper to load data based `TabConfig.data`.
99- *
100- * For each `TabConfig.data.type`, a different API endpoint, and hence
101- * react-query hook, is used. Since hooks can't be called conditionally within
102- * a single component, each type of data is handled in a separate component.
103- */
104- const DataPanel : React . FC < DataPanelProps > = ( {
105- dataConfig,
106- isLoading,
107- children,
108- } ) => {
109- if ( ! isLoading ) {
110- switch ( dataConfig . type ) {
111- case "resources" :
112- return < ResourcesData dataConfig = { dataConfig } > { children } </ ResourcesData >
113- case "lr_search" :
114- return < SearchData dataConfig = { dataConfig } > { children } </ SearchData >
115- case "lr_featured" :
116- return < FeaturedData dataConfig = { dataConfig } > { children } </ FeaturedData >
117- default :
118- // @ts -expect-error This will always be an error if the switch statement
119- // is exhaustive since dataConfig will have type `never`
120- throw new Error ( `Unknown data type: ${ dataConfig . type } ` )
121- }
122- } else
123- return children ( {
124- resources : [ ] ,
125- childrenLoading : true ,
126- } )
127- }
128-
12946/**
13047 * Tab button that loads the resource, so we can determine if it needs to be
13148 * displayed or not. This shouldn't cause double-loading since React Query
@@ -195,48 +112,45 @@ const TabsList = styled(TabButtonList)({
195112
196113type ContentProps = {
197114 resources : LearningResource [ ]
198- isLoading ?: boolean
115+ childrenLoading ?: boolean
199116 tabConfig : TabConfig
200117}
201118
202119type PanelChildrenProps = {
203120 config : TabConfig [ ]
121+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122+ queries : any
204123 children : ( props : ContentProps ) => React . ReactNode
205- isLoading ?: boolean
206124}
207125const PanelChildren : React . FC < PanelChildrenProps > = ( {
208126 config,
127+ queries,
209128 children,
210- isLoading,
211129} ) => {
212130 if ( config . length === 1 ) {
213- return (
214- < DataPanel dataConfig = { config [ 0 ] . data } isLoading = { isLoading } >
215- { ( { resources, childrenLoading } ) =>
216- children ( {
217- resources,
218- isLoading : childrenLoading || isLoading ,
219- tabConfig : config [ 0 ] ,
220- } )
221- }
222- </ DataPanel >
223- )
131+ const { data, isLoading } = queries [ 0 ]
132+ const resources = data ?. results ?? [ ]
133+ return children ( {
134+ resources,
135+ childrenLoading : isLoading ,
136+ tabConfig : config [ 0 ] ,
137+ } )
224138 }
225139 return (
226140 < >
227- { config . map ( ( tabConfig , index ) => (
228- < StyledTabPanel key = { index } value = { index . toString ( ) } >
229- < DataPanel dataConfig = { tabConfig . data } isLoading = { isLoading } >
230- { ( { resources , childrenLoading } ) =>
231- children ( {
232- resources ,
233- isLoading : childrenLoading || isLoading ,
234- tabConfig ,
235- } )
236- }
237- </ DataPanel >
238- </ StyledTabPanel >
239- ) ) }
141+ { config . map ( ( tabConfig , index ) => {
142+ const { data , isLoading } = queries [ index ]
143+ const resources = data ?. results ?? [ ]
144+ return (
145+ < StyledTabPanel key = { index } value = { index . toString ( ) } >
146+ { children ( {
147+ resources ,
148+ childrenLoading : isLoading ,
149+ tabConfig ,
150+ } ) }
151+ </ StyledTabPanel >
152+ )
153+ } ) }
240154 </ >
241155 )
242156}
@@ -258,6 +172,7 @@ type ResourceCarouselProps = {
258172 title : string
259173 className ?: string
260174 isLoading ?: boolean
175+ "data-testid" ?: string
261176}
262177/**
263178 * A tabbed carousel that fetches resources based on the configuration provided.
@@ -275,12 +190,27 @@ const ResourceCarousel: React.FC<ResourceCarouselProps> = ({
275190 title,
276191 className,
277192 isLoading,
193+ "data-testid" : dataTestId ,
278194} ) => {
279195 const [ tab , setTab ] = React . useState ( "0" )
280196 const [ ref , setRef ] = React . useState < HTMLDivElement | null > ( null )
281197
198+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
199+ const queries = useQueries < any > ( {
200+ queries : config . map ( ( tab ) => {
201+ switch ( tab . data . type ) {
202+ case "resources" :
203+ return learningResourcesKeyFactory . list ( tab . data . params )
204+ case "lr_search" :
205+ return learningResourcesKeyFactory . search ( tab . data . params )
206+ case "lr_featured" :
207+ return learningResourcesKeyFactory . featured ( tab . data . params )
208+ }
209+ } ) ,
210+ } )
211+
282212 return (
283- < MobileOverflow className = { className } >
213+ < MobileOverflow className = { className } data-testid = { dataTestId } >
284214 < TabContext value = { tab } >
285215 < HeaderRow >
286216 < HeaderText variant = "h4" > { title } </ HeaderText >
@@ -309,8 +239,8 @@ const ResourceCarousel: React.FC<ResourceCarouselProps> = ({
309239 </ ControlsContainer >
310240 ) : null }
311241 </ HeaderRow >
312- < PanelChildren config = { config } isLoading = { isLoading } >
313- { ( { resources, isLoading : childrenLoading , tabConfig } ) => (
242+ < PanelChildren config = { config } queries = { queries } >
243+ { ( { resources, childrenLoading, tabConfig } ) => (
314244 < StyledCarousel arrowsContainer = { ref } >
315245 { isLoading || childrenLoading
316246 ? Array . from ( { length : 6 } ) . map ( ( _ , index ) => (
0 commit comments