richie-education 2.28.0 → 2.28.2-dev23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/i18n/locales/fr-FR.json +10 -10
  2. package/js/components/AddressesManagement/AddressForm/validationSchema.spec.ts +2 -2
  3. package/js/components/PaymentInterfaces/LyraPopIn.tsx +10 -3
  4. package/js/components/PaymentInterfaces/types.ts +1 -1
  5. package/js/components/PurchaseButton/index.spec.tsx +9 -9
  6. package/js/components/PurchaseButton/index.tsx +2 -1
  7. package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +13 -3
  8. package/js/components/SaleTunnel/hooks/useTerms.tsx +2 -2
  9. package/js/components/SaleTunnel/index.credential.spec.tsx +2 -2
  10. package/js/components/SaleTunnel/index.full-process.spec.tsx +11 -4
  11. package/js/components/SaleTunnel/index.spec.tsx +2 -2
  12. package/js/components/SaleTunnel/index.stories.tsx +3 -2
  13. package/js/components/SaleTunnel/index.tsx +2 -2
  14. package/js/translations/fr-FR.json +1 -1
  15. package/js/types/commonDataProps.ts +0 -1
  16. package/js/types/index.ts +6 -0
  17. package/js/utils/CourseRunHelper/index.spec.ts +35 -0
  18. package/js/utils/CourseRunHelper/index.ts +13 -0
  19. package/js/utils/react-query/useSessionQuery/index.ts +1 -1
  20. package/js/utils/test/factories/richie.ts +16 -2
  21. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +35 -5
  22. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +11 -10
  23. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +3 -2
  24. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +32 -30
  25. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +5 -6
  26. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +3 -2
  27. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.login.spec.tsx +5 -3
  28. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.logout.spec.tsx +5 -3
  29. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.tsx +2 -2
  30. package/js/widgets/SyllabusCourseRunsList/components/SyllabusAsideList/index.tsx +11 -4
  31. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.tsx +20 -9
  32. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.tsx +111 -0
  33. package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +311 -15
  34. package/js/widgets/SyllabusCourseRunsList/index.tsx +24 -8
  35. package/package.json +44 -44
  36. package/tsconfig.json +1 -1
@@ -3,6 +3,7 @@ import {
3
3
  findByRole,
4
4
  getByRole,
5
5
  getByText,
6
+ queryByText,
6
7
  queryByRole,
7
8
  screen,
8
9
  within,
@@ -15,13 +16,14 @@ import { faker } from '@faker-js/faker';
15
16
  import {
16
17
  CourseRunFactoryFromPriority,
17
18
  RichieContextFactory as mockRichieContextFactory,
19
+ PacedCourseFactory,
18
20
  UserFactory,
19
21
  } from 'utils/test/factories/richie';
20
22
  import SyllabusCourseRunsList from 'widgets/SyllabusCourseRunsList/index';
21
23
  import { createTestQueryClient } from 'utils/test/createTestQueryClient';
22
24
  import { CourseRun, Priority } from 'types';
23
25
  import { CourseProductRelation } from 'types/Joanie';
24
- import { CourseLightFactory, CourseProductRelationFactory } from 'utils/test/factories/joanie';
26
+ import { CourseProductRelationFactory } from 'utils/test/factories/joanie';
25
27
  import { DEFAULT_DATE_FORMAT } from 'hooks/useDateFormat';
26
28
  import { StringHelper } from 'utils/StringHelper';
27
29
  import { computeStates } from 'utils/CourseRuns';
@@ -131,6 +133,83 @@ describe('<SyllabusCourseRunsList/>', () => {
131
133
  });
132
134
  };
133
135
 
