richie-education 2.24.0 → 2.25.0-b2.dev101

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 (492) hide show
  1. package/.eslintignore +2 -0
  2. package/.nvmrc +1 -1
  3. package/.prettierignore +2 -0
  4. package/.storybook/preview-body.html +1 -1
  5. package/.storybook/preview.tsx +5 -2
  6. package/cunningham.cjs +52 -43
  7. package/i18n/locales/ar-SA.json +500 -100
  8. package/i18n/locales/es-ES.json +500 -100
  9. package/i18n/locales/fa-IR.json +500 -100
  10. package/i18n/locales/fr-CA.json +564 -164
  11. package/i18n/locales/fr-FR.json +517 -117
  12. package/i18n/locales/ko-KR.json +500 -100
  13. package/i18n/locales/pt-PT.json +526 -126
  14. package/i18n/locales/ru-RU.json +500 -100
  15. package/i18n/locales/vi-VN.json +1734 -0
  16. package/jest/setup.ts +11 -1
  17. package/js/api/enrollment.ts +1 -1
  18. package/js/api/joanie.spec.ts +63 -2
  19. package/js/api/joanie.ts +218 -141
  20. package/js/api/lms/dummy.spec.ts +9 -1
  21. package/js/api/lms/dummy.ts +63 -10
  22. package/js/api/lms/joanie.spec.ts +49 -31
  23. package/js/api/lms/joanie.ts +53 -35
  24. package/js/api/lms/openedx-hawthorn.spec.ts +27 -11
  25. package/js/api/lms/openedx-hawthorn.ts +7 -6
  26. package/js/components/AddressesManagement/AddressForm/index.spec.tsx +157 -0
  27. package/js/components/AddressesManagement/AddressForm/index.stories.tsx +36 -0
  28. package/js/components/AddressesManagement/AddressForm/index.tsx +163 -0
  29. package/js/components/AddressesManagement/{validationSchema.ts → AddressForm/validationSchema.ts} +1 -23
  30. package/js/components/AddressesManagement/_styles.scss +1 -1
  31. package/js/components/AddressesManagement/index.spec.tsx +171 -202
  32. package/js/components/AddressesManagement/index.stories.tsx +29 -0
  33. package/js/components/AddressesManagement/index.tsx +11 -3
  34. package/js/components/Badge/index.spec.tsx +17 -0
  35. package/js/components/Badge/index.stories.tsx +22 -0
  36. package/js/components/Badge/index.tsx +18 -0
  37. package/js/components/Banner/index.tsx +6 -1
  38. package/js/components/ContractFrame/AbstractContractFrame.spec.tsx +332 -0
  39. package/js/components/ContractFrame/AbstractContractFrame.tsx +289 -0
  40. package/js/components/ContractFrame/LearnerContractFrame.spec.tsx +125 -0
  41. package/js/components/ContractFrame/LearnerContractFrame.tsx +42 -0
  42. package/js/components/ContractFrame/OrganizationContractFrame.spec.tsx +167 -0
  43. package/js/components/ContractFrame/OrganizationContractFrame.tsx +70 -0
  44. package/js/components/ContractFrame/_styles.scss +62 -0
  45. package/js/components/ContractFrame/iframe-manager.js +158 -0
  46. package/js/components/ContractFrame/index.ts +5 -0
  47. package/js/components/ContractStatus/index.spec.tsx +120 -0
  48. package/js/components/ContractStatus/index.tsx +67 -0
  49. package/js/components/CourseGlimpse/CourseGlimpseFooter.tsx +7 -7
  50. package/js/components/CourseGlimpse/index.tsx +5 -1
  51. package/js/components/CourseGlimpse/utils.ts +24 -16
  52. package/js/components/CourseGlimpseList/index.spec.tsx +1 -1
  53. package/js/components/CourseGlimpseList/index.tsx +1 -1
  54. package/js/components/CourseGlimpseList/utils.ts +3 -2
  55. package/js/components/DjangoCMSTemplate/index.spec.tsx +2 -2
  56. package/js/components/DownloadCertificateButton/index.tsx +58 -0
  57. package/js/components/DownloadContractButton/index.spec.tsx +155 -0
  58. package/js/components/DownloadContractButton/index.tsx +48 -0
  59. package/js/components/Form/CountrySelectField.tsx +28 -16
  60. package/js/components/Form/Input/index.spec.tsx +76 -0
  61. package/js/components/Form/Input/index.tsx +47 -0
  62. package/js/components/Form/Select/index.spec.tsx +99 -0
  63. package/js/components/Form/Select/index.tsx +43 -0
  64. package/js/components/{AddressesManagement → Form}/ValidationErrors.ts +10 -5
  65. package/js/components/Form/index.ts +5 -1
  66. package/js/components/Form/messages.ts +14 -0
  67. package/js/components/Form/test-utils.ts +19 -0
  68. package/js/components/Form/utils.spec.ts +72 -0
  69. package/js/components/Form/utils.ts +37 -0
  70. package/js/components/Icon/index.stories.tsx +2 -1
  71. package/js/components/Modal/_styles.scss +0 -8
  72. package/js/components/Modal/index.spec.tsx +0 -6
  73. package/js/components/Modal/index.tsx +23 -17
  74. package/js/components/PaymentButton/_styles.scss +26 -0
  75. package/js/{widgets/CourseProductItem → components/PaymentButton}/components/PaymentInterfaces/Dummy.tsx +1 -1
  76. package/js/{widgets/CourseProductItem → components/PaymentButton}/components/PaymentInterfaces/PayplugLightbox.tsx +30 -7
  77. package/js/{widgets/CourseProductItem → components/PaymentButton}/components/PaymentInterfaces/__mocks__/index.tsx +1 -1
  78. package/js/{widgets/CourseProductItem → components/PaymentButton}/components/PaymentInterfaces/index.spec.tsx +5 -3
  79. package/js/{widgets/CourseProductItem → components/PaymentButton}/components/PaymentInterfaces/index.tsx +7 -5
  80. package/js/components/PaymentButton/hooks/useTerms.tsx +74 -0
  81. package/js/components/PaymentButton/index.spec.tsx +1038 -0
  82. package/js/{widgets/CourseProductItem/components → components}/PaymentButton/index.tsx +94 -41
  83. package/js/components/PurchaseButton/index.spec.tsx +377 -0
  84. package/js/components/PurchaseButton/index.stories.tsx +15 -0
  85. package/js/{widgets/CourseProductItem/components → components}/PurchaseButton/index.tsx +72 -23
  86. package/js/components/PurchaseButton/styles.scss +7 -0
  87. package/js/components/RegisteredAddress/_styles.scss +1 -3
  88. package/js/components/RegisteredAddress/index.spec.tsx +1 -1
  89. package/js/components/RegisteredAddress/index.stories.tsx +40 -0
  90. package/js/components/RegisteredAddress/index.tsx +17 -19
  91. package/js/components/SaleTunnel/_styles.scss +11 -0
  92. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/RegisteredCreditCard/index.tsx +4 -10
  93. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/SaleTunnelStepPayment/_styles.scss +7 -0
  94. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/SaleTunnelStepPayment/index.spec.tsx +85 -61
  95. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/SaleTunnelStepPayment/index.tsx +55 -57
  96. package/js/components/SaleTunnel/components/SaleTunnelStepResume/_styles.scss +63 -0
  97. package/js/components/SaleTunnel/components/SaleTunnelStepResume/index.spec.tsx +80 -0
  98. package/js/components/SaleTunnel/components/SaleTunnelStepResume/index.tsx +88 -0
  99. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/SaleTunnelStepValidation/CourseRunsList.tsx +6 -2
  100. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/SaleTunnelStepValidation/_styles.scss +5 -0
  101. package/js/components/SaleTunnel/components/SaleTunnelStepValidation/index.spec.tsx +170 -0
  102. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/SaleTunnelStepValidation/index.tsx +41 -10
  103. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/StepBreadcrumb/index.spec.tsx +1 -1
  104. package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/StepBreadcrumb/index.tsx +1 -1
  105. package/js/components/SaleTunnel/context.tsx +44 -0
  106. package/js/{widgets/CourseProductItem/components → components}/SaleTunnel/index.spec.tsx +27 -22
  107. package/js/{widgets/CourseProductItem/components → components}/SaleTunnel/index.tsx +96 -24
  108. package/js/components/SignContractButton/index.omniscientOrders.spec.tsx +135 -0
  109. package/js/components/SignContractButton/index.spec.tsx +213 -0
  110. package/js/components/SignContractButton/index.tsx +97 -0
  111. package/js/components/SuccessIcon/_styles.scss +66 -0
  112. package/js/components/SuccessIcon/index.tsx +10 -0
  113. package/js/components/TeacherDashboardCourseList/_styles.scss +0 -1
  114. package/js/components/TeacherDashboardCourseList/index.spec.tsx +9 -9
  115. package/js/components/TeacherDashboardCourseList/index.tsx +25 -31
  116. package/js/contexts/JoanieApiContext/index.spec.tsx +1 -1
  117. package/js/contexts/SessionContext/BaseSessionProvider.tsx +12 -22
  118. package/js/contexts/SessionContext/JoanieSessionProvider.spec.tsx +14 -0
  119. package/js/contexts/SessionContext/JoanieSessionProvider.tsx +33 -34
  120. package/js/contexts/SessionContext/index.spec.tsx +6 -7
  121. package/js/hooks/useBreadcrumbsPlaceholders.tsx +1 -1
  122. package/js/hooks/useContractAbilities/index.spec.ts +27 -0
  123. package/js/hooks/useContractAbilities/index.ts +8 -0
  124. package/js/hooks/useContractArchive/index.download.spec.tsx +126 -0
  125. package/js/hooks/useContractArchive/index.spec.tsx +91 -0
  126. package/js/hooks/useContractArchive/index.ts +64 -0
  127. package/js/hooks/useContracts/index.tsx +68 -0
  128. package/js/hooks/useCourseProductRelation/index.ts +8 -5
  129. package/js/hooks/useCourseProductUnion/index.spec.tsx +14 -10
  130. package/js/hooks/useCourseProductUnion/index.ts +6 -2
  131. package/js/hooks/useCourseProducts.ts +45 -0
  132. package/js/hooks/useCourseSearchParams/computeNewFilterValue.ts +3 -3
  133. package/js/hooks/useCourses/index.spec.tsx +2 -2
  134. package/js/hooks/useCourses/index.ts +4 -4
  135. package/js/hooks/useCreditCards/index.spec.tsx +4 -4
  136. package/js/hooks/useDashboardAddressForm.tsx +85 -87
  137. package/js/hooks/useDownloadCertificate/index.spec.tsx +19 -6
  138. package/js/hooks/useDownloadCertificate/index.tsx +2 -20
  139. package/js/hooks/useEnrollments.ts +1 -1
  140. package/js/hooks/useJoanieUserAbilities/index.not.isJoanieEnabled.spec.tsx +17 -0
  141. package/js/hooks/useJoanieUserAbilities/index.spec.tsx +68 -0
  142. package/js/hooks/useJoanieUserAbilities/index.tsx +11 -0
  143. package/js/hooks/useJoanieUserProfile.tsx +34 -0
  144. package/js/hooks/useOrders.ts +69 -26
  145. package/js/hooks/useOrganizations/index.ts +1 -1
  146. package/js/hooks/useProductOrder/index.spec.tsx +113 -0
  147. package/js/hooks/useProductOrder/index.tsx +33 -0
  148. package/js/hooks/useQueryKeyInvalidateListener.tsx +16 -0
  149. package/js/hooks/useResources/index.spec.tsx +30 -29
  150. package/js/hooks/useResources/index.tsx +11 -4
  151. package/js/hooks/useResources/useResourcesOmniscient.ts +2 -2
  152. package/js/hooks/useResources/useResourcesRoot.ts +21 -17
  153. package/js/hooks/useTeacherPendingContractsCount/index.ts +34 -0
  154. package/js/hooks/useUnionResource/index.spec.tsx +5 -2
  155. package/js/hooks/useUnionResource/index.ts +20 -3
  156. package/js/hooks/useUnionResource/utils/fetchEntity.ts +5 -4
  157. package/js/index.tsx +2 -2
  158. package/js/pages/DashboardAddressesManagement/DashboardAddressBox.tsx +1 -1
  159. package/js/pages/DashboardAddressesManagement/DashboardCreateAddress.spec.tsx +79 -72
  160. package/js/pages/DashboardAddressesManagement/DashboardCreateAddress.tsx +4 -4
  161. package/js/pages/DashboardAddressesManagement/DashboardEditAddress.spec.tsx +32 -23
  162. package/js/pages/DashboardAddressesManagement/DashboardEditAddress.tsx +6 -6
  163. package/js/pages/DashboardAddressesManagement/DashboardEditAddressLoader.tsx +2 -2
  164. package/js/pages/DashboardAddressesManagement/index.spec.tsx +4 -3
  165. package/js/pages/DashboardAddressesManagement/index.tsx +5 -5
  166. package/js/pages/DashboardCertificates/index.spec.tsx +3 -2
  167. package/js/pages/DashboardCertificates/index.tsx +2 -1
  168. package/js/pages/DashboardContracts/_styles.scss +8 -0
  169. package/js/pages/DashboardContracts/index.spec.tsx +147 -0
  170. package/js/pages/DashboardContracts/index.tsx +76 -0
  171. package/js/pages/DashboardCourses/index.spec.tsx +81 -61
  172. package/js/pages/DashboardCourses/index.tsx +15 -12
  173. package/js/pages/DashboardCourses/useOrdersEnrollments.tsx +34 -8
  174. package/js/pages/DashboardCreditCardsManagement/DashboardCreditCardBox.tsx +1 -1
  175. package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.spec.tsx +7 -6
  176. package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.tsx +43 -45
  177. package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCardLoader.tsx +2 -2
  178. package/js/pages/DashboardCreditCardsManagement/index.spec.tsx +13 -12
  179. package/js/pages/DashboardCreditCardsManagement/index.tsx +3 -3
  180. package/js/pages/DashboardOrderLayout/_styles.scss +5 -0
  181. package/js/pages/DashboardOrderLayout/index.spec.tsx +8 -8
  182. package/js/pages/DashboardOrderLayout/index.tsx +11 -6
  183. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.spec.tsx +358 -0
  184. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.tsx +129 -0
  185. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardCourseContractsLayout/index.tsx +26 -0
  186. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardOrganizationContractsLayout/index.tsx +26 -0
  187. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.spec.tsx +136 -0
  188. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.timer.spec.tsx +144 -0
  189. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.tsx +73 -0
  190. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +185 -0
  191. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.tsx +47 -0
  192. package/js/pages/TeacherDashboardContractsLayout/components/ContractFiltersBar/index.spec.tsx +179 -0
  193. package/js/pages/TeacherDashboardContractsLayout/components/ContractFiltersBar/index.tsx +86 -0
  194. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +109 -0
  195. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.tsx +60 -0
  196. package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.spec.tsx +124 -0
  197. package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.tsx +73 -0
  198. package/js/pages/TeacherDashboardContractsLayout/hooks/useDefaultOrganizationId/index.spec.tsx +134 -0
  199. package/js/pages/TeacherDashboardContractsLayout/hooks/useDefaultOrganizationId/index.tsx +28 -0
  200. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.spec.ts +85 -0
  201. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.ts +50 -0
  202. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.spec.tsx +266 -0
  203. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +153 -0
  204. package/js/pages/TeacherDashboardContractsLayout/hooks/useHasContractToDownload/index.spec.tsx +100 -0
  205. package/js/pages/TeacherDashboardContractsLayout/hooks/useHasContractToDownload/index.tsx +27 -0
  206. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.spec.tsx +193 -0
  207. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.tsx +44 -0
  208. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractsToSign.tsx +32 -0
  209. package/js/pages/TeacherDashboardContractsLayout/index.ts +2 -0
  210. package/js/pages/TeacherDashboardContractsLayout/styles.scss +15 -0
  211. package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/CourseRunList/CourseRunListCell/index.spec.tsx +1 -1
  212. package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/CourseRunList/_styles.scss +1 -2
  213. package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/CourseRunList/index.spec.tsx +2 -2
  214. package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/CourseRunList/utils.spec.tsx +1 -1
  215. package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/CourseRunList/utils.tsx +11 -10
  216. package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/index.tsx +9 -8
  217. package/js/pages/{TeacherCoursesDashboardLoader → TeacherDashboardCoursesLoader}/index.spec.tsx +17 -17
  218. package/js/pages/{TeacherCoursesDashboardLoader → TeacherDashboardCoursesLoader}/index.tsx +7 -7
  219. package/js/pages/{TeacherOrganizationCourseDashboardLoader → TeacherDashboardOrganizationCourseLoader}/index.tsx +5 -5
  220. package/js/pages/{TeacherTrainingDashboard/TeacherTrainingDashboardLoader.tsx → TeacherDashboardTraining/TeacherDashboardTrainingLoader.tsx} +10 -9
  221. package/js/pages/{TeacherTrainingDashboard → TeacherDashboardTraining}/index.spec.tsx +76 -6
  222. package/js/pages/{TeacherTrainingDashboard → TeacherDashboardTraining}/index.tsx +6 -6
  223. package/js/settings.dev.dist.ts +3 -0
  224. package/js/settings.ts +29 -1
  225. package/js/translations/ar-SA.json +1 -1
  226. package/js/translations/es-ES.json +1 -1
  227. package/js/translations/fa-IR.json +1 -1
  228. package/js/translations/fr-CA.json +1 -1
  229. package/js/translations/fr-FR.json +1 -1
  230. package/js/translations/ko-KR.json +1 -1
  231. package/js/translations/pt-PT.json +1 -1
  232. package/js/translations/ru-RU.json +1 -1
  233. package/js/translations/vi-VN.json +1 -0
  234. package/js/types/Joanie.ts +263 -80
  235. package/js/types/Suggestion.ts +2 -2
  236. package/js/types/User.ts +19 -1
  237. package/js/types/commonDataProps.ts +3 -0
  238. package/js/types/index.ts +1 -1
  239. package/js/utils/AbilitiesHelper/contractAbilities.spec.ts +35 -0
  240. package/js/utils/AbilitiesHelper/contractAbilities.ts +14 -0
  241. package/js/utils/AbilitiesHelper/index.ts +71 -0
  242. package/js/utils/AbilitiesHelper/joanieUserProfileAbilities.spec.ts +55 -0
  243. package/js/utils/AbilitiesHelper/joanieUserProfileAbilities.ts +16 -0
  244. package/js/utils/AbilitiesHelper/types.ts +36 -0
  245. package/js/utils/ContractHelper/index.spec.ts +73 -0
  246. package/js/utils/ContractHelper/index.ts +72 -0
  247. package/js/utils/CourseRuns/index.spec.tsx +20 -1
  248. package/js/utils/CourseRuns/index.ts +14 -2
  249. package/js/utils/CoursesHelper/index.spec.ts +45 -55
  250. package/js/utils/CoursesHelper/index.ts +6 -7
  251. package/js/utils/CreditCardHelper/index.spec.tsx +26 -22
  252. package/js/utils/CreditCardHelper/index.tsx +19 -6
  253. package/js/utils/ObjectHelper/index.spec.ts +18 -10
  254. package/js/utils/ObjectHelper/index.ts +9 -0
  255. package/js/utils/OrderHelper/index.ts +32 -0
  256. package/js/utils/ProductHelper/index.ts +5 -1
  257. package/js/utils/StringHelper/index.spec.tsx +11 -0
  258. package/js/utils/StringHelper/index.ts +8 -0
  259. package/js/utils/UserHelper/index.spec.ts +18 -0
  260. package/js/utils/UserHelper/index.ts +8 -0
  261. package/js/utils/download.ts +43 -0
  262. package/js/utils/errors/HttpError.ts +10 -0
  263. package/js/utils/indirection/window.ts +1 -1
  264. package/js/utils/react-query/createQueryClient.ts +12 -21
  265. package/js/utils/react-query/useLocalizedQueryKey.ts +1 -1
  266. package/js/utils/react-query/useSessionMutation/index.spec.tsx +8 -8
  267. package/js/utils/react-query/useSessionMutation/index.ts +6 -11
  268. package/js/utils/react-query/useSessionQuery/index.spec.tsx +36 -8
  269. package/js/utils/react-query/useSessionQuery/index.ts +14 -21
  270. package/js/utils/search/getSuggestionsSection/index.spec.ts +4 -3
  271. package/js/utils/search/getSuggestionsSection/index.ts +4 -1
  272. package/js/utils/search/index.tsx +8 -3
  273. package/js/utils/test/createTestQueryClient.ts +7 -7
  274. package/js/utils/test/expectBanner.ts +16 -3
  275. package/js/utils/test/factories/factories.ts +4 -4
  276. package/js/utils/test/factories/joanie.spec.ts +7 -0
  277. package/js/utils/test/factories/joanie.ts +214 -63
  278. package/js/utils/test/factories/reactQuery.ts +1 -1
  279. package/js/utils/test/factories/richie.ts +4 -2
  280. package/js/utils/test/mockCourseProductWithOrder.ts +28 -0
  281. package/js/utils/test/mockPaginatedResponse.ts +1 -1
  282. package/js/utils/test/render.tsx +72 -0
  283. package/js/utils/test/wrappers/IntlWrapper.tsx +23 -0
  284. package/js/utils/test/wrappers/JoanieAppWrapper.tsx +42 -0
  285. package/js/utils/test/wrappers/PresentationalAppWrapper.tsx +18 -0
  286. package/js/utils/test/wrappers/ReactQueryWrapper.tsx +16 -0
  287. package/js/utils/test/wrappers/RouterWrapper.tsx +29 -0
  288. package/js/utils/test/wrappers/types.ts +26 -0
  289. package/js/widgets/Dashboard/components/DashboardAvatar/_styles.scss +17 -5
  290. package/js/widgets/Dashboard/components/DashboardAvatar/index.spec.tsx +9 -2
  291. package/js/widgets/Dashboard/components/DashboardAvatar/index.tsx +16 -5
  292. package/js/widgets/Dashboard/components/DashboardBox/index.stories.tsx +1 -1
  293. package/js/widgets/Dashboard/components/DashboardBreadcrumbs/_styles.scss +1 -0
  294. package/js/widgets/Dashboard/components/DashboardBreadcrumbs/index.tsx +7 -4
  295. package/js/widgets/Dashboard/components/DashboardCard/index.spec.tsx +1 -1
  296. package/js/widgets/Dashboard/components/DashboardCard/index.stories.tsx +3 -3
  297. package/js/widgets/Dashboard/components/DashboardCard/index.tsx +2 -2
  298. package/js/widgets/Dashboard/components/DashboardItem/Certificate/index.spec.tsx +49 -9
  299. package/js/widgets/Dashboard/components/DashboardItem/Certificate/index.tsx +27 -73
  300. package/js/widgets/Dashboard/components/DashboardItem/CertificateStatus/index.spec.tsx +65 -0
  301. package/js/widgets/Dashboard/components/DashboardItem/CertificateStatus/index.tsx +59 -0
  302. package/js/widgets/Dashboard/components/DashboardItem/Contract/_styles.scss +29 -0
  303. package/js/widgets/Dashboard/components/DashboardItem/Contract/index.spec.tsx +197 -0
  304. package/js/widgets/Dashboard/components/DashboardItem/Contract/index.stories.tsx +34 -0
  305. package/js/widgets/Dashboard/components/DashboardItem/Contract/index.tsx +53 -0
  306. package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/hooks/useCourseRunPeriodMessage.ts +76 -0
  307. package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.spec.tsx +158 -0
  308. package/js/widgets/Dashboard/components/DashboardItem/{DashboardItemCourseEnrolling.stories.tsx → CourseEnrolling/index.stories.tsx} +6 -6
  309. package/js/widgets/Dashboard/components/DashboardItem/{DashboardItemCourseEnrolling.tsx → CourseEnrolling/index.tsx} +141 -84
  310. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.spec.tsx +40 -37
  311. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.tsx +29 -11
  312. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +248 -0
  313. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +89 -0
  314. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.spec.tsx +188 -117
  315. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +124 -78
  316. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.spec.tsx +299 -0
  317. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +286 -0
  318. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderReadonly.stories.tsx +9 -5
  319. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderWritable.stories.tsx +10 -6
  320. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.spec.tsx +121 -0
  321. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +98 -0
  322. package/js/widgets/Dashboard/components/DashboardItem/Order/_styles.scss +43 -0
  323. package/js/widgets/Dashboard/components/DashboardItem/_styles.scss +34 -37
  324. package/js/widgets/Dashboard/components/DashboardItem/index.spec.tsx +74 -4
  325. package/js/widgets/Dashboard/components/DashboardItem/index.stories.tsx +18 -0
  326. package/js/widgets/Dashboard/components/DashboardItem/index.tsx +91 -26
  327. package/js/widgets/Dashboard/components/DashboardItem/stories.mock.ts +4 -8
  328. package/js/widgets/Dashboard/components/DashboardLayout/_styles.scss +14 -5
  329. package/js/widgets/Dashboard/components/DashboardLayout/index.tsx +10 -3
  330. package/js/widgets/Dashboard/components/DashboardListAvatar/_styles.scss +8 -0
  331. package/js/widgets/Dashboard/components/DashboardListAvatar/index.tsx +11 -0
  332. package/js/widgets/Dashboard/components/DashboardOrderLoader/_styles.scss +5 -0
  333. package/js/widgets/Dashboard/components/DashboardOrderLoader/index.tsx +50 -14
  334. package/js/widgets/Dashboard/components/DashboardSidebar/_styles.scss +37 -24
  335. package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.spec.tsx +244 -0
  336. package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.tsx +49 -0
  337. package/js/widgets/Dashboard/components/DashboardSidebar/components/MenuNavLink/index.spec.tsx +40 -0
  338. package/js/widgets/Dashboard/components/DashboardSidebar/components/MenuNavLink/index.tsx +28 -0
  339. package/js/widgets/Dashboard/components/DashboardSidebar/components/NavigationSelect.tsx +58 -0
  340. package/js/widgets/Dashboard/components/DashboardSidebar/index.stories.tsx +11 -1
  341. package/js/widgets/Dashboard/components/DashboardSidebar/index.tsx +18 -69
  342. package/js/widgets/Dashboard/components/DashboardSidebar/utils.ts +6 -0
  343. package/js/widgets/Dashboard/components/FilterOrganization/index.tsx +58 -0
  344. package/js/widgets/Dashboard/components/FiltersBar/index.tsx +9 -0
  345. package/js/widgets/Dashboard/components/LearnerDashboardSidebar/index.tsx +4 -2
  346. package/js/widgets/Dashboard/components/NavigateWithParams/index.spec.tsx +31 -40
  347. package/js/widgets/Dashboard/components/RouterButton/index.tsx +2 -1
  348. package/js/widgets/Dashboard/components/Signature/DummyContractPlaceholder.tsx +25 -0
  349. package/js/widgets/Dashboard/components/Signature/SignatureDummy.tsx +58 -0
  350. package/js/widgets/Dashboard/components/Signature/SignatureLexPersona.tsx +72 -0
  351. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.spec.tsx +215 -0
  352. package/js/widgets/Dashboard/components/{TeacherCourseDashboardSidebar → TeacherDashboardCourseSidebar}/index.tsx +75 -31
  353. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/utils.ts +23 -0
  354. package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.spec.tsx +154 -0
  355. package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.stories.tsx +42 -0
  356. package/js/widgets/Dashboard/components/{TeacherOrganizationDashboardSidebar → TeacherDashboardOrganizationSidebar}/index.tsx +40 -29
  357. package/js/widgets/Dashboard/components/{TeacherProfileDashboardSidebar → TeacherDashboardProfileSidebar}/components/OrganizationLinks/_styles.scss +27 -10
  358. package/js/widgets/Dashboard/components/TeacherDashboardProfileSidebar/components/OrganizationLinks/index.spec.tsx +49 -0
  359. package/js/widgets/Dashboard/components/{TeacherProfileDashboardSidebar → TeacherDashboardProfileSidebar}/components/OrganizationLinks/index.tsx +18 -7
  360. package/js/widgets/Dashboard/components/{TeacherProfileDashboardSidebar → TeacherDashboardProfileSidebar}/index.spec.tsx +13 -39
  361. package/js/widgets/Dashboard/components/{TeacherProfileDashboardSidebar → TeacherDashboardProfileSidebar}/index.stories.tsx +5 -5
  362. package/js/widgets/Dashboard/components/{TeacherProfileDashboardSidebar → TeacherDashboardProfileSidebar}/index.tsx +4 -3
  363. package/js/widgets/Dashboard/hooks/useEnroll/index.ts +8 -8
  364. package/js/widgets/Dashboard/index.spec.tsx +22 -12
  365. package/js/widgets/Dashboard/utils/learnerRouteMessages.tsx +12 -1
  366. package/js/widgets/Dashboard/utils/learnerRoutes.tsx +6 -0
  367. package/js/widgets/Dashboard/utils/teacherRouteMessages.tsx +60 -4
  368. package/js/widgets/Dashboard/utils/teacherRoutes.tsx +87 -13
  369. package/js/widgets/LtiConsumer/index.spec.tsx +44 -33
  370. package/js/widgets/LtiConsumer/index.tsx +11 -15
  371. package/js/widgets/Search/components/SearchFilterGroup/index.spec.tsx +0 -5
  372. package/js/widgets/Search/components/SearchFilterGroupModal/_styles.scss +0 -9
  373. package/js/widgets/Search/components/SearchFilterGroupModal/index.spec.tsx +0 -5
  374. package/js/widgets/Search/components/SearchFilterGroupModal/index.tsx +86 -60
  375. package/js/widgets/Search/components/SearchFilterValueParent/index.stories.tsx +51 -0
  376. package/js/widgets/Search/components/SearchFilterValueParent/index.tsx +7 -7
  377. package/js/widgets/Search/components/SearchFiltersPane/_styles.scss +2 -16
  378. package/js/widgets/Search/components/SearchFiltersPane/index.tsx +9 -6
  379. package/js/widgets/Search/hooks/useCourseSearch/index.ts +13 -7
  380. package/js/widgets/Search/index.spec.tsx +3 -2
  381. package/js/widgets/Search/utils/getResourceList/index.spec.ts +12 -5
  382. package/js/widgets/Search/utils/getResourceList/index.ts +6 -2
  383. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +74 -0
  384. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/_styles.scss +11 -14
  385. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCertificateItem/_styles.scss +1 -1
  386. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCertificateItem/index.spec.tsx +13 -2
  387. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCertificateItem/index.stories.tsx +33 -0
  388. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCertificateItem/index.tsx +5 -8
  389. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCourseRuns/CourseRunList.tsx +1 -1
  390. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCourseRuns/EnrollableCourseRunList.tsx +16 -8
  391. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCourseRuns/EnrolledCourseRun.tsx +5 -3
  392. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCourseRuns/_styles.scss +1 -0
  393. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCourseRuns/index.spec.tsx +100 -35
  394. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseRunItem/index.spec.tsx +7 -4
  395. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.stories.tsx +36 -0
  396. package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseRunItem/index.tsx +5 -3
  397. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader/index.tsx +40 -0
  398. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +898 -0
  399. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +83 -0
  400. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +253 -0
  401. package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/CourseRunUnenrollmentButton/index.tsx +3 -2
  402. package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/_styles.scss +0 -25
  403. package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/index.joanie.spec.tsx +73 -30
  404. package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/index.openedx.spec.tsx +10 -5
  405. package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/index.tsx +54 -26
  406. package/js/widgets/SyllabusCourseRunsList/components/CourseRunItemWithEnrollment/index.tsx +1 -0
  407. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/_styles.scss +0 -3
  408. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/hooks/useCourseWish/index.spec.tsx +12 -9
  409. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.login.spec.tsx +14 -12
  410. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.logout.spec.tsx +4 -6
  411. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.tsx +5 -5
  412. package/js/widgets/SyllabusCourseRunsList/components/SyllabusAsideList/index.tsx +2 -2
  413. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.tsx +19 -13
  414. package/js/widgets/SyllabusCourseRunsList/components/SyllabusSimpleCourseRunsList/index.tsx +1 -0
  415. package/js/widgets/SyllabusCourseRunsList/hooks/useCourseEnrollment/index.spec.tsx +2 -1
  416. package/js/widgets/SyllabusCourseRunsList/hooks/useCourseEnrollment/index.ts +14 -19
  417. package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +54 -14
  418. package/js/widgets/SyllabusCourseRunsList/index.tsx +5 -8
  419. package/js/widgets/UserLogin/components/UserMenu/DesktopUserMenu.tsx +2 -1
  420. package/js/widgets/UserLogin/components/UserMenu/MobileUserMenu.tsx +2 -1
  421. package/js/widgets/UserLogin/components/UserMenu/index.tsx +6 -5
  422. package/js/widgets/UserLogin/index.not.isJoanieEnabled.spec.tsx +120 -0
  423. package/js/widgets/UserLogin/index.spec.tsx +108 -43
  424. package/js/widgets/UserLogin/index.stories.tsx +29 -0
  425. package/js/widgets/UserLogin/index.tsx +33 -15
  426. package/js/widgets/index.tsx +3 -6
  427. package/mocks/browser.ts +1 -1
  428. package/mocks/handlers/contracts.ts +16 -0
  429. package/mocks/handlers.ts +3 -1
  430. package/package.json +82 -78
  431. package/scss/_main.scss +2 -0
  432. package/scss/colors/_palette.scss +2 -2
  433. package/scss/colors/_theme.scss +6 -16
  434. package/scss/components/_content.scss +1 -1
  435. package/scss/components/_index.scss +24 -14
  436. package/scss/generic/_type.scss +1 -1
  437. package/scss/objects/_characteristics.scss +7 -14
  438. package/scss/objects/_course_glimpses.scss +3 -7
  439. package/scss/objects/_dashboard.scss +28 -0
  440. package/scss/objects/_form.scss +14 -355
  441. package/scss/objects/_index.scss +1 -0
  442. package/scss/objects/_list.scss +8 -0
  443. package/scss/objects/_organization_glimpses.scss +2 -8
  444. package/scss/objects/_selector.scss +1 -0
  445. package/scss/trumps/_bootstrap.scss +4 -0
  446. package/scss/vendors/css/cunningham-tokens.css +89 -25
  447. package/scss/vendors/cunningham-tokens.scss +208 -128
  448. package/js/components/AddressesManagement/AddressForm.spec.tsx +0 -206
  449. package/js/components/AddressesManagement/AddressForm.tsx +0 -169
  450. package/js/components/Button/index.spec.tsx +0 -36
  451. package/js/components/Button/index.stories.tsx +0 -26
  452. package/js/components/Button/index.tsx +0 -38
  453. package/js/components/Form/CheckboxField.stories.tsx +0 -12
  454. package/js/components/Form/Field.stories.config.tsx +0 -24
  455. package/js/components/Form/Inputs.tsx +0 -295
  456. package/js/components/Form/RadioField.stories.tsx +0 -18
  457. package/js/components/Form/SelectField.stories.tsx +0 -27
  458. package/js/components/Form/TextAreaField.stories.tsx +0 -12
  459. package/js/components/Form/TextField.stories.tsx +0 -12
  460. package/js/components/Form/index.spec.tsx +0 -297
  461. package/js/hooks/useProduct.ts +0 -28
  462. package/js/utils/test/mockProductWithOrder.ts +0 -17
  463. package/js/widgets/CourseProductItem/components/PaymentButton/_styles.scss +0 -12
  464. package/js/widgets/CourseProductItem/components/PaymentButton/index.spec.tsx +0 -473
  465. package/js/widgets/CourseProductItem/components/PurchaseButton/index.spec.tsx +0 -259
  466. package/js/widgets/CourseProductItem/components/SaleTunnel/_styles.scss +0 -41
  467. package/js/widgets/CourseProductItem/components/SaleTunnelStepResume/_styles.scss +0 -130
  468. package/js/widgets/CourseProductItem/components/SaleTunnelStepResume/index.spec.tsx +0 -29
  469. package/js/widgets/CourseProductItem/components/SaleTunnelStepResume/index.tsx +0 -59
  470. package/js/widgets/CourseProductItem/components/SaleTunnelStepValidation/index.spec.tsx +0 -71
  471. package/js/widgets/CourseProductItem/contexts/CourseProductContext/index.spec.tsx +0 -35
  472. package/js/widgets/CourseProductItem/contexts/CourseProductContext/index.tsx +0 -45
  473. package/js/widgets/CourseProductItem/index.spec.tsx +0 -486
  474. package/js/widgets/CourseProductItem/index.tsx +0 -205
  475. package/js/widgets/Dashboard/components/DashboardItem/DashboardItemCourseEnrolling.spec.tsx +0 -64
  476. package/js/widgets/Dashboard/components/TeacherCourseDashboardSidebar/index.spec.tsx +0 -105
  477. package/js/widgets/Dashboard/components/TeacherOrganizationDashboardSidebar/index.stories.tsx +0 -28
  478. /package/js/components/AddressesManagement/{validationSchema.spec.ts → AddressForm/validationSchema.spec.ts} +0 -0
  479. /package/js/{widgets/CourseProductItem/components → components}/EnrollmentDate/index.tsx +0 -0
  480. /package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/RegisteredCreditCard/_styles.scss +0 -0
  481. /package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/RegisteredCreditCard/index.spec.tsx +0 -0
  482. /package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/SaleTunnelStepValidation/TargetCourseDetail.tsx +0 -0
  483. /package/js/{widgets/CourseProductItem → components/SaleTunnel}/components/StepBreadcrumb/_styles.scss +0 -0
  484. /package/js/{widgets/CourseProductItem/hooks → hooks}/useStepManager/index.spec.ts +0 -0
  485. /package/js/{widgets/CourseProductItem/hooks → hooks}/useStepManager/index.ts +0 -0
  486. /package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/CourseRunList/CourseRunListCell/index.tsx +0 -0
  487. /package/js/pages/{TeacherCourseDashboardLoader → TeacherDashboardCourseLoader}/CourseRunList/index.tsx +0 -0
  488. /package/js/pages/{TeacherTrainingDashboard → TeacherDashboardTraining}/_styles.scss +0 -0
  489. /package/js/widgets/Dashboard/components/{TeacherCourseDashboardSidebar → TeacherDashboardCourseSidebar}/_styles.scss +0 -0
  490. /package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCourseRuns/CourseRunSection.tsx +0 -0
  491. /package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/components/CourseProductCourseRuns/index.tsx +0 -0
  492. /package/js/widgets/{CourseProductItem → SyllabusCourseRunsList/components/CourseProductItem}/types/payments/payplug.d.ts +0 -0
