Skip to content

Commit

Permalink
feat: render course listing in paragon carousel
Browse files Browse the repository at this point in the history
  • Loading branch information
brian-smith-tcril committed Mar 22, 2023
1 parent da1c9c2 commit fd2e362
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 14 deletions.
65 changes: 61 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "0.2.0",
"@reduxjs/toolkit": "^1.9.3",
"core-js": "3.27.2",
"prop-types": "15.8.1",
"react": "16.14.0",
"react-dom": "16.14.0",
"react-redux": "7.2.9",
"react-redux": "^7.2.9",
"react-router": "5.3.4",
"react-router-dom": "5.3.4",
"redux": "4.2.1",
Expand Down
17 changes: 17 additions & 0 deletions src/data/configureStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { configureStore } from '@reduxjs/toolkit';
import { setupListeners } from '@reduxjs/toolkit/query';
import { coursesApi } from '../example';

const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[coursesApi.reducerPath]: coursesApi.reducer,
},
// Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(coursesApi.middleware),
});

setupListeners(store.dispatch);

export default store;
42 changes: 42 additions & 0 deletions src/example/ExampleCarousel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import PropTypes from 'prop-types';

import { Carousel } from '@edx/paragon';

const examplePrevIcon = (
<img width="100px" alt="Previous" src="https://raw.githubusercontent.com/brian-smith-tcril/workshop-images/73023da716b7b11723a2eea5d2a76a2ab944c55d/aiga_left_arrow_bg.svg" />
);

const exampleNextIcon = (
<img width="100px" alt="Next" src="https://raw.githubusercontent.com/brian-smith-tcril/workshop-images/73023da716b7b11723a2eea5d2a76a2ab944c55d/aiga_right_arrow_bg.svg" />
);

const ExampleCarousel = ({ carouselData }) => (
<Carousel prevIcon={examplePrevIcon} nextIcon={exampleNextIcon}>
{carouselData.map(carouselEntryData => (
<Carousel.Item>
<div className="img-gradient">
<img
className="d-block w-100"
src={carouselEntryData.imageUrl}
alt={carouselEntryData.imageAltText}
/>
</div>
<Carousel.Caption>
<h1 style={{ color: 'white' }}>{carouselEntryData.title}</h1>
<p>{carouselEntryData.description}</p>
</Carousel.Caption>
</Carousel.Item>
))}
</Carousel>
);

ExampleCarousel.propTypes = {
carouselData: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string,
description: PropTypes.string,
imageUrl: PropTypes.string,
imageAltText: PropTypes.string,
})).isRequired,
};

export default ExampleCarousel;
23 changes: 15 additions & 8 deletions src/example/ExamplePage.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { Container } from '@edx/paragon';
import { useGetCoursesQuery } from './data/coursesApiService';
import ExampleCarousel from './ExampleCarousel';

const ExamplePage = () => (
<main>
<Container className="py-5">
<h1>Example Page</h1>
<p>Hello world!</p>
</Container>
</main>
);
const ExamplePage = () => {
const { data, error, isLoading } = useGetCoursesQuery();

return (
<main>
<Container className="py-5">
{error && <>Oh no, there was an error</>}
{isLoading && <>Loading...</>}
{data && <ExampleCarousel carouselData={data} />}
</Container>
</main>
);
};

export default ExamplePage;
27 changes: 27 additions & 0 deletions src/example/data/coursesApiService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

import { ensureConfig, getConfig } from '@edx/frontend-platform';

ensureConfig(['LMS_BASE_URL'], 'Courses API service');

// Define a service using a base URL and expected endpoints
export const coursesApi = createApi({
reducerPath: 'coursesApi',
baseQuery: fetchBaseQuery({ baseUrl: `${getConfig().LMS_BASE_URL}/api/` }),
endpoints: (builder) => ({
getCourses: builder.query({
query: () => 'courses/v1/courses/',
transformResponse: (response) => response.results.map(entry => ({
title: entry.name,
description: entry.short_description,
imageUrl: entry.media.banner_image.uri_absolute,
imageAltText: 'API does not return alt text for images',
})),
}),
}),
});

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetCoursesQuery } = coursesApi;
2 changes: 2 additions & 0 deletions src/example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { coursesApi } from './data/coursesApiService';
export { default as ExamplePage } from './ExamplePage';
9 changes: 9 additions & 0 deletions src/example/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.img-gradient:after {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: linear-gradient(0deg, rgba(0,0,0,1) 17%, rgba(0,0,0,0) 100%);
}
4 changes: 3 additions & 1 deletion src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import Footer, { messages as footerMessages } from '@edx/frontend-component-foot
import appMessages from './i18n';
import ExamplePage from './example/ExamplePage';

import store from './data/configureStore';

import './index.scss';

subscribe(APP_READY, () => {
ReactDOM.render(
<AppProvider>
<AppProvider store={store}>
<Header />
<ExamplePage />
<Footer />
Expand Down

0 comments on commit fd2e362

Please sign in to comment.