richie-education 2.34.1-dev5 → 2.34.1-dev52

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 (33) hide show
  1. package/js/components/ContractFrame/AbstractContractFrame.spec.tsx +1 -1
  2. package/js/components/CourseGlimpse/CourseGlimpseFooter.tsx +84 -13
  3. package/js/components/CourseGlimpse/index.spec.tsx +80 -5
  4. package/js/components/CourseGlimpse/index.tsx +92 -76
  5. package/js/components/CourseGlimpse/utils.ts +31 -1
  6. package/js/components/Icon/index.tsx +7 -0
  7. package/js/components/OpenEdxFullNameForm/index.spec.tsx +17 -7
  8. package/js/components/OpenEdxFullNameForm/index.tsx +13 -16
  9. package/js/components/SaleTunnel/index.full-process.spec.tsx +1 -1
  10. package/js/pages/DashboardCreditCardsManagement/index.spec.tsx +9 -8
  11. package/js/types/Course.ts +18 -0
  12. package/js/types/index.ts +5 -0
  13. package/js/utils/test/expectAlert.ts +63 -0
  14. package/js/utils/test/factories/richie.ts +31 -1
  15. package/js/widgets/Slider/components/Slide.tsx +20 -0
  16. package/js/widgets/Slider/components/SlidePanel.tsx +83 -0
  17. package/js/widgets/Slider/components/Slideshow.tsx +58 -0
  18. package/js/widgets/Slider/index.spec.tsx +167 -0
  19. package/js/widgets/Slider/index.tsx +119 -0
  20. package/js/widgets/Slider/types/index.ts +8 -0
  21. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.tsx +107 -0
  22. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.tsx +107 -0
  23. package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +450 -5
  24. package/js/widgets/index.tsx +3 -0
  25. package/package.json +45 -43
  26. package/scss/colors/_theme.scss +26 -7
  27. package/scss/components/_header.scss +108 -14
  28. package/scss/components/_subheader.scss +35 -0
  29. package/scss/components/templates/courses/cms/_program_detail.scss +71 -0
  30. package/scss/components/templates/richie/slider/_slider.scss +165 -99
  31. package/scss/objects/_course_glimpses.scss +109 -10
  32. package/scss/objects/_selector.scss +1 -0
  33. package/scss/settings/_variables.scss +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "2.34.1-dev5",
3
+ "version": "2.34.1-dev52",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {
@@ -38,75 +38,77 @@
38
38
  "not dead"
39
39
  ],