@@ -0,0 +1,286 @@
1
+ // FIXME: this test is about useUnionResource behavior.
2
+ // we need to rewrite it in useUnionResource tests suite as small and generic as possible.
3
+ import { QueryClientProvider } from '@tanstack/react-query';
4
+ import { IntlProvider } from 'react-intl';
5
+ import fetchMock from 'fetch-mock';
6
+ import { act, render, screen, waitFor, within } from '@testing-library/react';
7
+ import userEvent from '@testing-library/user-event';
8
+ import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
9
+ import { createTestQueryClient } from 'utils/test/createTestQueryClient';
10
+ import { SessionProvider } from 'contexts/SessionContext';
11
+ import { DashboardTest } from 'widgets/Dashboard/components/DashboardTest';
12
+ import {
13
+ ContractFactory,
14
+ CredentialOrderFactory,
15
+ TargetCourseFactory,
16
+ } from 'utils/test/factories/joanie';
17
+ import { mockCourseProductWithOrder } from 'utils/test/mockCourseProductWithOrder';
18
+ import { LearnerDashboardPaths } from 'widgets/Dashboard/utils/learnerRouteMessages';
19
+ import { Deferred } from 'utils/test/deferred';
20
+ import { expectNoSpinner, expectSpinner } from 'utils/test/expectSpinner';
21
+ import { CONTRACT_SETTINGS } from 'settings';
22
+
23
+ jest.mock('utils/context', () => ({
24
+ __esModule: true,
25
+ default: mockRichieContextFactory({
26
+ authentication: { backend: 'fonzie', endpoint: 'https://demo.endpoint' },
27
+ joanie_backend: { endpoint: 'https://joanie.endpoint' },
28
+ }).one(),
29
+ }));
30
+
31
+ jest.mock('hooks/useIntersectionObserver', () => ({
32
+ useIntersectionObserver: (props: any) => {
33
+ (globalThis as any).__intersection_observer_props__ = props;
34
+ },
35
+ }));
36
+
37
+ describe('<DashboardItemOrder/> Contract', () => {
38
+ const Wrapper = (route: string) => {
39
+ return (
40
+ <QueryClientProvider client={createTestQueryClient({ user: true })}>
41
+ <IntlProvider locale="en">
42
+ <SessionProvider>
43
+ <DashboardTest initialRoute={route} />
44
+ </SessionProvider>
45
+ </IntlProvider>
46
+ </QueryClientProvider>
47
+ );
48
+ };
49
+
50
+ beforeEach(() => {
51
+ jest.useFakeTimers();
52
+ jest.clearAllTimers();
53
+
54
+ fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', []);
55
+ fetchMock.get('https://joanie.endpoint/api/v1.0/credit-cards/', []);
56
+ fetchMock.get('https://joanie.endpoint/api/v1.0/orders/', []);
57
+ });
58
+
59
+ afterEach(() => {
60
+ jest.restoreAllMocks();
61
+ jest.clearAllMocks();
62
+ fetchMock.restore();
63
+ });
64
+
65
+ describe('writable', () => {
66
+ it('successfully sign a contract', async () => {
67
+ const order = CredentialOrderFactory({
68
+ target_courses: TargetCourseFactory().many(1),
69
+ target_enrollments: [],
70
+ contract: ContractFactory({ student_signed_on: undefined }).one(),
71
+ }).one();
72
+
73
+ // learner dashboard course page do one call to course product relation per order
74
+ const { product } = mockCourseProductWithOrder(order);
75
+
76
+ // overwrite useOmniscientOrders call
77
+ fetchMock.get(
78
+ 'https://joanie.endpoint/api/v1.0/orders/',
79
+ { results: [order], next: null, previous: null, count: null },
80
+ { overwriteRoutes: true },
81
+ );
82
+
83
+ // mock useUnionResources calls
84
+ fetchMock.get('begin:https://joanie.endpoint/api/v1.0/enrollments/', {
85
+ results: [],
86
+ next: null,
87
+ previous: null,
88
+ count: null,
89
+ });
90
+
91
+ fetchMock.get(
92
+ 'https://joanie.endpoint/api/v1.0/orders/?product_type=credential&state_exclude=canceled&page=1&page_size=50',
93
+ { results: [order], next: null, previous: null, count: null },
94
+ );
95
+
96
+ const submitDeferred = new Deferred();
97
+ fetchMock.post(
98
+ `https://joanie.endpoint/api/v1.0/orders/${order.id}/submit_for_signature/`,
99
+ submitDeferred.promise,
100
+ );
101
+
102
+ // delay: null is needed because as we are using fake timers it would mock the timers of
103
+ // RTL too. See https://github.com/testing-library/user-event/issues/833.
104
+ const user = userEvent.setup({ delay: null });
105
+
106
+ render(Wrapper(LearnerDashboardPaths.COURSES));
107
+
108
+ // console.log('apiCalls:2', fetchMock.calls().length);
109
+ await expectNoSpinner('Loading orders and enrollments...');
110
+
111
+ expect(
112
+ await screen.findByRole('heading', { level: 5, name: product.title }),
113
+ ).toBeInTheDocument();
114
+
115
+ // Make sure the sign button is shown.
116
+ await user.click(screen.getByRole('link', { name: 'Sign' }));
117
+
118
+ // Contract is shown and not in loading state.
119
+ const contractElement = await screen.findByTestId(`dashboard-item-contract-${order.id}`);
120
+ expect(within(contractElement).queryByRole('status')).not.toBeInTheDocument();
121
+ const signButton = screen.getByRole('button', { name: 'Sign' });
122
+ expect(signButton).not.toHaveAttribute('disabled');
123
+
124
+ // The modal is not shown.
125
+ expect(screen.queryByTestId('dashboard-contract-frame')).not.toBeInTheDocument();
126
+
127
+ await user.click(signButton);
128
+
129
+ // Modal is opened.
130
+ const modal = screen.getByTestId('dashboard-contract-frame');
131
+
132
+ // Waiting for submit route.
133
+ within(modal).queryByRole('header', { name: 'Loading your contract ...' });
134
+ within(modal).queryByRole('status');
135
+
136
+ // Resolve submit request.
137
+ await act(async () => {
138
+ submitDeferred.resolve({
139
+ invitation_link:
140
+ 'https://dummysignaturebackend.fr/?requestToken=wfl_fake_dummy_dbe038b3-b6fe-40f4-b5bb-101fc80047a6#requestId=req',
141
+ });
142
+ });
143
+
144
+ // The contract is displayed.
145
+ within(modal).queryByTestId('dummy-contract-placeholder');
146
+ const contractSignButton = within(modal).getByRole('button', { name: 'Sign' });
147
+
148
+ // Sign the contract.
149
+ await user.click(contractSignButton);
150
+
151
+ // Fake loading screen.
152
+ within(modal).queryByRole('header', { name: 'Signing the contract ...' });
153
+ within(modal).queryByRole('status');
154
+
155
+ // Mock polling request.
156
+ let calls = 0;
157
+ const orderDeferredFirst = new Deferred();
158
+ const orderDeferredSecond = new Deferred();
159
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/orders/${order.id}/`, () => {
160
+ calls++;
161
+ if (calls === 1) {
162
+ return orderDeferredFirst.promise;
163
+ }
164
+ return orderDeferredSecond.promise;
165
+ });
166
+
167
+ expect(fetchMock.calls(`https://joanie.endpoint/api/v1.0/orders/${order.id}/`).length).toBe(
168
+ 0,
169
+ );
170
+
171
+ // Polling starts and succeeds after the second call.
172
+ await act(async () => {
173
+ // We prefer advanceTimersByTime over runOnlyPendingTimers, because the latter would trigger internal
174
+ // react-query garbage collection, which is not what we want as we want to make sure the cache is well
175
+ // handled by fetchEntity ( useUnionResources ) by verifying that isInvalidated is true. ( Otherwise we would
176
+ // have got a undefined getQueryState(...) result. That's why we test that the "Sign" button from the
177
+ // courses view is well removed.
178
+ jest.advanceTimersByTime(CONTRACT_SETTINGS.pollInterval + 50);
179
+ });
180
+ await within(modal).findByRole('heading', { name: 'Verifying signature ...' });
181
+ within(modal).queryByRole('status');
182
+
183
+ // Verify the route has been called.
184
+ expect(fetchMock.calls(`https://joanie.endpoint/api/v1.0/orders/${order.id}/`).length).toBe(
185
+ 1,
186
+ );
187
+
188
+ // Resolve the first request.
189
+ await act(async () => {
190
+ orderDeferredFirst.resolve({
191
+ ...order,
192
+ contract: undefined,
193
+ });
194
+ });
195
+
196
+ // Still displaying pending message.
197
+ await within(modal).findByRole('heading', { name: 'Verifying signature ...' });
198
+ within(modal).queryByRole('status');
199
+
200
+ // Fast-forward the second polling request.
201
+ await act(async () => {
202
+ // We prefer advanceTimersByTime over runOnlyPendingTimers, because the latter would trigger internal
203
+ // react-query garbage collection, which is not what we want as we want to make sure the cache is well
204
+ // handled by fetchEntity ( useUnionResources ) by verifying that isInvalidated is true. ( Otherwise we would
205
+ // have got a undefined getQueryState(...) result. That's why we test that the "Sign" button from the
206
+ // courses view is well removed.
207
+ jest.advanceTimersByTime(CONTRACT_SETTINGS.pollInterval + 50);
208
+ });
209
+
210
+ // We update the orders mock in order to return a signed contract before resolving the polling.
211
+ const signedOrderDeferred = new Deferred();
212
+ fetchMock.get('https://joanie.endpoint/api/v1.0/orders/', signedOrderDeferred.promise, {
213
+ overwriteRoutes: true,
214
+ });
215
+
216
+ // Resolve the second request.
217
+ await act(async () => {
218
+ orderDeferredSecond.resolve({
219
+ ...order,
220
+ contract: {
221
+ ...order.contract,
222
+ student_signed_on: new Date().toISOString(),
223
+ },
224
+ });
225
+ });
226
+
227
+ // We have the success message.
228
+ await screen.findByRole('heading', { name: 'Congratulations!' });
229
+ screen.getByText(
230
+ 'You will receive an email once your contract will be fully signed. You can now enroll in your course runs!',
231
+ );
232
+ const nextButton = screen.getByRole('button', { name: 'Next' });
233
+
234
+ // Next closes the modal.
235
+ await user.click(nextButton);
236
+
237
+ // Contract sign button must prevent any interaction, waiting for order re-fetch.
238
+ const orderDetailsSignButton = within(
239
+ screen.getByTestId(`dashboard-item-contract-${order.id}`),
240
+ ).getByRole('button', {
241
+ name: 'Sign',
242
+ });
243
+ expect(orderDetailsSignButton).toHaveAttribute('disabled');
244
+
245
+ // Resolve the refresh order request.
246
+ const signedOrder = {
247
+ ...order,
248
+ contract: {
249
+ ...order.contract,
250
+ student_signed_on: new Date().toISOString(),
251
+ },
252
+ };
253
+ signedOrderDeferred.resolve({
254
+ results: [signedOrder],
255
+ next: null,
256
+ previous: null,
257
+ count: null,
258
+ });
259
+
260
+ // Contract signing is removed.
261
+ await waitFor(() =>
262
+ expect(screen.queryByRole('button', { name: 'Sign' })).not.toBeInTheDocument(),
263
+ );
264
+
265
+ // Go back to the list view to make sure the sign button is not shown anymore.
266
+ fetchMock.get(
267
+ 'https://joanie.endpoint/api/v1.0/orders/?product_type=credential&state_exclude=canceled&page=1&page_size=50',
268
+ { results: [signedOrder], next: null, previous: null, count: null },
269
+ { overwriteRoutes: true },
270
+ );
271
+
272
+ const $backButton = screen.getByRole('link', { name: /Back/ });
273
+ await user.click($backButton);
274
+
275
+ await expectSpinner('Loading orders and enrollments...');
276
+ await expectNoSpinner('Loading orders and enrollments...');
277
+
278
+ expect(
279
+ await screen.findByRole('heading', { level: 5, name: product.title }),
280
+ ).toBeInTheDocument();
281
+
282
+ // Make sure the sign button is not shown.
283
+ expect(screen.queryByRole('link', { name: 'Sign' })).not.toBeInTheDocument();
284
+ });
285
+ });
286
+ });
@@ -1,13 +1,17 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
  import { createMemoryRouter, RouterProvider } from 'react-router-dom';