136
+ const expectFullDates = (container: HTMLElement, courseRun: CourseRun) => {
137
+ [courseRun] = computeStates([courseRun]);
138
+ const intl = createIntl({ locale: 'en' });
139
+ const heading = getByRole(container, 'heading', {
140
+ name: courseRun.title,
141
+ });
142
+ const runContainer = heading.parentNode! as HTMLElement;
143
+
144
+ const enrollmentNode = getByText(runContainer, 'Enrollment');
145
+
146
+ const enrollmentDatesContainer = enrollmentNode.nextSibling!;
147
+ const enrollmentStart = intl.formatDate(
148
+ new Date(courseRun.enrollment_start),
149
+ DEFAULT_DATE_FORMAT,
150
+ );
151
+ const enrollmentEnd = intl.formatDate(new Date(courseRun.enrollment_end), DEFAULT_DATE_FORMAT);
152
+ expect(enrollmentDatesContainer.textContent).toEqual(
153
+ `From ${enrollmentStart} to ${enrollmentEnd}`,
154
+ );
155
+
156
+ const courseNode = enrollmentDatesContainer.nextSibling!;
157
+ expect(courseNode.textContent).toEqual('Course');
158
+
159
+ const start = intl.formatDate(new Date(courseRun.start), DEFAULT_DATE_FORMAT);
160
+ const end = intl.formatDate(new Date(courseRun.end), DEFAULT_DATE_FORMAT);
161
+
162
+ const datesContainer = courseNode.nextSibling!;
163
+ expect(datesContainer.textContent).toEqual(`From ${start} to ${end}`);
164
+ };
165
+
166
+ const expectCompactedDates = (container: HTMLElement, courseRun: CourseRun) => {
167
+ [courseRun] = computeStates([courseRun]);
168
+ const intl = createIntl({ locale: 'en' });
169
+ const heading = getByRole(container, 'heading', {
170
+ name: courseRun.title,
171
+ });
172
+ const runContainer = heading.parentNode! as HTMLElement;
173
+ const courseDatesText = courseRun.end
174
+ ? `Available until ${intl.formatDate(new Date(courseRun.end), DEFAULT_DATE_FORMAT)}`
175
+ : `Available`;
176
+
177
+ const courseDatesContainer = getByText(runContainer, courseDatesText);
178
+ expect(courseDatesContainer).not.toBeNull();
179
+
180
+ getByRole(runContainer, 'link', {
181
+ name: StringHelper.capitalizeFirst(courseRun.state.call_to_action)!,
182
+ });
183
+ };
184
+
185
+ const expectLanguageVisibility = (
186
+ container: HTMLElement,
187
+ courseRun: CourseRun,
188
+ isLanguagesVisible: boolean,
189
+ ) => {
190
+ [courseRun] = computeStates([courseRun]);
191
+ const intl = createIntl({ locale: 'en' });
192
+ const heading = getByRole(container, 'heading', {
193
+ name: courseRun.title,
194
+ });
195
+
196
+ const runContainer = heading.parentNode! as HTMLElement;
197
+
198
+ const languagesNode = queryByText(runContainer, 'Languages');
199
+ if (isLanguagesVisible) {
200
+ expect(languagesNode).not.toBeNull();
201
+
202
+ const languagesContainer = languagesNode?.nextSibling! as HTMLElement;
203
+ getByText(languagesContainer, IntlHelper.getLocalizedLanguages(courseRun.languages, intl));
204
+ } else {
205
+ expect(languagesNode).toBeNull();
206
+ }
207
+
208
+ getByRole(runContainer, 'link', {
209
+ name: StringHelper.capitalizeFirst(courseRun.state.call_to_action)!,
210
+ });
211
+ };
212
+
134
213
  const expectCourseProduct = async (container: HTMLElement, relation: CourseProductRelation) => {
135
214
  const heading = await findByRole(container, 'heading', {
136
215
  name: relation.product.title,
@@ -145,7 +224,7 @@ describe('<SyllabusCourseRunsList/>', () => {
145
224
  };
146
225
 
147
226
  it('has no opened course run', async () => {
148
- const course = CourseLightFactory().one();
227
+ const course = PacedCourseFactory().one();
149
228
  const courseRuns = [
150
229
  CourseRunFactoryFromPriority(Priority.FUTURE_NOT_YET_OPEN)().one(),
151
230
  CourseRunFactoryFromPriority(Priority.FUTURE_CLOSED)({
@@ -182,7 +261,7 @@ describe('<SyllabusCourseRunsList/>', () => {
182
261
  });
183
262
 
184
263
  it('has one opened course run', async () => {
185
- const course = CourseLightFactory().one();
264
+ const course = PacedCourseFactory().one();
186
265
  const courseRuns = [
187
266
  CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)().one(),
188
267
  CourseRunFactoryFromPriority(Priority.FUTURE_CLOSED)({
@@ -221,7 +300,7 @@ describe('<SyllabusCourseRunsList/>', () => {
221
300
  });
222
301
 
223
302
  it('has one forever open course run', async () => {
224
- const course = CourseLightFactory().one();
303
+ const course = PacedCourseFactory().one();
225
304
  const startDate = faker.date.past();
226
305
  const enrollmentStartDate = faker.date.past();
227
306
  const courseRun = CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
@@ -263,7 +342,7 @@ describe('<SyllabusCourseRunsList/>', () => {
263
342
  );
264
343
  });
265
344
  it('has multiple opened course run', async () => {
266
- const course = CourseLightFactory().one();
345
+ const course = PacedCourseFactory().one();
267
346
  const courseRuns = [
268
347
  CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)().one(),
269
348
  CourseRunFactoryFromPriority(Priority.FUTURE_OPEN)().one(),
@@ -302,7 +381,7 @@ describe('<SyllabusCourseRunsList/>', () => {
302
381
  });
303
382
 
304
383
  it('has one opened product', async () => {
305
- const course = CourseLightFactory().one();
384
+ const course = PacedCourseFactory().one();
306
385
  const relation = CourseProductRelationFactory().one();
307
386
  const resourceLink = `https://joanie.endpoint/api/v1.0/courses/${course.code}/products/${relation.product.id}/`;
308
387
  fetchMock.get(resourceLink, relation);
@@ -333,7 +412,7 @@ describe('<SyllabusCourseRunsList/>', () => {
333
412
  });
334
413
 
335
414
  it('renders a specific title in portal when there is one opened course run', () => {
336
- const course = CourseLightFactory().one();
415
+ const course = PacedCourseFactory().one();
337
416
 
338
417
  render(
339
418
  <SyllabusCourseRunsList
@@ -356,7 +435,7 @@ describe('<SyllabusCourseRunsList/>', () => {
356
435
  });
357
436
 
358
437
  it('renders a specific title in portal when there are multiple opened course run', () => {
359
- const course = CourseLightFactory().one();
438
+ const course = PacedCourseFactory().one();
360
439
  render(
361
440
  <SyllabusCourseRunsList
362
441
  courseRuns={[
@@ -381,7 +460,7 @@ describe('<SyllabusCourseRunsList/>', () => {
381
460
  });
382
461
 
383
462
  it('renders different categories correctly', async () => {
384
- const course = CourseLightFactory().one();
463
+ const course = PacedCourseFactory().one();
385
464
  const courseRuns = [
386
465
  CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)().one(),
387
466
  CourseRunFactoryFromPriority(Priority.FUTURE_OPEN)().one(),
@@ -445,7 +524,7 @@ describe('<SyllabusCourseRunsList/>', () => {
445
524
  });
446
525
 
447
526
  it('renders an opened course run with an existing LMS Backend', () => {
448
- const course = CourseLightFactory().one();
527
+ const course = PacedCourseFactory().one();
449
528
  const courseRun = CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)().one();
450
529
  courseRun.resource_link = 'https://openedx.endpoint' + courseRun.resource_link;
451
530
 
@@ -472,7 +551,7 @@ describe('<SyllabusCourseRunsList/>', () => {
472
551
  });
473
552
 
474
553
  it('limits the amount of archived course runs displayed', async () => {
475
- const course = CourseLightFactory().one();
554
+ const course = PacedCourseFactory().one();
476
555
  const courseRuns = [...Array(MAX_ARCHIVED_COURSE_RUNS * 2)]
477
556
  .map(() => CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)().one())
478
557
  .sort((a, b) => Date.parse(a.start) - Date.parse(b.start));
@@ -516,7 +595,7 @@ describe('<SyllabusCourseRunsList/>', () => {
516
595
  });
517
596
 
518
597
  it('does not limit the amount of archived course runs displayed', async () => {
519
- const course = CourseLightFactory().one();
598
+ const course = PacedCourseFactory().one();
520
599
 
521
600
  const courseRuns = [...Array(MAX_ARCHIVED_COURSE_RUNS - 1)]
522
601
  .map(() => CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)().one())
@@ -545,7 +624,7 @@ describe('<SyllabusCourseRunsList/>', () => {
545
624
  });
546
625
 
547
626
  it('renders opened runs with the same locale as the user above', async () => {
548
- const course = CourseLightFactory().one();
627
+ const course = PacedCourseFactory().one();
549
628
 
550
629
  const refDate = faker.date.future();
551
630
  const futureDate = (days: number) => {
@@ -614,8 +693,225 @@ describe('<SyllabusCourseRunsList/>', () => {
614
693
  expectCourseRunOpened(elements[4], courseRuns[4]);
615
694
  });
616
695
 
696
+ it('renders instructor pace opened run with same languages', async () => {
697
+ const course = PacedCourseFactory({ is_self_paced: false }).one();
698
+
699
+ const courseRuns: CourseRun[] = [
700
+ CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
701
+ languages: ['en'],
702
+ }).one(),
703
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
704
+ languages: ['en'],
705
+ }).one(),
706
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
707
+ languages: ['en'],
708
+ }).one(),
709
+ ];
710
+
711
+ render(
712
+ <SyllabusCourseRunsList
713
+ courseRuns={courseRuns}
714
+ course={course}
715
+ maxArchivedCourseRuns={MAX_ARCHIVED_COURSE_RUNS}
716
+ />,
717
+ {
718
+ queryOptions: { client: createTestQueryClient({ user: null }) },
719
+ },
720
+ );
721
+
722
+ // Header of the opened run.
723
+ expect(getHeaderContainer().querySelectorAll('.course-detail__run-descriptions').length).toBe(
724
+ 1,
725
+ );
726
+
727
+ // Assert that the run displays the extended dates.
728
+ expectFullDates(getHeaderContainer(), courseRuns[0]);
729
+
730
+ // Assert that the run does not display the containers related to languages.
731
+ expectLanguageVisibility(getHeaderContainer(), courseRuns[0], false);
732
+
733
+ const portalContainer = getPortalContainer();
734
+
735
+ // Expect that all closed course runs to be present.
736
+ courseRuns.slice(1).forEach((run) => expectCourseRunInList(portalContainer, run));
737
+ });
738
+
739
+ it('renders instructor pace opened run with different languages', async () => {
740
+ const course = PacedCourseFactory({ is_self_paced: false }).one();
741
+
742
+ const courseRuns: CourseRun[] = [
743
+ CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
744
+ languages: ['en'],
745
+ }).one(),
746
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
747
+ languages: ['it'],
748
+ }).one(),
749
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
750
+ languages: ['fr'],
751
+ }).one(),
752
+ ];
753
+
754
+ render(
755
+ <SyllabusCourseRunsList
756
+ courseRuns={courseRuns}
757
+ course={course}
758
+ maxArchivedCourseRuns={MAX_ARCHIVED_COURSE_RUNS}
759
+ />,
760
+ {
761
+ queryOptions: { client: createTestQueryClient({ user: null }) },
762
+ },
763
+ );
764
+
765
+ // Header of the opened run.
766
+ expect(getHeaderContainer().querySelectorAll('.course-detail__run-descriptions').length).toBe(
767
+ 1,
768
+ );
769
+
770
+ // Assert that the run displays the extended dates.
771
+ expectFullDates(getHeaderContainer(), courseRuns[0]);
772
+
773
+ // Assert that the run displays its languages.
774
+ expectLanguageVisibility(getHeaderContainer(), courseRuns[0], true);
775
+
776
+ const portalContainer = getPortalContainer();
777
+
778
+ // Expect that all closed course runs to be present.
779
+ courseRuns.slice(1).forEach((run) => expectCourseRunInList(portalContainer, run));
780
+ });
781
+
782
+ it('renders self-paced opened run with different languages', async () => {
783
+ const course = PacedCourseFactory({ is_self_paced: true }).one();
784
+
785
+ const courseRuns = [
786
+ CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
787
+ languages: ['it'],
788
+ }).one(),
789
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
790
+ languages: ['en'],
791
+ }).one(),
792
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
793
+ languages: ['fr'],
794
+ }).one(),
795
+ ];
796
+
797
+ render(
798
+ <SyllabusCourseRunsList
799
+ courseRuns={courseRuns}
800
+ course={course}
801
+ maxArchivedCourseRuns={MAX_ARCHIVED_COURSE_RUNS}
802
+ />,
803
+ {
804
+ queryOptions: { client: createTestQueryClient({ user: null }) },
805
+ },
806
+ );
807
+
808
+ // Header of the opened run.
809
+ expect(getHeaderContainer().querySelectorAll('.course-detail__run-descriptions').length).toBe(
810
+ 1,
811
+ );
812
+
813
+ // Assert that the run displays the containers related to the self-paced run dates.
814
+ expectCompactedDates(getHeaderContainer(), courseRuns[0]);
815
+
816
+ // Assert that the run displays the containers related to languages.
817
+ expectLanguageVisibility(getHeaderContainer(), courseRuns[0], true);
818
+
819
+ const portalContainer = getPortalContainer();
820
+
821
+ // Expect that all closed course runs to be present.
822
+ courseRuns.slice(1).forEach((run) => expectCourseRunInList(portalContainer, run));
823
+ });
824
+
825
+ it('renders self-paced opened run with same languages', async () => {
826
+ const course = PacedCourseFactory({ is_self_paced: true }).one();
827
+
828
+ const courseRuns: CourseRun[] = [
829
+ CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
830
+ languages: ['en'],
831
+ }).one(),
832
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
833
+ languages: ['en'],
834
+ }).one(),
835
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
836
+ languages: ['en'],
837
+ }).one(),
838
+ ];
839
+
840
+ render(
841
+ <SyllabusCourseRunsList
842
+ courseRuns={courseRuns}
843
+ course={course}
844
+ maxArchivedCourseRuns={MAX_ARCHIVED_COURSE_RUNS}
845
+ />,
846
+ {
847
+ queryOptions: { client: createTestQueryClient({ user: null }) },
848
+ },
849
+ );
850
+
851
+ // Header of the opened run.
852
+ expect(getHeaderContainer().querySelectorAll('.course-detail__run-descriptions').length).toBe(
853
+ 1,
854
+ );
855
+
856
+ // Assert that the run displays the simplified version of dates
857
+ expectCompactedDates(getHeaderContainer(), courseRuns[0]);
858
+
859
+ // Assert that the run doesn't display the languages
860
+ expectLanguageVisibility(getHeaderContainer(), courseRuns[0], false);
861
+
862
+ const portalContainer = getPortalContainer();
863
+
864
+ // Expect that all closed course runs to be present.
865
+ courseRuns.slice(1).forEach((run) => expectCourseRunInList(portalContainer, run));
866
+ });
867
+
868
+ it('renders self-paced opened forever run with same languages', async () => {
869
+ const course = PacedCourseFactory({ is_self_paced: true }).one();
870
+
871
+ const courseRuns: CourseRun[] = [
872
+ CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
873
+ languages: ['en'],
874
+ end: undefined,
875
+ }).one(),
876
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
877
+ languages: ['en'],
878
+ }).one(),
879
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_CLOSED)({
880
+ languages: ['en'],
881
+ }).one(),
882
+ ];
883
+
884
+ render(
885
+ <SyllabusCourseRunsList
886
+ courseRuns={courseRuns}
887
+ course={course}
888
+ maxArchivedCourseRuns={MAX_ARCHIVED_COURSE_RUNS}
889
+ />,
890
+ {
891
+ queryOptions: { client: createTestQueryClient({ user: null }) },
892
+ },
893
+ );
894
+
895
+ // Header of the opened run.
896
+ expect(getHeaderContainer().querySelectorAll('.course-detail__run-descriptions').length).toBe(
897
+ 1,
898
+ );
899
+
900
+ // Assert that the run displays the simplified version of dates,
901
+ // with hidden course end date.
902
+ expectCompactedDates(getHeaderContainer(), courseRuns[0]);
903
+
904
+ // Assert that the run doesn't display the languages
905
+ expectLanguageVisibility(getHeaderContainer(), courseRuns[0], false);
906
+
907
+ const portalContainer = getPortalContainer();
908
+
909
+ // Expect that all closed course runs to be present.
910
+ courseRuns.slice(1).forEach((run) => expectCourseRunInList(portalContainer, run));
911
+ });
912
+
617
913
  it('renders course runs with snapshot link if needed', async () => {
618
- const course = CourseLightFactory().one();
914
+ const course = PacedCourseFactory().one();
619
915
  const refDate = faker.date.past();
620
916
  const pastDate = (days: number) => {
621
917
  const date = new Date(refDate.getTime());
@@ -661,7 +957,7 @@ describe('<SyllabusCourseRunsList/>', () => {
661
957
 
662
958
  it('renders ongoing course runs with enrollment information', async () => {
663
959
  const user = UserFactory().one();
664
- const course = CourseLightFactory().one();
960
+ const course = PacedCourseFactory().one();
665
961
 
666
962
  const onGoingCourseRun = CourseRunFactoryFromPriority(Priority.ONGOING_CLOSED)({
667
963
  title: 'Ongoing course run',
@@ -2,14 +2,15 @@ import React, { useEffect, useMemo } from 'react';
2
2
  import { defineMessages, FormattedMessage } from 'react-intl';
3
3
  import { createPortal } from 'react-dom';
4
4
  import { Button, CunninghamProvider } from '@openfun/cunningham-react';
5
- import { CourseRun, Priority } from 'types';
5
+ import { PacedCourse, CourseRun, Priority } from 'types';
6
6
  import { computeStates } from 'utils/CourseRuns';
7
+ import { CourseRunHelper } from 'utils/CourseRunHelper';
7
8
  import { SyllabusAsideList } from 'widgets/SyllabusCourseRunsList/components/SyllabusAsideList';
9
+ import { SyllabusCourseRunCompacted } from 'widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted';
8
10
  import { SyllabusCourseRun } from 'widgets/SyllabusCourseRunsList/components/SyllabusCourseRun';
9
11
  import { DjangoCMSPluginsInit } from 'components/DjangoCMSTemplate';
10
12
  import { isJoanieEnabled } from 'api/joanie';
11
13
  import context from 'utils/context';
12
- import { CourseLight } from 'types/Joanie';
13
14
  import CourseWishButton from './components/CourseWishButton';
14
15
 
15
16
  const OPENED_COURSES_ELEMENT_ID = 'courseDetailsRunsOpen';
@@ -41,7 +42,7 @@ const SyllabusCourseRunsList = ({
41
42
  maxArchivedCourseRuns,
42
43
  }: {
43
44
  courseRuns: CourseRun[];
44
- course: CourseLight;
45
+ course: PacedCourse;
45
46
  maxArchivedCourseRuns: number;
46
47
  }) => {
47
48
  useEffect(() => {
@@ -60,6 +61,8 @@ const SyllabusCourseRunsList = ({
60
61
  );
61
62
  }, [courseRunsComputed]);
62
63
 
64
+ const runsWithSameLanguages = CourseRunHelper.IsAllCourseRunsWithSameLanguages(courseRuns);
65
+
63
66
  const choose = (e: React.MouseEvent) => {
64
67
  e.preventDefault();
65
68
  document
@@ -81,11 +84,24 @@ const SyllabusCourseRunsList = ({
81
84
  </div>
82
85
  </div>
83
86
  )}
84
- {openedRuns.length === 1 && (
85
- <div className="course-detail__row course-detail__runs course-detail__runs--open">
86
- <SyllabusCourseRun courseRun={openedRuns[0]} course={course} />
87
- </div>
88
- )}
87
+ {openedRuns.length === 1 &&
88
+ (course.is_self_paced && openedRuns[0].state.priority !== Priority.ARCHIVED_OPEN ? (
89
+ <div className="course-detail__row course-detail__runs course-detail__runs--open">
90
+ <SyllabusCourseRunCompacted
91
+ courseRun={openedRuns[0]}
92
+ course={course}
93
+ showLanguages={runsWithSameLanguages}
94
+ />
95
+ </div>
96
+ ) : (
97
+ <div className="course-detail__row course-detail__runs course-detail__runs--open">
98
+ <SyllabusCourseRun
99
+ courseRun={openedRuns[0]}
100
+ course={course}
101
+ showLanguages={runsWithSameLanguages}
102
+ />
103
+ </div>
104
+ ))}
89
105
  {openedRuns.length > 1 && (
90
106
  <div className="course-detail__row course-detail__runs course-detail__go-to-open-runs">
91
107
  <p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "2.28.0",
3
+ "version": "2.28.2-dev23",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {
@@ -38,34 +38,34 @@
38
38
  "not dead"
39
39
  ],
40
40
  "dependencies": {
41
- "@babel/core": "7.24.7",
41
+ "@babel/core": "7.24.9",
42
42
  "@babel/plugin-syntax-dynamic-import": "7.8.3",
43
- "@babel/plugin-transform-modules-commonjs": "7.24.7",
44
- "@babel/preset-env": "7.24.7",
43
+ "@babel/plugin-transform-modules-commonjs": "7.24.8",
44
+ "@babel/preset-env": "7.25.0",
45
45
  "@babel/preset-react": "7.24.7",
46
46
  "@babel/preset-typescript": "7.24.7",
47
47
  "@faker-js/faker": "8.4.1",
48
48
  "@formatjs/cli": "6.2.12",
49
49
  "@formatjs/intl-relativetimeformat": "11.2.14",
50
- "@hookform/resolvers": "3.6.0",
50
+ "@hookform/resolvers": "3.9.0",
51
51
  "@lyracom/embedded-form-glue": "1.4.2",
52
52
  "@openfun/cunningham-react": "2.9.3",
53
53
  "@openfun/cunningham-tokens": "2.1.1",
54
- "@sentry/browser": "8.8.0",
55
- "@sentry/types": "8.8.0",
56
- "@storybook/addon-actions": "8.1.6",
57
- "@storybook/addon-essentials": "8.1.6",
58
- "@storybook/addon-interactions": "8.1.6",
59
- "@storybook/addon-links": "8.1.6",
60
- "@storybook/react": "8.1.6",
61
- "@storybook/react-webpack5": "8.1.6",
62
- "@storybook/test": "8.1.6",
63
- "@tanstack/query-core": "5.40.0",
64
- "@tanstack/query-sync-storage-persister": "5.40.0",
65
- "@tanstack/react-query": "5.40.1",
66
- "@tanstack/react-query-persist-client": "5.40.1",
67
- "@testing-library/dom": "10.1.0",
68
- "@testing-library/jest-dom": "6.4.5",
54
+ "@sentry/browser": "8.20.0",
55
+ "@sentry/types": "8.20.0",
56
+ "@storybook/addon-actions": "8.2.6",
57
+ "@storybook/addon-essentials": "8.2.6",
58
+ "@storybook/addon-interactions": "8.2.6",
59
+ "@storybook/addon-links": "8.2.6",
60
+ "@storybook/react": "8.2.6",
61
+ "@storybook/react-webpack5": "8.2.6",
62
+ "@storybook/test": "8.2.6",
63
+ "@tanstack/query-core": "5.51.15",
64
+ "@tanstack/query-sync-storage-persister": "5.51.15",
65
+ "@tanstack/react-query": "5.51.15",
66
+ "@tanstack/react-query-persist-client": "5.51.15",
67
+ "@testing-library/dom": "10.4.0",
68
+ "@testing-library/jest-dom": "6.4.8",
69
69
  "@testing-library/react": "16.0.0",
70
70
  "@testing-library/user-event": "14.5.2",
71
71
  "@types/fetch-mock": "7.3.8",
@@ -79,9 +79,9 @@
79
79
  "@types/react-autosuggest": "10.1.11",
80
80
  "@types/react-dom": "18.3.0",
81
81
  "@types/react-modal": "3.16.3",
82
- "@types/uuid": "9.0.8",
83
- "@typescript-eslint/eslint-plugin": "7.12.0",
84
- "@typescript-eslint/parser": "7.12.0",
82
+ "@types/uuid": "10.0.0",
83
+ "@typescript-eslint/eslint-plugin": "7.17.0",
84
+ "@typescript-eslint/parser": "7.17.0",
85
85
  "babel-jest": "29.7.0",
86
86
  "babel-loader": "9.1.3",
87
87
  "babel-plugin-react-intl": "8.2.25",
@@ -89,50 +89,50 @@
89
89
  "classnames": "2.5.1",
90
90
  "cljs-merge": "1.1.1",
91
91
  "core-js": "3.37.1",
92
- "downshift": "9.0.6",
92
+ "downshift": "9.0.7",
93
93
  "eslint": ">=8.57.0 <9",
94
94
  "eslint-config-airbnb": "19.0.4",
95
95
  "eslint-config-airbnb-typescript": "18.0.0",
96
96
  "eslint-config-prettier": "9.1.0",
97
97
  "eslint-import-resolver-webpack": "0.13.8",
98
- "eslint-plugin-compat": "5.0.0",
98
+ "eslint-plugin-compat": "6.0.0",
99
99
  "eslint-plugin-formatjs": "4.13.3",
100
100
  "eslint-plugin-import": "2.29.1",
101
- "eslint-plugin-jsx-a11y": "6.8.0",
102
- "eslint-plugin-prettier": "5.1.3",
103
- "eslint-plugin-react": "7.34.2",
101
+ "eslint-plugin-jsx-a11y": "6.9.0",
102
+ "eslint-plugin-prettier": "5.2.1",
103
+ "eslint-plugin-react": "7.35.0",
104
104
  "eslint-plugin-react-hooks": "4.6.2",
105
105
  "eslint-plugin-storybook": "0.8.0",
106
- "fetch-mock": "9.11.0",
106
+ "fetch-mock": "<10",
107
107
  "file-loader": "6.2.0",
108
- "glob": "10.4.1",
109
- "i18n-iso-countries": "7.11.2",
110
- "iframe-resizer": "4.4.2",
108
+ "glob": "11.0.0",
109
+ "i18n-iso-countries": "7.11.3",
110
+ "iframe-resizer": "4.4.5",
111
111
  "intl-pluralrules": "2.0.1",
112
112
  "jest": "29.7.0",
113
113
  "jest-environment-jsdom": "29.7.0",
114
114
  "js-cookie": "3.0.5",
115
115
  "lodash-es": "4.17.21",
116
116
  "mdn-polyfills": "5.20.0",
117
- "msw": "2.3.1",
117
+ "msw": "2.3.4",
118
118
  "node-fetch": ">2.6.6 <3",
119
- "nodemon": "3.1.3",
120
- "prettier": "3.3.1",
121
- "query-string": "9.0.0",
119
+ "nodemon": "3.1.4",
120
+ "prettier": "3.3.3",
121
+ "query-string": "9.1.0",
122
122
  "react": "18.3.1",
123
123
  "react-autosuggest": "10.1.0",
124
124
  "react-dom": "18.3.1",
125
- "react-hook-form": "7.51.5",
125
+ "react-hook-form": "7.52.1",
126
126
  "react-intl": "6.6.8",
127
127
  "react-modal": "3.16.1",
128
- "react-router-dom": "6.23.1",
129
- "sass": "1.77.4",
128
+ "react-router-dom": "6.25.1",
129
+ "sass": "1.77.8",
130
130
  "source-map-loader": "5.0.0",
131
- "storybook": "8.1.6",
131
+ "storybook": "8.2.6",
132
132
  "tsconfig-paths-webpack-plugin": "4.1.0",
133
- "typescript": "5.4.5",
133
+ "typescript": "5.5.4",
134
134
  "uuid": "10.0.0",
135
- "webpack": "5.91.0",
135
+ "webpack": "5.93.0",
136
136
  "webpack-cli": "5.1.4",
137
137
  "whatwg-fetch": "3.6.20",
138
138
  "xhr-mock": "2.5.1",
@@ -140,7 +140,7 @@
140
140
  "yup": "1.4.0"
141
141
  },
142
142
  "resolutions": {
143
- "@testing-library/dom": "10.1.0",
143
+ "@testing-library/dom": "10.4.0",
144
144
  "@types/react": "18.3.3",
145
145
  "@types/react-dom": "18.3.0"
146
146
  },
@@ -151,7 +151,7 @@
151
151
  "node": "20.11.0"
152
152
  },
153
153
  "devDependencies": {
154
- "@storybook/addon-mdx-gfm": "8.1.6",
154
+ "@storybook/addon-mdx-gfm": "8.2.6",
155
155
  "@storybook/addon-webpack5-compiler-babel": "3.0.3"
156
156
  }
157
157
  }
package/tsconfig.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "skipLibCheck": true,
26
26
  "strict": true,
27
27
  "sourceMap": true,
28
- "target": "es6"
28
+ "target": "es2018"
29
29
  },
30
30
  "include": ["./**/*"]
31
31
  }