40
40
  "dependencies": {
41
- "@babel/core": "7.26.9",
41
+ "@babel/core": "7.26.10",
42
42
  "@babel/plugin-syntax-dynamic-import": "7.8.3",
43
43
  "@babel/plugin-transform-modules-commonjs": "7.26.3",
44
44
  "@babel/preset-env": "7.26.9",
45
45
  "@babel/preset-react": "7.26.3",
46
46
  "@babel/preset-typescript": "7.26.0",
47
- "@faker-js/faker": "9.5.0",
48
- "@formatjs/cli": "6.6.1",
49
- "@formatjs/intl-relativetimeformat": "11.4.10",
50
- "@hookform/resolvers": "4.1.0",
47
+ "@faker-js/faker": "9.6.0",
48
+ "@formatjs/cli": "6.6.2",
49
+ "@formatjs/intl-relativetimeformat": "11.4.11",
50
+ "@hookform/resolvers": "4.1.3",
51
51
  "@lyracom/embedded-form-glue": "1.4.2",
52
52
  "@openfun/cunningham-react": "3.0.0",
53
53
  "@openfun/cunningham-tokens": "2.2.0",
54
- "@sentry/browser": "9.1.0",
55
- "@sentry/types": "9.1.0",
56
- "@storybook/addon-actions": "8.5.6",
57
- "@storybook/addon-essentials": "8.5.6",
58
- "@storybook/addon-interactions": "8.5.6",
59
- "@storybook/addon-links": "8.5.6",
60
- "@storybook/react": "8.5.6",
61
- "@storybook/react-webpack5": "8.5.6",
62
- "@storybook/test": "8.5.6",
63
- "@tanstack/query-core": "5.66.3",
64
- "@tanstack/query-sync-storage-persister": "5.66.3",
65
- "@tanstack/react-query": "5.66.3",
66
- "@tanstack/react-query-devtools": "5.66.3",
67
- "@tanstack/react-query-persist-client": "5.66.3",
54
+ "@sentry/browser": "9.8.0",
55
+ "@sentry/types": "9.8.0",
56
+ "@storybook/addon-actions": "8.6.8",
57
+ "@storybook/addon-essentials": "8.6.8",
58
+ "@storybook/addon-interactions": "8.6.8",
59
+ "@storybook/addon-links": "8.6.8",
60
+ "@storybook/react": "8.6.8",
61
+ "@storybook/react-webpack5": "8.6.8",
62
+ "@storybook/test": "8.6.8",
63
+ "@tanstack/query-core": "5.69.0",
64
+ "@tanstack/query-sync-storage-persister": "5.69.0",
65
+ "@tanstack/react-query": "5.69.0",
66
+ "@tanstack/react-query-devtools": "5.69.0",
67
+ "@tanstack/react-query-persist-client": "5.69.0",
68
68
  "@testing-library/dom": "10.4.0",
69
69
  "@testing-library/jest-dom": "6.6.3",
70
70
  "@testing-library/react": "16.2.0",
71
71
  "@testing-library/user-event": "14.6.1",
72
- "@types/fetch-mock": "7.3.8",
72
+ "@types/fetch-mock": "9.2.2",
73
73
  "@types/iframe-resizer": "4.0.0",
74
74
  "@types/jest": "29.5.14",
75
75
  "@types/js-cookie": "3.0.6",
76
76
  "@types/lodash-es": "4.17.12",
77
77
  "@types/node-fetch": "2.6.12",
78
78
  "@types/query-string": "6.3.0",
79
- "@types/react": "19.0.10",
79
+ "@types/react": "19.0.12",
80
80
  "@types/react-autosuggest": "10.1.11",
81
81
  "@types/react-dom": "19.0.4",
82
82
  "@types/react-modal": "3.16.3",
83
- "@typescript-eslint/eslint-plugin": "8.24.0",
84
- "@typescript-eslint/parser": "8.24.0",
83
+ "@typescript-eslint/eslint-plugin": "8.27.0",
84
+ "@typescript-eslint/parser": "8.27.0",
85
85
  "babel-jest": "29.7.0",
86
- "babel-loader": "9.2.1",
86
+ "babel-loader": "10.0.0",
87
87
  "babel-plugin-react-intl": "8.2.25",
88
88
  "bootstrap": ">=4.6.0 <5",
89
89
  "classnames": "2.5.1",
90
90
  "cljs-merge": "1.1.1",
91
- "core-js": "3.40.0",
92
- "downshift": "9.0.8",
91
+ "core-js": "3.41.0",
92
+ "downshift": "9.0.9",
93
+ "embla-carousel-react": "8.5.2",
94
+ "embla-carousel-wheel-gestures": "8.0.1",
93
95
  "eslint": ">=8.57.0 <9",
94
96
  "eslint-config-airbnb": "19.0.4",
95
97
  "eslint-config-airbnb-typescript": "18.0.0",
96
- "eslint-config-prettier": "10.0.1",
98
+ "eslint-config-prettier": "10.1.1",
97
99
  "eslint-import-resolver-webpack": "0.13.10",
98
100
  "eslint-plugin-compat": "6.0.2",
99
- "eslint-plugin-formatjs": "5.2.14",
101
+ "eslint-plugin-formatjs": "5.3.0",
100
102
  "eslint-plugin-import": "2.31.0",
101
103
  "eslint-plugin-jsx-a11y": "6.10.2",
102
- "eslint-plugin-prettier": "5.2.3",
104
+ "eslint-plugin-prettier": "5.2.4",
103
105
  "eslint-plugin-react": "7.37.4",
104
- "eslint-plugin-react-hooks": "5.1.0",
105
- "eslint-plugin-storybook": "0.11.3",
106
+ "eslint-plugin-react-hooks": "5.2.0",
107
+ "eslint-plugin-storybook": "0.11.6",
106
108
  "fetch-mock": "<10",
107
109
  "file-loader": "6.2.0",
108
110
  "glob": "11.0.1",
109
- "i18n-iso-countries": "7.13.0",
111
+ "i18n-iso-countries": "7.14.0",
110
112
  "iframe-resizer": "<5",
111
113
  "intl-pluralrules": "2.0.1",
112
114
  "jest": "29.7.0",
@@ -114,24 +116,24 @@
114
116
  "js-cookie": "3.0.5",
115
117
  "lodash-es": "4.17.21",
116
118
  "mdn-polyfills": "5.20.0",
117
- "msw": "2.7.0",
119
+ "msw": "2.7.3",
118
120
  "node-fetch": ">2.6.6 <3",
119
121
  "nodemon": "3.1.9",
120
- "prettier": "3.5.1",
122
+ "prettier": "3.5.3",
121
123
  "query-string": "9.1.1",
122
124
  "react": "19.0.0",
123
125
  "react-autosuggest": "10.1.0",
124
126
  "react-dom": "19.0.0",
125
127
  "react-hook-form": "7.54.2",
126
- "react-intl": "7.1.6",
128
+ "react-intl": "7.1.9",
127
129
  "react-modal": "3.16.3",
128
- "react-router": "7.1.5",
129
- "sass": "1.85.0",
130
+ "react-router": "7.4.0",
131
+ "sass": "1.86.0",
130
132
  "source-map-loader": "5.0.0",
131
- "storybook": "8.5.6",
133
+ "storybook": "8.6.8",
132
134
  "tsconfig-paths-webpack-plugin": "4.2.0",
133
- "typescript": "5.7.3",
134
- "uuid": "11.0.5",
135
+ "typescript": "5.8.2",
136
+ "uuid": "11.1.0",
135
137
  "webpack": "5.98.0",
136
138
  "webpack-cli": "6.0.1",
137
139
  "whatwg-fetch": "3.6.20",
@@ -141,7 +143,7 @@
141
143
  },