3
- import { Order, OrderState, Product, TargetCourse } from 'types/Joanie';
4
- import { OrderFactory, ProductFactory, TargetCourseFactory } from 'utils/test/factories/joanie';
3
+ import { CredentialOrder, OrderState, Product, TargetCourse } from 'types/Joanie';
4
+ import {
5
+ CredentialOrderFactory,
6
+ ProductFactory,
7
+ TargetCourseFactory,
8
+ } from 'utils/test/factories/joanie';
5
9
  import { QueryStateFactory } from 'utils/test/factories/reactQuery';
6
10
  import { StorybookHelper } from 'utils/StorybookHelper';
7
11
  import { enrollment } from '../stories.mock';
8
12
  import { DashboardItemOrder } from './DashboardItemOrder';
9
13
 
10
- const order: Order = { ...OrderFactory().one(), target_courses: [] };
14
+ const order: CredentialOrder = CredentialOrderFactory({ target_courses: [] }).one();
11
15
  const product: Product = ProductFactory().one();
12
16
  const targetsCourses: TargetCourse[] = TargetCourseFactory().many(3);
13
17
 
@@ -33,7 +37,7 @@ export default {
33
37
  />,
34
38
  {
35
39
  queriesCallback: (queries) => {
36
- queries.push(QueryStateFactory(['product', order.product], { data: product }));
40
+ queries.push(QueryStateFactory(['product', order.product_id], { data: product }));
37
41
  },
38
42
  },
39
43
  ),
