Skip to content

feat: [FC-86] created course list for Catalog page#5

Merged
brian-smith-tcril merged 15 commits intoopenedx:masterfrom
raccoongang:Peter_Kulko/course-course-card-component
Jun 23, 2025
Merged

feat: [FC-86] created course list for Catalog page#5
brian-smith-tcril merged 15 commits intoopenedx:masterfrom
raccoongang:Peter_Kulko/course-course-card-component

Conversation

@PKulkoRaccoonGang
Copy link
Contributor

@PKulkoRaccoonGang PKulkoRaccoonGang commented Jun 4, 2025

Note

  • The Course catalog page will be adapted to the new requirements in subsequent PRs.
  • Displaying the organization logo on the course card will be added in future PRs.
  • This PR has already passed an internal code review.

Description

This PR includes the following changes:

  • added Loader, CourseCard generic components
  • added Course list page content
  • created Course catalog page layout
  • implemented navigation between Course Catalog and Course About pages
  • Updated tests and setupTest.js configuration

Initial setup

  1. Clone and install the tutor-mfe plugin from the draft PR that adds support for Catalog MFE.
  2. Go to http://apps.local.openedx.io:1998/catalog/courses

How Has This Been Tested?

  1. Go to Course Catalog page (http://apps.local.openedx.io:1998/catalog/courses).
  2. When loading the page, a loader (spinner) is displayed.
  3. If there are no courses, an alert is displayed with information about this.

image

  1. If an error occurred while loading the page content - an error message is displayed (message taken from LearnerDashboard MFE).

image

  1. Create a new course from Studio.
  2. Check the display of the course card on the new Course Catalog page.
  3. If there is no course image, the default image is displayed.

image

  1. Through the Schedule and Details page, load the course image and check on the Course Catalog page that the image is displayed.
  2. By clicking on the course card, the New Course About page opens (http://apps.local.openedx.io:1998/catalog/courses/<course_id>/about) without full page reloading (React Router).

Merge Checklist

  • If your update includes visual changes, have they been reviewed by a designer? Send them a link to the Sandbox, if applicable.
  • Is there adequate test coverage for your changes?

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Jun 4, 2025
@openedx-webhooks
Copy link

openedx-webhooks commented Jun 4, 2025

Thanks for the pull request, @PKulkoRaccoonGang!

This repository is currently maintained by @openedx/committers-frontend.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@github-project-automation github-project-automation bot moved this to Needs Triage in Contributions Jun 4, 2025
@PKulkoRaccoonGang PKulkoRaccoonGang changed the title Peter kulko/course course card component feat: course course card component Jun 4, 2025
@PKulkoRaccoonGang PKulkoRaccoonGang self-assigned this Jun 4, 2025
@PKulkoRaccoonGang PKulkoRaccoonGang changed the title feat: course course card component feat: created course list for Catalog page Jun 4, 2025
@codecov
Copy link

codecov bot commented Jun 4, 2025

Codecov Report

Attention: Patch coverage is 98.03922% with 2 lines in your changes missing coverage. Please review.

Project coverage is 95.32%. Comparing base (3b11900) to head (4bbbf81).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/generic/course-card/utils.ts 80.00% 1 Missing ⚠️
src/сatalog/CatalogPage.tsx 95.83% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master       #5      +/-   ##
==========================================
+ Coverage   89.09%   95.32%   +6.23%     
==========================================
  Files          10       25      +15     
  Lines          55      171     +116     
  Branches        0       21      +21     
==========================================
+ Hits           49      163     +114     
- Misses          6        8       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@PKulkoRaccoonGang PKulkoRaccoonGang requested a review from Copilot June 4, 2025 20:45
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Adds reusable loading and course card components, implements the Course Catalog page layout, and wires up navigation and tests.

  • Introduces LoadingSpinner and full-page Loading components to display spinners with proper accessibility.
  • Adds CourseCard component (with utils, styles, and tests) to render course information and images.
  • Implements AlertNotification, updates environment ports, and exports new generics for use in the Catalog page.

Reviewed Changes

Copilot reviewed 43 out of 43 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/generic/loading-spinner/types.ts Defines spinner prop types
src/generic/loading-spinner/messages.ts Adds i18n message for screen-reader text
src/generic/loading-spinner/index.tsx Implements LoadingSpinner and Loading wrapper
src/generic/loading-spinner/LoadingSpinner.test.tsx Tests spinner rendering, sizes, and accessibility
src/generic/index.ts Exports new generic components
src/generic/index.scss Imports styles for course-card and sub-header
src/generic/course-card/utils.ts Builds full image URLs from LMS base
src/generic/course-card/types.ts Declares course and card props types
src/generic/course-card/messages.ts Defines start-date i18n message
src/generic/course-card/index.tsx Renders course cards with images, titles, and dates
src/generic/course-card/constants.ts Holds date formatting options
src/generic/course-card/CourseCard.test.tsx Tests card rendering, links, and image sources
src/generic/course-card/CourseCard.scss Styles course-card layout and responsive behavior
src/generic/alert-notification/types.ts Declares alert notification prop types
src/generic/alert-notification/index.tsx Implements AlertNotification with variant and icon
src/generic/alert-notification/AlertNotification.test.tsx Tests alert rendering and variants
src/mocks/index.ts Re-exports mock data
src/mocks/course.ts Provides mockCourseResponse
.env.test Updates test BASE_URL port
.env.development Updates development PORT and BASE_URL
Comments suppressed due to low confidence (4)

src/generic/course-card/index.tsx:24

  • The route omits the /catalog prefix used elsewhere (/catalog/courses/<id>/about). Consider updating to /catalog/courses/${course.id}/about to keep navigation consistent.
to={`/courses/${course.id}/about`}

src/generic/alert-notification/types.ts:2

  • Using a general string for variant reduces type safety. Consider restricting it to known alert variants (e.g., 'info' | 'warning' | 'success' | 'danger').
variant: string;

src/generic/loading-spinner/messages.ts:5

  • [nitpick] The message id 'category.generic.loading' is inconsistent with other namespaces (e.g., 'generic.course-card.start-date'). Align ids to a common convention for clarity.
id: 'category.generic.loading',

src/generic/course-card/index.tsx:32

  • There’s no test case covering the scenario when orgImg is missing to ensure the fallback logo is rendered. Consider adding a test for that branch.
logoSrc={course.data.orgImg ? getFullImageUrl(course.data.orgImg) : undefined}

@PKulkoRaccoonGang PKulkoRaccoonGang marked this pull request as ready for review June 5, 2025 08:08
@PKulkoRaccoonGang PKulkoRaccoonGang changed the title feat: created course list for Catalog page feat: [FC-86] created course list for Catalog page Jun 5, 2025
it('navigates to /courses route', () => {
renderWithProviders('/courses');
// eslint-disable-next-line no-underscore-dangle
expect(window._testHistory).toContain('/courses');
Copy link

@diana-villalvazo-wgu diana-villalvazo-wgu Jun 6, 2025

Choose a reason for hiding this comment

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

can we check courses content instead? it seems this test will never fail since is checking the path you are providing and setting inside renderWithProviders, with the routes you defined there, not really checking index.tsx routing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that would be better. Corrected in b5055e1

render,
renderHook,
within,
waitFor,

Choose a reason for hiding this comment

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

should we convert this into a ts file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Converted in faf9d98

@mphilbrick211 mphilbrick211 moved this from Needs Triage to In Eng Review in Contributions Jun 12, 2025
@mphilbrick211 mphilbrick211 added the FC Relates to an Axim Funded Contribution project label Jun 12, 2025
Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

Overall this looks great!

I left a few comments - nothing super major, mostly questions about hardcoded spacing.

@PKulkoRaccoonGang PKulkoRaccoonGang force-pushed the Peter_Kulko/course-course-card-component branch from a1ec006 to d74d889 Compare June 13, 2025 08:49
@PKulkoRaccoonGang
Copy link
Contributor Author

@brian-smith-tcril @diana-villalvazo-wgu I have rebased this PR and added the suggested changes after the review. Please take a look.

Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

I pulled this down and was able to test it against tutor with a couple changes:

  • Tutor doesn't know about this MFE so I just used port 2000 as a workaround, it'd be great to add a tutor plugin to the docs to avoid the need to do this
  • Styles weren't loading at all because the version of frontend-platform this MFE is using doesn't include openedx/frontend-platform#806, so I needed to bump the version

I left some specific comments, but the more I look at this PR the more I feel there's a bigger picture question to ask: do we need override so much stuff?

A lot of the styles in this PR are overriding Paragon defaults - do we need to do that?

The example Card component from the "Card status" example on the Paragon docs site looks quite close to what the overrides are doing here, and using the design system as designed feels better to me than overriding things left and right.

I think using more "out of the box" Paragon components would lead to a much more maintainable MFE.

@brian-smith-tcril
Copy link
Contributor

More context on the "big picture" question I raised here:

From Slack (https://openedx.slack.com/archives/C08QR8K7K38/p1749830688812029):

Sarina Canelake: I'd rather us use default components than override just to match pixel perfect. The point of this project was to leverage Paragon.

Brian Smith: Awesome, glad to hear we're on the same page

Sarina Canelake: Have a design that was approximately the same as the previous design, but modernized using default Paragon to look the same as the rest of the site.

@PKulkoRaccoonGang
Copy link
Contributor Author

PKulkoRaccoonGang commented Jun 16, 2025

@brian-smith-tcril thank you for bringing this to my attention 💯

Sarina Canelake: I'd rather us use default components than override just to match pixel perfect. The point of this project was to leverage Paragon.

Brian Smith: Awesome, glad to hear we're on the same page

Sarina Canelake: Have a design that was approximately the same as the previous design, but modernized using default Paragon to look the same as the rest of the site.

I support using Paragon components "out of the box" without adding unnecessary styles (where possible). This is definitely a good approach! Removed all styles that were added to match the Figma layout.

Tutor doesn't know about this MFE so I just used port 2000 as a workaround, it'd be great to add a tutor plugin to the docs to avoid the need to do this

Good point! I added a small installation guide using the draft PR for tutor-mfe, which adds the Catalog MFE. We have already tried deploying with the new Catalog MFE and it works.

Styles weren't loading at all because the version of frontend-platform this MFE is using doesn't include openedx/frontend-platform#806, so I needed to bump the version

I upgraded the frontend-platform to 8.4.0 and fixed the error that started appearing with @edx/browserslist-config

expect(getByText(mockCourseResponse.data.content.displayName)).toBeInTheDocument();
expect(getByText(mockCourseResponse.data.org)).toBeInTheDocument();
expect(getByText('Starts: Apr 1, 2024')).toBeInTheDocument();
});
Copy link

@diana-villalvazo-wgu diana-villalvazo-wgu Jun 16, 2025

Choose a reason for hiding this comment

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

Optional: RTL generally recommend using the screen helper, it's shorter and helps if you also appreciate the shorter syntax and less need to pick which selectors to define, so instead of

const { getByText } = renderComponent();
expect( getByText(...) ).toBlah()

they encourage to do:

import { ..., screen } from '../setupTest';

renderComponent();
expect( screen.getByText(...) ).toBlah()

Again this is optional, not a requirement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If RTL generally recommend, then I also support this idea 💯
Added screen for tests in this PR (other tests will be updated iteratively, when adding subsequent PRs)

Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

Overall this is looking great using default Paragon styles!

I left one small comment about the use of jpegs for the no course image.

When testing, it also felt to me like the margin between the course image and the card header was too big. This is a default paragon thing, so I created an issue to discuss it openedx/paragon#3660

Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be possible to use SVGs for this instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced, good idea

@brian-smith-tcril
Copy link
Contributor

I played around with the card a bit more and I think the issue isn't so much that there's too much margin above the header, but that the card overall doesn't feel balanced.

I tried moving the start date from the footer into a section as a quick test, and while it doesn't look perfect it does feel more balanced to me

image

diff --git a/src/generic/course-card/index.tsx b/src/generic/course-card/index.tsx
index 07fe49d..564f3ec 100644
--- a/src/generic/course-card/index.tsx
+++ b/src/generic/course-card/index.tsx
@@ -38,12 +38,13 @@ export const CourseCard = ({ course }: CourseCardProps) => {
         subtitle={course.data.org}
       />
       {formattedDate && (
-        <Card.Footer className="justify-content-start">
+        <Card.Section className="justify-content-start">
           {intl.formatMessage(messages.startDate, {
             startDate: formattedDate,
           })}
-        </Card.Footer>
+        </Card.Section>
       )}
+      <Card.Footer />
     </Card>
   );
 };

@brian-smith-tcril
Copy link
Contributor

Tried another thing

image

diff --git a/src/generic/course-card/index.tsx b/src/generic/course-card/index.tsx
index 07fe49d..20af8bf 100644
--- a/src/generic/course-card/index.tsx
+++ b/src/generic/course-card/index.tsx
@@ -35,15 +35,18 @@ export const CourseCard = ({ course }: CourseCardProps) => {
       />
       <Card.Header
         title={course.data.content.displayName}
-        subtitle={course.data.org}
       />
-      {formattedDate && (
-        <Card.Footer className="justify-content-start">
+      <Card.Section>
+        <div>{course.data.org}</div>
+        {formattedDate && (
+        <div>
           {intl.formatMessage(messages.startDate, {
             startDate: formattedDate,
           })}
-        </Card.Footer>
-      )}
+        </div>
+        )}
+      </Card.Section>
+      <Card.Footer />
     </Card>
   );
 };

I don't think either this or the previous diff I tried are good enough to ship, but it does seem like there are ways to make the card look nice without overriding Paragon styles.

I think getting some some design input on this one would be helpful.

@PKulkoRaccoonGang
Copy link
Contributor Author

@brian-smith-tcril thanks for looking into this 💯 Do I need to add the "workarounds" you suggested to improve the Paragon Card display? Or do we want to resolve this issue within Paragon, after getting feedback from the designer/others, without blocking this PR?

@brian-smith-tcril
Copy link
Contributor

@PKulkoRaccoonGang I think moving the start date out of the card footer should happen in this PR.

Either of the diffs I commented earlier would be fine, but another option (that would likely provide even more flexibility) would be to not use a Card.Header at all, and instead follow a pattern like https://paragon-openedx.netlify.app/components/card/#card-content-block-empty where all of the card content is in one Card.Section.

After that, we can create a new issue on this repo to determine a final design (ideally without needing to change Paragon).

@PKulkoRaccoonGang
Copy link
Contributor Author

@brian-smith-tcril Thanks for clarification!

Currently the card looks like this:

image

Moved content from Card.Header, Card.Footer to Card.Section in this commit: e498645

Created a new issue for Determination the final design for the course Card component: #10

Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

I tested this locally and it's looking great! One last comment, but after that I think this should be good to merge!

Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

LGTM!

Thank you so much for working through the review process with me! I'm very happy with how this turned out!

@brian-smith-tcril brian-smith-tcril merged commit 2552216 into openedx:master Jun 23, 2025
6 checks passed
@github-project-automation github-project-automation bot moved this from In Eng Review to Done in Contributions Jun 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FC Relates to an Axim Funded Contribution project open-source-contribution PR author is not from Axim or 2U

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

7 participants