142
144
  "resolutions": {
143
145
  "@testing-library/dom": "10.4.0",
144
- "@types/react": "19.0.10",
146
+ "@types/react": "19.0.12",
145
147
  "@types/react-dom": "19.0.4",
146
148
  "react": "19.0.0",
147
149
  "react-dom": "19.0.0"
@@ -154,7 +156,7 @@
154
156
  "yarn": "1.22.22"
155
157
  },
156
158
  "devDependencies": {
157
- "@storybook/addon-mdx-gfm": "8.5.6",
159
+ "@storybook/addon-mdx-gfm": "8.6.8",
158
160
  "@storybook/addon-webpack5-compiler-babel": "3.0.5"
159
161
  }
160
162
  }
@@ -47,6 +47,14 @@ $r-theme: (
47
47
  item-cta-hollow-background: transparent,
48
48
  item-cta-hollow-border: transparent,
49
49
  item-divider-border: r-color('light-grey'),
50
+ dropdown-border-radius: 8px,
51
+ dropdown-padding: 0.5rem,
52
+ dropdown-gap: 0.5rem,
53
+ dropdown-background-color: r-color('azure2'),
54
+ dropdown-item-background-color: r-color('indianred3'),
55
+ dropdown-item-text-color: r-color('white'),
56
+ dropdown-item-border-radius: 4px,
57
+ dropdown-item-padding: 0.5rem 1rem,
50
58
  ),