@@ -60,7 +64,7 @@ export const MultipleTargetCourses: Story = {
60
64
  order: {
61
65
  ...order,
62
66
  target_courses: targetsCourses,
63
- enrollments: [
67
+ target_enrollments: [
64
68
  {
65
69
  ...enrollment,
66
70
  course_run: { ...targetsCourses[1].course_runs[0] },
@@ -1,12 +1,16 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
- import { Order, OrderState, Product, TargetCourse } from 'types/Joanie';
2
+ import { CredentialOrder, OrderState, Product, TargetCourse } from 'types/Joanie';
3
3
  import { QueryStateFactory } from 'utils/test/factories/reactQuery';
4
- import { OrderFactory, ProductFactory, TargetCourseFactory } from 'utils/test/factories/joanie';
4
+ import {
5
+ CredentialOrderFactory,
6
+ ProductFactory,
7
+ TargetCourseFactory,
8
+ } from 'utils/test/factories/joanie';
5
9
  import { StorybookHelper } from 'utils/StorybookHelper';
6
10
  import { enrollment } from '../stories.mock';
7
11
  import { DashboardItemOrder } from './DashboardItemOrder';
8
12
 
9
- const order: Order = { ...OrderFactory().one(), target_courses: [] };
13
+ const order: CredentialOrder = CredentialOrderFactory({ target_courses: [] }).one();
10
14
  const product: Product = ProductFactory().one();
11
15
  const targetsCourses: TargetCourse[] = TargetCourseFactory().many(3);
12
16
 
@@ -23,7 +27,7 @@ export default {
23
27
  render: (args) =>
24
28
  StorybookHelper.wrapInApp(<DashboardItemOrder {...args} />, {
25
29
  queriesCallback: (queries) => {
26
- queries.push(QueryStateFactory(['product', order.product], { data: product }));
30
+ queries.push(QueryStateFactory(['product', order.product_id], { data: product }));
27
31
  },
28
32
  }),
29
33
  } as Meta<typeof DashboardItemOrder>;
@@ -54,8 +58,8 @@ export const MultipleTargetCourses: Story = {
54
58
  args: {
55
59
  order: {
56
60
  ...order,
57
- target_courses: [...targetsCourses, { ...TargetCourseFactory().one(), course_runs: [] }],
58
- enrollments: [
61
+ target_courses: [...targetsCourses, TargetCourseFactory({ course_runs: [] }).one()],
62
+ target_enrollments: [
59
63
  {
60
64
  ...enrollment,
61
65
  course_run: { ...targetsCourses[1].course_runs[0] },
@@ -0,0 +1,121 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { IntlProvider, createIntl } from 'react-intl';
4
+ import {
5
+ ContractDefinitionFactory,
6
+ ContractFactory,
7
+ CredentialOrderFactory,
8
+ } from 'utils/test/factories/joanie';
9
+ import { OrderState } from 'types/Joanie';
10
+ import OrderStateMessage, { messages } from '.';
11
+
12
+ const intl = createIntl({ locale: 'en' });
13
+
14
+ /*
15
+ @TODO (rlecellier): Rewrite everything with these guidlignes
16
+ Order lifecycle:
17
+ * Draft: order has been created
18
+ * Submitted: order information have been validated
19
+ * Pending: payment has failed but can be retried
20
+ * Validated:
21
+ * Completed (Validated with generated certificate)
22
+ * On going (Validated without generated certificate)
23
+ * Signature needed: !order.product.contract.isSign
24
+ * Canceled: has been canceled
25
+ */
26
+ describe('<DashboardItemOrder/>', () => {
27
+ const Wrapper = ({ children }: PropsWithChildren) => (
28
+ <IntlProvider locale="en">{children}</IntlProvider>
29
+ );
30
+
31
+ it.each([
32
+ [OrderState.DRAFT, 'Draft'],
33
+ [OrderState.SUBMITTED, 'Submitted'],
34
+ [OrderState.PENDING, 'Pending'],
35
+ [OrderState.CANCELED, 'Canceled'],
36
+ ])(
37
+ 'should display message from order state: %s when order have no contract',
38
+ (state, expectedMessage) => {
39
+ const order = CredentialOrderFactory({ state }).one();
40
+ render(
41
+ <Wrapper>
42
+ <OrderStateMessage order={order} />
43
+ </Wrapper>,
44
+ );
45
+ expect(screen.getByText(expectedMessage)).toBeInTheDocument();
46
+ },
47
+ );
48
+
49
+ it.each([
50
+ [OrderState.DRAFT, 'Draft'],
51
+ [OrderState.SUBMITTED, 'Submitted'],
52
+ [OrderState.PENDING, 'Pending'],
53
+ [OrderState.CANCELED, 'Canceled'],
54
+ ])(
55
+ 'should display message from order state: %s when order have no contract',
56
+ (state, expectedMessage) => {
57
+ const orderWithContract = CredentialOrderFactory({
58
+ state,
59
+ contract: ContractFactory().one(),
60
+ }).one();
61
+ render(
62
+ <Wrapper>
63
+ <OrderStateMessage order={orderWithContract} />
64
+ </Wrapper>,
65
+ );
66
+ expect(screen.getByText(expectedMessage)).toBeInTheDocument();
67
+ },
68
+ );
69
+
70
+ it('should display message for validated order that need learner signature', () => {
71
+ const order = CredentialOrderFactory({
72
+ state: OrderState.VALIDATED,
73
+ contract: null,
74
+ }).one();
75
+
76
+ const contractDefinition = ContractDefinitionFactory().one();
77
+
78
+ render(
79
+ <Wrapper>
80
+ <OrderStateMessage order={order} contractDefinition={contractDefinition} />
81
+ </Wrapper>,
82
+ );
83
+ expect(screen.getByText('Signature required')).toBeInTheDocument();
84
+ });
85
+
86
+ it("should display message for validated order that don't have a generated certificate", () => {
87
+ const order = CredentialOrderFactory({
88
+ state: OrderState.VALIDATED,
89
+ contract: ContractFactory({ student_signed_on: new Date().toISOString() }).one(),
90
+ certificate_id: undefined,
91
+ }).one();
92
+ render(
93
+ <Wrapper>
94
+ <OrderStateMessage order={order} />
95
+ </Wrapper>,
96
+ );
97
+ expect(
98
+ screen.getByText(intl.formatMessage(messages.statusOnGoing), {
99
+ exact: false,
100
+ }),
101
+ );
102
+ });
103
+
104
+ it('should display message for validated order that have a generated certificate', () => {
105
+ const order = CredentialOrderFactory({
106
+ state: OrderState.VALIDATED,
107
+ contract: ContractFactory({ student_signed_on: new Date().toISOString() }).one(),
108
+ certificate_id: 'FAKE_CERTIFICATE_ID',
109
+ }).one();
110
+ render(
111
+ <Wrapper>
112
+ <OrderStateMessage order={order} />
113
+ </Wrapper>,
114
+ );
115
+ expect(
116
+ screen.getByText(intl.formatMessage(messages.statusCompleted), {
117
+ exact: false,
118
+ }),
119
+ );
120
+ });
121
+ });
@@ -0,0 +1,98 @@
1
+ import { FormattedMessage, defineMessages } from 'react-intl';
2
+ import { useEffect } from 'react';
3
+ import { CertificateOrder, CredentialOrder, OrderState, ContractDefinition } from 'types/Joanie';
4
+ import { StringHelper } from 'utils/StringHelper';
5
+ import { handle } from 'utils/errors/handle';
6
+ import { OrderHelper } from 'utils/OrderHelper';
7
+
8
+ export const messages = defineMessages({
9
+ statusDraft: {
10
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusDraft',
11
+ description: 'Status shown on the dashboard order item when order is draft.',
12
+ defaultMessage: 'Draft',
13
+ },
14
+ statusSubmitted: {
15
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusSubmitted',
16
+ description: 'Status shown on the dashboard order item when order is submitted.',
17
+ defaultMessage: 'Submitted',
18
+ },
19
+ statusPending: {
20
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusPending',
21
+ description: 'Status shown on the dashboard order item when order is pending.',
22
+ defaultMessage: 'Pending',
23
+ },
24
+ statusOnGoing: {
25
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusOnGoing',
26
+ description:
27
+ 'Status shown on the dashboard order item when order is validated with no certificate',
28
+ defaultMessage: 'On going',
29
+ },
30
+ statusCompleted: {
31
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusCompleted',
32
+ description:
33
+ 'Status shown on the dashboard order item when order is validated with certificate',
34
+ defaultMessage: 'Completed',
35
+ },
36
+ statusWaitingSignature: {
37
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusWaitingSignature',
38
+ description:
39
+ "Status shown on the dashboard order item when order is validated with contract's learner signature missing.",
40
+ defaultMessage: 'Signature required',
41
+ },
42
+ statusCanceled: {
43
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusCanceled',
44
+ description: 'Status shown on the dashboard order item when order is canceled',
45
+ defaultMessage: 'Canceled',
46
+ },
47
+ statusOther: {
48
+ id: 'components.DashboardItem.Order.OrderStateMessage.statusOther',
49
+ description: 'Status shown on the dashboard order item when order status is unknown',
50
+ defaultMessage: '{state}',
51
+ },
52
+ });
53
+
54
+ interface OrderStateMessageProps {
55
+ order: CredentialOrder | CertificateOrder;
56
+ contractDefinition?: ContractDefinition;
57
+ }
58
+
59
+ const OrderStateMessage = ({ order, contractDefinition }: OrderStateMessageProps) => {
60
+ const { certificate_id: certificateId } = order;
61
+ const orderStatusMessages = {
62
+ [OrderState.DRAFT]: messages.statusDraft,
63
+ [OrderState.SUBMITTED]: messages.statusSubmitted,
64
+ [OrderState.PENDING]: messages.statusPending,
65
+ [OrderState.CANCELED]: messages.statusCanceled,
66
+ };
67
+
68
+ useEffect(() => {
69
+ if (!Object.values(OrderState).includes(order.state)) {
70
+ handle(new Error(`Unknown order state ${order.state}`));
71
+ }
72
+ }, [order.state]);
73
+
74
+ if (order.state === OrderState.VALIDATED) {
75
+ if (OrderHelper.orderNeedsSignature(order, contractDefinition)) {
76
+ return <FormattedMessage {...messages.statusWaitingSignature} />;
77
+ }
78
+
79
+ if (certificateId) {
80
+ return <FormattedMessage {...messages.statusCompleted} />;
81
+ } else {
82
+ return <FormattedMessage {...messages.statusOnGoing} />;
83
+ }
84
+ }
85
+
86
+ if (order.state in orderStatusMessages) {
87
+ return <FormattedMessage {...orderStatusMessages[order.state]} />;
88
+ }
89
+
90
+ return (
91
+ <FormattedMessage
92
+ {...messages.statusOther}
93
+ values={{ state: StringHelper.capitalizeFirst(order.state) }}
94
+ />
95
+ );
96
+ };
97
+
98
+ export default OrderStateMessage;
@@ -0,0 +1,43 @@
1
+ .dashboard-item-order {
2
+ > .dashboard-item,
3
+ .dashboard__contract-item {
4
+ padding-top: rem-calc(16px);
5
+ &:first-child {
6
+ padding-top: 0;
7
+ }
8
+ }
9
+ &__footer {
10
+ display: flex;
11
+ justify-content: space-between;
12
+ align-items: center;
13
+ padding: 0.5rem 1rem;
14
+ min-height: 3.75rem;
15
+ border-bottom: $onepixel solid r-theme-val(dashboard-item, base-border);
16
+ &:last-child {
17
+ border-bottom: 0;
18
+ }
19
+
20
+ @include media-breakpoint-down(sm) {
21
+ flex-direction: column;
22
+
23
+ .button {
24
+ width: 100%;
25
+ }
26
+ }
27
+
28
+ .button {
29
+ flex-shrink: 0;
30
+ }
31
+ }
32
+
33
+ .dashboard__contract-item {
34
+ .dashboard-item__block__head__title {
35
+ display: flex;
36
+ align-items: center;
37
+
38
+ .c__loader {
39
+ margin-right: 1rem;
40
+ }
41
+ }
42
+ }
43
+ }