51
59
  body-content: (
52
60
  base-color: r-color('black'),
@@ -152,10 +160,12 @@ $r-theme: (
152
160
  secondary-color: r-color('white'),
153
161
  ),
154
162
  slider-plugin: (
155
- arrows-color: r-color('white'),
156
- arrows-hover-color: r-color('firebrick6'),
157
- index-color: r-color('charcoal'),
158
- index-hover-color: r-color('black'),
163
+ arrows-fill-color: r-color('white'),
164
+ arrows-stroke-color: r-color('black'),
165
+ arrows-stroke-width: 0.5px,
166
+ index-color: r-color('battleship-grey'),
167
+ index-hover-color: r-color('indianred3'),
168
+ index-active-color: r-color('firebrick6'),
159
169
  ),
160
170
  blogpost-glimpse: (
161
171
  card-background: r-color('white'),
@@ -209,8 +219,8 @@ $r-theme: (
209
219
  ),
210
220
  course-glimpse: (
211
221
  card-background: r-color('white'),
212
- base-shadow: 0 0 6px r-color('light-grey'),
213
- base-hover-shadow: 0 0 6px r-color('battleship-grey'),
222
+ base-shadow: 0 0 8px #00000033,
223
+ base-hover-shadow: 0 0 4px #00000055,
214
224
  cta-background: r-color('battleship-grey'),
215
225
  empty-color: r-color('slate-grey'),
216
226
  icon-shadow: (
@@ -224,8 +234,15 @@ $r-theme: (
224
234
  organization-color: r-color('firebrick6'),
225
235
  code-color: r-color('battleship-grey'),
226
236
  svg-icon-fill: r-color('white'),
227
- footer: $battleship-grey-scheme,
228
237
  organization-shadow: 0 0 6px r-color('light-grey'),
238
+ footer: $battleship-grey-scheme,
239
+ footer-offer-paid: $indianred3-scheme,
240
+ footer-offer-subscription: $indianred3-scheme,
241
+ footer-offer-partially_free: $indianred3-scheme,
242
+ footer-offer-certificate: null,
243
+ offer-icon-visibility: visible,
244
+ offer-certificate-icon-visibility: visible,
245
+ offer-price-visibility: visible,
229
246
  ),
230
247
  category-badges: (
231
248
  primary-item: $indianred3-scheme,
@@ -483,6 +500,8 @@ $r-theme: (
483
500
  ),
484
501
  program-detail: (
485
502
  cover-empty-background: r-color('smoke'),
503
+ checkmark-list-decoration: url('../../richie/images/components/checkmark.svg'),
504
+ checkmark-list-decoration-color: r-color('indianred3'),
486
505
  ),
487
506
  registered-credit-card: (
488
507
  title-color: r-color('charcoal'),
@@ -168,6 +168,10 @@
168
168
  flex-wrap: nowrap;
169
169
  }
170
170
 
171
+ & > .topbar__list {
172
+ position: relative;
173
+ }
174
+
171
175
  // Aside menu variation
172
176
  &--aside {
173
177
  @include sv-flex(1, 0, auto);
@@ -197,6 +201,81 @@
197
201
  // Menu item element
198
202
  &__item {
199
203
  $item-selector: &;
204
+ &.dropdown {
205
+ display: block;
206
+ position: static;
207
+
208
+ & > button {
209
+ width: 100%;
210
+
211
+ & > .icon {
212
+ height: 1rem;
213
+ margin-left: 0.5rem;
214
+ width: 1rem;
215
+ }
216
+ }
217
+
218
+ & ul[role='menu'] {
219
+ padding-left: 0.5rem;
220
+ }
221
+ }
222
+
223
+ @include media-breakpoint-up($r-topbar-breakpoint) {
224
+ &.dropdown {
225
+ // See header_menu.html for the definition of the variables
226
+ --active-background-color: #{r-theme-val(topbar, dropdown-item-background-color)};
227
+ --active-text-color: #{r-theme-val(topbar, dropdown-item-text-color)};
228
+
229
+ & > button[aria-expanded='true'] {
230
+ background-color: r-theme-val(topbar, dropdown-background-color);
231
+ }
232
+
233
+ & > button[aria-expanded='true'] + .topbar__sublist {
234
+ display: block;
235
+ }
236
+
237
+ & > .topbar__sublist {
238
+ background: r-theme-val(topbar, dropdown-background-color);
239
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
240
+ border-radius: r-theme-val(topbar, dropdown-border-radius);
241
+ display: none;
242
+ position: absolute;
243
+ transform: translateX(-8px);
244
+ width: max-content;
245
+ max-width: 100%;
246
+
247
+ & > ul[role='menu'] {
248
+ display: flex;
249
+ flex-wrap: wrap;
250
+ gap: r-theme-val(topbar, dropdown-gap);
251
+ padding: r-theme-val(topbar, dropdown-padding);
252
+ }
253
+
254
+ & .topbar__item > a {
255
+ background-color: #fff;
256
+ border-radius: r-theme-val(topbar, dropdown-item-border-radius);
257
+ padding: r-theme-val(topbar, dropdown-item-padding);
258
+ &::after {
259
+ display: none;
260
+ }
261
+
262
+ &:is(:focus, :hover) {
263
+ background-color: var(--active-background-color);
264
+ color: var(--active-text-color);
265
+ }
266
+ }
267
+
268
+ &--full-width {
269
+ left: 0;
270
+ transform: inherit;
271
+ }
272
+
273
+ &--position-right {
274
+ transform: translateX(calc(-100% + (var(--button-width) + 8px)));
275
+ }
276
+ }
277
+ }
278
+ }
200
279
 
201
280
  @include sv-flex(1, 0, auto);
202
281
  display: flex;
@@ -213,15 +292,23 @@
213
292
  --r--menu--item--hover--color: #{r-theme-val(topbar, item-hover-color)};
214
293
  }
215
294
 
216
- & > a {
295
+ & > button {
296
+ @include button-reset-style();
297
+ --radius: #{r-theme-val(topbar, dropdown-border-radius)};
298
+ border-radius: var(--radius) var(--radius) 0 0;
299
+ }
300
+
301
+ & > a,
302
+ & > button {
217
303
  @include sv-flex(1, 0, 100%);
304
+ align-items: center;
305
+ color: inherit;
218
306
  display: flex;
219
- padding: 1rem 0.2rem 1rem 1rem;
220
307
  flex-direction: row;
221
- align-items: center;
222
308
  font-family: inherit;
223
309
  font-weight: inherit;
224
- color: inherit;
310
+ justify-content: space-between;
311
+ padding: 1rem 0.2rem 1rem 1rem;
225
312
 
226
313
  @include media-breakpoint-up($r-topbar-breakpoint) {
227
314
  padding: 1rem 1rem;
@@ -235,18 +322,22 @@
235
322
 
236
323
  // If there is no default hover color we assume there is also no variant
237
324
  @if r-theme-val(topbar, item-hover-color) {
238
- &::after {
239
- content: '';
240
- position: absolute;
241
- bottom: 0;
242
- left: 0;
243
- right: 0;
244
- height: 8px;
325
+ &:is(a)::after {
245
326
  background-color: var(--r--menu--item--hover--color);
246
327
  border-top-left-radius: 0.2rem;
247
328
  border-top-right-radius: 0.2rem;
329
+ bottom: 0;
330
+ content: '';
331
+ height: 8px;
332
+ left: 0;
333
+ position: absolute;
334
+ right: 0;
248
335
  }
249
336
  }
337
+
338
+ &:is(button) {
339
+ background-color: r-theme-val(topbar, dropdown-background-color);
340
+ }
250
341
  }
251
342
  }
252
343
  }
@@ -257,8 +348,10 @@
257
348
 
258
349
  // Current page item or current ancestor
259
350
  &--selected,
260
- &--ancestor {
261
- & > a {
351
+ &--ancestor,
352
+ &:has(.topbar__sublist .topbar__item.topbar__item--selected) {
353
+ & > a,
354
+ & > button {
262
355
  position: relative;
263
356
  color: r-theme-val(topbar, item-active-color);
264
357
 
@@ -328,7 +421,8 @@
328
421
  }
329
422
 
330
423
  // Item divider
331
- & + #{$item-selector} {
424
+ & + #{$item-selector},
425
+ & > .topbar__list #{$item-selector} {
332
426
  @if r-theme-val(topbar, item-divider-border) {
333
427
  border-top: $onepixel solid r-theme-val(topbar, item-divider-border);
334
428
  }
@@ -170,6 +170,41 @@ $r-subheader-search-title-width: 19rem !default; // aligned on computed search r
170
170
  position: relative;
171
171
  padding-bottom: 56.25%; // Aspect ratio 16/9
172
172
 
173
+ .video-player-image {
174
+ img {
175
+ filter: brightness(0.85);
176
+ object-fit: cover;
177
+ }
178
+ img,
179
+ span {
180
+ position: absolute;
181
+ width: 100%;
182
+ top: 0;
183
+ bottom: 0;
184
+ margin: auto;
185
+ }
186
+ span {
187
+ text-align: center;
188
+ font: 48px/1.5 sans-serif;
189
+ fill: white;
190
+ display: flex;
191
+ justify-content: center;
192
+ align-items: center;
193
+ }
194
+ span svg {
195
+ transition: 0.5s;
196
+ width: 85px;
197
+ height: 85px;
198
+ }
199
+ img:hover,
200
+ span:hover svg {
201
+ fill-opacity: 1;
202
+ filter: drop-shadow(3px 3px 30px rgb(0 0 0 / 0.65));
203
+ }
204
+ span svg {
205
+ filter: drop-shadow(3px 3px 12px rgb(0 0 0 / 0.25));
206
+ }
207
+ }
173
208
  iframe {
174
209
  height: 100%;
175
210
  position: absolute;
@@ -2,11 +2,13 @@
2
2
  //
3
3
 
4
4
  .program-detail {
5
+ $detail-selector: &;
5
6
  margin: 0 auto;
6
7
  padding: 0;
7
8
 
8
9
  &__block {
9
10
  @include detail-block;
11
+
10
12
  @if $body-padding-fix {
11
13
  @include content-padding-fix($target: '&:last-child');
12
14
  }
@@ -54,6 +56,75 @@
54
56
  padding-right: $grid-gutter-width;
55
57
  }
56
58
 
59
+ &__objectives {
60
+ ul {
61
+ padding-left: 0.3rem;
62
+ list-style-type: none;
63
+
64
+ li {
65
+ position: relative;
66
+ margin-top: 0.5rem;
67
+ font-size: 1rem;
68
+ padding-left: 1.5rem;
69
+
70
+ &::before {
71
+ content: '';
72
+ display: block;
73
+ position: absolute;
74
+ top: 0.2rem;
75
+ left: 0;
76
+ width: 0.8rem;
77
+ height: 0.8rem;
78
+ background-repeat: no-repeat;
79
+ background-color: r-theme-val(program-detail, checkmark-list-decoration-color);
80
+ -webkit-mask: r-theme-val(program-detail, checkmark-list-decoration);
81
+ mask: r-theme-val(program-detail, checkmark-list-decoration);
82
+ -webkit-mask-size: cover;
83
+ mask-size: cover;
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ &__content {
90
+ @include media-breakpoint-up(lg) {
91
+ padding-right: 3rem;
92
+ }
93
+ }
94
+
95
+ &__aside {
96
+ padding: 1rem 0;
97
+
98
+ @include media-breakpoint-up(lg) {
99
+ @include sv-flex(1, 0, $r-program-aside);
100
+ padding: 3rem 1rem;
101
+ }
102
+
103
+ #{$detail-selector}__row {
104
+ margin-bottom: 1.5rem;
105
+ }
106
+
107
+ #{$detail-selector}__title {
108
+ @include font-size($h3-font-size);
109
+ padding-bottom: 1rem;
110
+
111
+ @if r-theme-val(course-detail, aside-title-border) {
112
+ border-bottom: $onepixel solid r-theme-val(course-detail, aside-title-border);
113
+ }
114
+ }
115
+ }
116
+
117
+ &__main {
118
+ @include media-breakpoint-up(lg) {
119
+ @include sv-flex(1, 0, calc(100% - #{$r-program-subheader-aside}));
120
+ display: flex;
121
+ justify-content: flex-start;
122
+ align-content: flex-start;
123
+ align-items: flex-start;
124
+ flex-wrap: wrap;
125
+ }
126
+ }
127
+
57
128
  &__courses {
58
129
  @include make-col-ready();
59
130
  @include make-col(12);