@openedx/frontend-base 1.0.0-alpha.0

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 (356) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +112 -0
  3. package/config/babel/babel.config.js +28 -0
  4. package/config/config-helpers/createConfig.js +13 -0
  5. package/config/config-helpers/createLintConfig.js +16 -0
  6. package/config/config-helpers/getBaseConfig.js +12 -0
  7. package/config/defaultConfigPaths.js +35 -0
  8. package/config/eslint/base.eslint.config.js +113 -0
  9. package/config/index.js +12 -0
  10. package/config/jest/jest.config.js +30 -0
  11. package/config/tsconfig.json +32 -0
  12. package/config/types.js +25 -0
  13. package/config/webpack/common-config/all/getCodeRules.js +52 -0
  14. package/config/webpack/common-config/all/getFileLoaderRules.js +26 -0
  15. package/config/webpack/common-config/all/getIgnoreWarnings.js +14 -0
  16. package/config/webpack/common-config/all/getImageMinimizer.js +25 -0
  17. package/config/webpack/common-config/all/getStylesheetRule.js +112 -0
  18. package/config/webpack/common-config/dev/getDevServer.js +38 -0
  19. package/config/webpack/common-config/index.js +18 -0
  20. package/config/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
  21. package/config/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
  22. package/config/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
  23. package/config/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
  24. package/config/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
  25. package/config/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
  26. package/config/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
  27. package/config/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
  28. package/config/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
  29. package/config/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
  30. package/config/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
  31. package/config/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
  32. package/config/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
  33. package/config/webpack/types.js +2 -0
  34. package/config/webpack/utils/getLocalAliases.js +65 -0
  35. package/config/webpack/utils/getPublicPath.js +6 -0
  36. package/config/webpack/utils/getResolvedSiteConfigPath.js +32 -0
  37. package/config/webpack/utils/paragonUtils.js +138 -0
  38. package/config/webpack/webpack.config.build.js +80 -0
  39. package/config/webpack/webpack.config.dev.js +76 -0
  40. package/config/webpack/webpack.config.dev.shell.js +110 -0
  41. package/eslint.config.js +18 -0
  42. package/frontend-base.d.ts +8 -0
  43. package/index.ts +7 -0
  44. package/jest.config.js +7 -0
  45. package/openedx-frontend-base.tgz +0 -0
  46. package/package.json +149 -0
  47. package/runtime/analytics/MockAnalyticsService.js +71 -0
  48. package/runtime/analytics/SegmentAnalyticsService.js +243 -0
  49. package/runtime/analytics/index.ts +12 -0
  50. package/runtime/analytics/interface.js +145 -0
  51. package/runtime/auth/AxiosCsrfTokenService.js +60 -0
  52. package/runtime/auth/AxiosJwtAuthService.js +363 -0
  53. package/runtime/auth/AxiosJwtTokenService.js +134 -0
  54. package/runtime/auth/LocalForageCache.js +76 -0
  55. package/runtime/auth/MockAuthService.js +278 -0
  56. package/runtime/auth/index.ts +19 -0
  57. package/runtime/auth/interceptors/createCsrfTokenProviderInterceptor.js +36 -0
  58. package/runtime/auth/interceptors/createJwtTokenProviderInterceptor.js +37 -0
  59. package/runtime/auth/interceptors/createProcessAxiosRequestErrorInterceptor.js +20 -0
  60. package/runtime/auth/interceptors/createRetryInterceptor.js +74 -0
  61. package/runtime/auth/interface.js +309 -0
  62. package/runtime/auth/utils.js +105 -0
  63. package/runtime/babel.config.js +3 -0
  64. package/runtime/config/index.ts +295 -0
  65. package/runtime/constants.ts +68 -0
  66. package/runtime/i18n/index.js +118 -0
  67. package/runtime/i18n/injectIntlWithShim.jsx +48 -0
  68. package/runtime/i18n/lib.ts +272 -0
  69. package/runtime/index.ts +134 -0
  70. package/runtime/initialize.js +352 -0
  71. package/runtime/jest.config.js +32 -0
  72. package/runtime/logging/MockLoggingService.js +31 -0
  73. package/runtime/logging/NewRelicLoggingService.js +184 -0
  74. package/runtime/logging/index.ts +9 -0
  75. package/runtime/logging/interface.js +109 -0
  76. package/runtime/logging/types.ts +4 -0
  77. package/runtime/react/AuthenticatedPageRoute.jsx +43 -0
  78. package/runtime/react/CombinedAppProvider.tsx +46 -0
  79. package/runtime/react/CurrentAppContext.tsx +25 -0
  80. package/runtime/react/CurrentAppProvider.tsx +46 -0
  81. package/runtime/react/Divider.tsx +5 -0
  82. package/runtime/react/ErrorBoundary.jsx +47 -0
  83. package/runtime/react/ErrorPage.jsx +72 -0
  84. package/runtime/react/LoginRedirect.jsx +16 -0
  85. package/runtime/react/PageWrap.jsx +24 -0
  86. package/runtime/react/SiteContext.tsx +32 -0
  87. package/runtime/react/SiteProvider.tsx +78 -0
  88. package/runtime/react/hooks.ts +106 -0
  89. package/runtime/react/index.ts +19 -0
  90. package/runtime/routing/index.ts +1 -0
  91. package/runtime/routing/utils.ts +34 -0
  92. package/runtime/scripts/GoogleAnalyticsLoader.ts +59 -0
  93. package/runtime/scripts/index.ts +1 -0
  94. package/runtime/setupTest.js +49 -0
  95. package/runtime/slots/Slot.tsx +32 -0
  96. package/runtime/slots/SlotContext.tsx +8 -0
  97. package/runtime/slots/hooks.ts +35 -0
  98. package/runtime/slots/index.ts +7 -0
  99. package/runtime/slots/layout/DefaultSlotLayout.tsx +9 -0
  100. package/runtime/slots/layout/hooks.ts +65 -0
  101. package/runtime/slots/layout/index.ts +5 -0
  102. package/runtime/slots/layout/types.ts +45 -0
  103. package/runtime/slots/layout/utils.ts +14 -0
  104. package/runtime/slots/types.ts +23 -0
  105. package/runtime/slots/utils.ts +59 -0
  106. package/runtime/slots/widget/WidgetContext.tsx +9 -0
  107. package/runtime/slots/widget/WidgetProvider.tsx +30 -0
  108. package/runtime/slots/widget/hooks.ts +105 -0
  109. package/runtime/slots/widget/iframe/IFrameContentWrapper.messages.tsx +16 -0
  110. package/runtime/slots/widget/iframe/IFrameContentWrapper.tsx +84 -0
  111. package/runtime/slots/widget/iframe/IFrameWidget.tsx +59 -0
  112. package/runtime/slots/widget/iframe/constants.ts +19 -0
  113. package/runtime/slots/widget/iframe/hooks.ts +179 -0
  114. package/runtime/slots/widget/iframe/index.ts +6 -0
  115. package/runtime/slots/widget/iframe/types.ts +7 -0
  116. package/runtime/slots/widget/index.ts +6 -0
  117. package/runtime/slots/widget/types.ts +134 -0
  118. package/runtime/slots/widget/utils.tsx +201 -0
  119. package/runtime/subscriptions.ts +60 -0
  120. package/runtime/testing/index.ts +9 -0
  121. package/runtime/testing/initializeMockApp.ts +81 -0
  122. package/runtime/testing/mockMessages.ts +23 -0
  123. package/runtime/utils.js +178 -0
  124. package/shell/DefaultLayout.tsx +18 -0
  125. package/shell/DefaultMain.tsx +7 -0
  126. package/shell/Logo.tsx +28 -0
  127. package/shell/Shell.messages.ts +61 -0
  128. package/shell/Shell.tsx +18 -0
  129. package/shell/app.scss +149 -0
  130. package/shell/app.ts +24 -0
  131. package/shell/babel.config.js +3 -0
  132. package/shell/dev/devFooter/app.tsx +43 -0
  133. package/shell/dev/devFooter/index.ts +1 -0
  134. package/shell/dev/devHeader/BarContext.tsx +13 -0
  135. package/shell/dev/devHeader/BarLink.tsx +16 -0
  136. package/shell/dev/devHeader/BarProvider.tsx +25 -0
  137. package/shell/dev/devHeader/CoursesLink.tsx +16 -0
  138. package/shell/dev/devHeader/FooContext.tsx +13 -0
  139. package/shell/dev/devHeader/FooLink.tsx +16 -0
  140. package/shell/dev/devHeader/FooProvider.tsx +25 -0
  141. package/shell/dev/devHeader/app.tsx +53 -0
  142. package/shell/dev/devHeader/index.ts +1 -0
  143. package/shell/dev/devHeader/providers.tsx +11 -0
  144. package/shell/dev/devHome/HomePage.tsx +28 -0
  145. package/shell/dev/devHome/app.ts +18 -0
  146. package/shell/dev/devHome/i18n/index.ts +27 -0
  147. package/shell/dev/devHome/index.ts +1 -0
  148. package/shell/dev/devHome/messages.ts +11 -0
  149. package/shell/dev/devUser/app.tsx +24 -0
  150. package/shell/dev/devUser/index.ts +1 -0
  151. package/shell/dev/index.ts +5 -0
  152. package/shell/dev/slotShowcase/HorizontalSlotLayout.tsx +11 -0
  153. package/shell/dev/slotShowcase/LayoutWithOptions.tsx +17 -0
  154. package/shell/dev/slotShowcase/SlotShowcasePage.tsx +66 -0
  155. package/shell/dev/slotShowcase/WidgetWithOptions.tsx +11 -0
  156. package/shell/dev/slotShowcase/app.tsx +373 -0
  157. package/shell/dev/slotShowcase/index.ts +1 -0
  158. package/shell/footer/CenterLinks.tsx +11 -0
  159. package/shell/footer/CopyrightNotice.tsx +36 -0
  160. package/shell/footer/Footer.tsx +34 -0
  161. package/shell/footer/LabeledLinkColumn.tsx +19 -0
  162. package/shell/footer/LanguageMenu.tsx +35 -0
  163. package/shell/footer/LanguageMenuItem.tsx +23 -0
  164. package/shell/footer/LeftLinks.tsx +11 -0
  165. package/shell/footer/LegalNotices.tsx +17 -0
  166. package/shell/footer/PoweredBy.tsx +17 -0
  167. package/shell/footer/RevealLinks.tsx +43 -0
  168. package/shell/footer/RightLinks.tsx +11 -0
  169. package/shell/footer/app.tsx +73 -0
  170. package/shell/footer/data/api.ts +48 -0
  171. package/shell/footer/index.ts +2 -0
  172. package/shell/header/AuthenticatedMenu.tsx +32 -0
  173. package/shell/header/Header.tsx +17 -0
  174. package/shell/header/anonymous-menu/AnonymousMenu.tsx +14 -0
  175. package/shell/header/anonymous-menu/LoginButton.tsx +14 -0
  176. package/shell/header/anonymous-menu/RegisterButton.tsx +15 -0
  177. package/shell/header/app.tsx +142 -0
  178. package/shell/header/desktop/DesktopLayout.tsx +22 -0
  179. package/shell/header/desktop/PrimaryNavLinks.tsx +10 -0
  180. package/shell/header/desktop/SecondaryNavLinks.tsx +10 -0
  181. package/shell/header/index.ts +2 -0
  182. package/shell/header/mobile/MobileLayout.tsx +47 -0
  183. package/shell/header/mobile/MobileNavLinks.tsx +10 -0
  184. package/shell/i18n/index.ts +25 -0
  185. package/shell/index.ts +7 -0
  186. package/shell/jest.config.js +30 -0
  187. package/shell/menus/LinkMenuItem.tsx +64 -0
  188. package/shell/menus/NavDropdownMenuSlot.tsx +29 -0
  189. package/shell/menus/ProfileLinkMenuItem.tsx +33 -0
  190. package/shell/menus/data/utils.ts +19 -0
  191. package/shell/public/index.html +10 -0
  192. package/shell/router/createRouter.ts +17 -0
  193. package/shell/router/getAppRoutes.ts +21 -0
  194. package/shell/setupTest.js +48 -0
  195. package/shell/site.config.dev.tsx +49 -0
  196. package/shell/site.tsx +41 -0
  197. package/test-site/app.d.ts +15 -0
  198. package/test-site/dist/176.436443549ebb858db483.js +2 -0
  199. package/test-site/dist/176.436443549ebb858db483.js.map +1 -0
  200. package/test-site/dist/362.536eff787d2380fe246c.js +2 -0
  201. package/test-site/dist/362.536eff787d2380fe246c.js.map +1 -0
  202. package/test-site/dist/653.486966b108d224551296.js +2 -0
  203. package/test-site/dist/653.486966b108d224551296.js.map +1 -0
  204. package/test-site/dist/74e025d3fe9a7b7f8503054e2563b353.jpg +0 -0
  205. package/test-site/dist/806.323cf6496ad0a7fe73a7.js +3 -0
  206. package/test-site/dist/806.323cf6496ad0a7fe73a7.js.LICENSE.txt +106 -0
  207. package/test-site/dist/806.323cf6496ad0a7fe73a7.js.map +1 -0
  208. package/test-site/dist/95ec738c0b7faac5b5c9126794446bbd.svg +4 -0
  209. package/test-site/dist/app.612058b36c74787759ac.css +61 -0
  210. package/test-site/dist/app.612058b36c74787759ac.css.map +1 -0
  211. package/test-site/dist/app.612058b36c74787759ac.js +2 -0
  212. package/test-site/dist/app.612058b36c74787759ac.js.map +1 -0
  213. package/test-site/dist/cb28cdb1468c915e27e5cec9af64f22f.svg +1 -0
  214. package/test-site/dist/index.html +1 -0
  215. package/test-site/dist/report.html +39 -0
  216. package/test-site/dist/runtime.c7aeaf7b967496cb076f.js +2 -0
  217. package/test-site/dist/runtime.c7aeaf7b967496cb076f.js.map +1 -0
  218. package/test-site/eslint.config.js +12 -0
  219. package/test-site/package-lock.json +19226 -0
  220. package/test-site/package.json +29 -0
  221. package/test-site/public/index.html +10 -0
  222. package/test-site/site.config.build.tsx +27 -0
  223. package/test-site/site.config.dev.tsx +27 -0
  224. package/test-site/src/authenticated-page/AuthenticatedPage.tsx +18 -0
  225. package/test-site/src/authenticated-page/i18n/index.ts +27 -0
  226. package/test-site/src/authenticated-page/index.tsx +28 -0
  227. package/test-site/src/example-page/ExamplePage.tsx +79 -0
  228. package/test-site/src/example-page/Image.tsx +11 -0
  229. package/test-site/src/example-page/ParagonPreview.jsx +66 -0
  230. package/test-site/src/example-page/apple.jpg +0 -0
  231. package/test-site/src/example-page/apple.svg +1 -0
  232. package/test-site/src/example-page/index.ts +16 -0
  233. package/test-site/src/i18n/README.md +3 -0
  234. package/test-site/src/i18n/messages/frontend-app-sample/ar.json +4 -0
  235. package/test-site/src/i18n/messages/frontend-app-sample/eo.json +1 -0
  236. package/test-site/src/i18n/messages/frontend-app-sample/es_419.json +4 -0
  237. package/test-site/src/i18n/messages/frontend-component-emptylangs/ar.json +1 -0
  238. package/test-site/src/i18n/messages/frontend-component-singlelang/ar.json +3 -0
  239. package/test-site/src/iframe-widget/IframeWidget.tsx +14 -0
  240. package/test-site/src/iframe-widget/index.ts +16 -0
  241. package/test-site/src/index.tsx +3 -0
  242. package/test-site/src/messages.js +11 -0
  243. package/test-site/src/site.scss +11 -0
  244. package/test-site/tsconfig.json +14 -0
  245. package/tools/babel/babel.config.js +27 -0
  246. package/tools/babel.config.js +3 -0
  247. package/tools/cli/README.md +29 -0
  248. package/tools/cli/commands/pack.ts +9 -0
  249. package/tools/cli/commands/release.ts +27 -0
  250. package/tools/cli/commands/serve.ts +43 -0
  251. package/tools/cli/intl-imports.ts +274 -0
  252. package/tools/cli/openedx.ts +101 -0
  253. package/tools/cli/transifex-utils.ts +75 -0
  254. package/tools/cli/utils/ensureConfigFilenameOption.ts +40 -0
  255. package/tools/cli/utils/formatter.ts +10 -0
  256. package/tools/cli/utils/getResolvedConfigPath.ts +23 -0
  257. package/tools/cli/utils/prettyPrintTitle.ts +15 -0
  258. package/tools/cli/utils/printUsage.ts +53 -0
  259. package/tools/config-helpers/createConfig.ts +8 -0
  260. package/tools/config-helpers/createLintConfig.ts +14 -0
  261. package/tools/config-helpers/getBaseConfig.ts +11 -0
  262. package/tools/defaultConfigPaths.ts +30 -0
  263. package/tools/dist/babel/babel.config.js +28 -0
  264. package/tools/dist/cli/commands/pack.js +14 -0
  265. package/tools/dist/cli/commands/release.js +28 -0
  266. package/tools/dist/cli/commands/serve.js +44 -0
  267. package/tools/dist/cli/intl-imports.js +233 -0
  268. package/tools/dist/cli/openedx.js +100 -0
  269. package/tools/dist/cli/transifex-utils.js +68 -0
  270. package/tools/dist/cli/utils/ensureConfigFilenameOption.js +42 -0
  271. package/tools/dist/cli/utils/formatter.js +10 -0
  272. package/tools/dist/cli/utils/getResolvedConfigPath.js +28 -0
  273. package/tools/dist/cli/utils/prettyPrintTitle.js +17 -0
  274. package/tools/dist/cli/utils/printUsage.js +48 -0
  275. package/tools/dist/config-helpers/createConfig.js +13 -0
  276. package/tools/dist/config-helpers/createLintConfig.js +16 -0
  277. package/tools/dist/config-helpers/getBaseConfig.js +12 -0
  278. package/tools/dist/defaultConfigPaths.js +35 -0
  279. package/tools/dist/eslint/base.eslint.config.js +113 -0
  280. package/tools/dist/eslint.config.js +11 -0
  281. package/tools/dist/index.js +12 -0
  282. package/tools/dist/jest/jest.config.js +30 -0
  283. package/tools/dist/jest.config.js +20 -0
  284. package/tools/dist/types.js +25 -0
  285. package/tools/dist/typescript/tsconfig.json +32 -0
  286. package/tools/dist/webpack/common-config/all/getCodeRules.js +52 -0
  287. package/tools/dist/webpack/common-config/all/getFileLoaderRules.js +26 -0
  288. package/tools/dist/webpack/common-config/all/getIgnoreWarnings.js +14 -0
  289. package/tools/dist/webpack/common-config/all/getImageMinimizer.js +25 -0
  290. package/tools/dist/webpack/common-config/all/getStylesheetRule.js +112 -0
  291. package/tools/dist/webpack/common-config/dev/getDevServer.js +38 -0
  292. package/tools/dist/webpack/common-config/index.js +18 -0
  293. package/tools/dist/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
  294. package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
  295. package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
  296. package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
  297. package/tools/dist/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
  298. package/tools/dist/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
  299. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
  300. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
  301. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
  302. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
  303. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
  304. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
  305. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
  306. package/tools/dist/webpack/types.js +2 -0
  307. package/tools/dist/webpack/utils/getLocalAliases.js +65 -0
  308. package/tools/dist/webpack/utils/getPublicPath.js +6 -0
  309. package/tools/dist/webpack/utils/getResolvedSiteConfigPath.js +32 -0
  310. package/tools/dist/webpack/utils/paragonUtils.js +138 -0
  311. package/tools/dist/webpack/webpack.config.build.js +80 -0
  312. package/tools/dist/webpack/webpack.config.dev.js +76 -0
  313. package/tools/dist/webpack/webpack.config.dev.shell.js +110 -0
  314. package/tools/eslint/base.eslint.config.js +124 -0
  315. package/tools/eslint/modules.d.ts +5 -0
  316. package/tools/eslint.config.js +15 -0
  317. package/tools/index.ts +3 -0
  318. package/tools/jest/jest.config.js +30 -0
  319. package/tools/jest.config.js +19 -0
  320. package/tools/tsconfig.json +24 -0
  321. package/tools/types.ts +21 -0
  322. package/tools/typescript/tsconfig.json +32 -0
  323. package/tools/webpack/common-config/README.md +15 -0
  324. package/tools/webpack/common-config/all/getCodeRules.ts +51 -0
  325. package/tools/webpack/common-config/all/getFileLoaderRules.ts +23 -0
  326. package/tools/webpack/common-config/all/getIgnoreWarnings.ts +13 -0
  327. package/tools/webpack/common-config/all/getImageMinimizer.ts +26 -0
  328. package/tools/webpack/common-config/all/getStylesheetRule.ts +111 -0
  329. package/tools/webpack/common-config/dev/getDevServer.ts +35 -0
  330. package/tools/webpack/common-config/index.ts +6 -0
  331. package/tools/webpack/common-config/site/getHtmlWebpackPlugin.ts +11 -0
  332. package/tools/webpack/modules.d.ts +6 -0
  333. package/tools/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.ts +102 -0
  334. package/tools/webpack/plugins/html-webpack-new-relic-plugin/LICENSE +21 -0
  335. package/tools/webpack/plugins/html-webpack-new-relic-plugin/README.md +7 -0
  336. package/tools/webpack/plugins/html-webpack-new-relic-plugin/index.js +3 -0
  337. package/tools/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +1 -0
  338. package/tools/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.ts +134 -0
  339. package/tools/webpack/plugins/paragon-webpack-plugin/index.ts +3 -0
  340. package/tools/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.ts +71 -0
  341. package/tools/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.ts +72 -0
  342. package/tools/webpack/plugins/paragon-webpack-plugin/utils/index.ts +6 -0
  343. package/tools/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.ts +131 -0
  344. package/tools/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.ts +144 -0
  345. package/tools/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.ts +106 -0
  346. package/tools/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.ts +54 -0
  347. package/tools/webpack/types.ts +69 -0
  348. package/tools/webpack/utils/getLocalAliases.ts +65 -0
  349. package/tools/webpack/utils/getPublicPath.ts +3 -0
  350. package/tools/webpack/utils/getResolvedSiteConfigPath.ts +28 -0
  351. package/tools/webpack/utils/paragonUtils.ts +152 -0
  352. package/tools/webpack/webpack.config.build.ts +93 -0
  353. package/tools/webpack/webpack.config.dev.shell.ts +122 -0
  354. package/tools/webpack/webpack.config.dev.ts +90 -0
  355. package/tsconfig.json +23 -0
  356. package/types.ts +99 -0
@@ -0,0 +1,11 @@
1
+ import { useWidgetOptions } from '../../../runtime';
2
+
3
+ export default function WidgetWithOptions() {
4
+ const options = useWidgetOptions();
5
+
6
+ const title = typeof options.title === 'string' ? options.title : 'Foo';
7
+
8
+ return (
9
+ <div>{title}</div>
10
+ );
11
+ }
@@ -0,0 +1,373 @@
1
+ import { NavDropdownMenuSlot } from '../..';
2
+ import LinkMenuItem from '../../menus/LinkMenuItem';
3
+ import { LayoutOperationTypes, WidgetOperationTypes, useSlotContext } from '../../../runtime';
4
+ import { App } from '../../../types';
5
+ import HorizontalSlotLayout from './HorizontalSlotLayout';
6
+ import SlotShowcasePage from './SlotShowcasePage';
7
+ import WidgetWithOptions from './WidgetWithOptions';
8
+
9
+ function Title({ title, op }: { title: string, op?: string }) {
10
+ return (
11
+ <span>
12
+ {title}
13
+ {op && (
14
+ <>{' '}(<code>{op}</code>)</>
15
+ )}
16
+ </span>
17
+ );
18
+ }
19
+
20
+ function Child({ title, op }: { title: string, op?: string }) {
21
+ return (
22
+ <div>
23
+ {title}
24
+ {op && (
25
+ <span>{' '}(<code>{op}</code>)</span>
26
+ )}
27
+ </div>
28
+ );
29
+ }
30
+
31
+ function TakesProps({ aSlotProp }: { aSlotProp: string }) {
32
+ return (
33
+ <div>And this is a slot prop that was passed down via props: <code>{aSlotProp}</code></div>
34
+ );
35
+ }
36
+
37
+ function TakesPropsViaContext() {
38
+ const slotContext = useSlotContext();
39
+ const aSlotProp = typeof slotContext.aSlotProp === 'string' ? slotContext.aSlotProp : 'foo';
40
+ return (
41
+ <div>And this is the same prop, but accessed via slot context: <code>{aSlotProp}</code></div>
42
+ );
43
+ }
44
+
45
+ const app: App = {
46
+ appId: 'org.openedx.frontend.app.dev.slotShowcase',
47
+ routes: [{
48
+ id: 'org.openedx.frontend.route.dev.slotShowcase',
49
+ path: '/slots',
50
+ Component: SlotShowcasePage,
51
+ handle: {
52
+ role: 'slotShowcase',
53
+ }
54
+ }],
55
+ slots: [
56
+ // Simple
57
+ {
58
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseSimple',
59
+ id: 'org.openedx.frontend.widget.slotShowcase.simpleChild1',
60
+ op: WidgetOperationTypes.APPEND,
61
+ element: (<Child title="Child One" />)
62
+ },
63
+ {
64
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseSimple',
65
+ id: 'org.openedx.frontend.widget.slotShowcase.simpleChild2',
66
+ op: WidgetOperationTypes.APPEND,
67
+ element: (<Child title="Child Two" />)
68
+ },
69
+ {
70
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseSimple',
71
+ id: 'org.openedx.frontend.widget.slotShowcase.simpleChild3',
72
+ op: WidgetOperationTypes.APPEND,
73
+ element: (<Child title="Child Three" />)
74
+ },
75
+ {
76
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseSimpleWithDefaultContent',
77
+ id: 'org.openedx.frontend.widget.slotShowcase.simpleChild4',
78
+ op: WidgetOperationTypes.APPEND,
79
+ component: TakesProps
80
+ },
81
+ {
82
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseSimpleWithDefaultContent',
83
+ id: 'org.openedx.frontend.widget.slotShowcase.simpleChild5',
84
+ op: WidgetOperationTypes.APPEND,
85
+ component: TakesPropsViaContext
86
+ },
87
+
88
+ // Custom Layout
89
+ {
90
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseCustom',
91
+ id: 'org.openedx.frontend.widget.slotShowcase.customChild1',
92
+ op: WidgetOperationTypes.APPEND,
93
+ element: (<Child title="Child One" />)
94
+ },
95
+ {
96
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseCustom',
97
+ id: 'org.openedx.frontend.widget.slotShowcase.customChild2',
98
+ op: WidgetOperationTypes.APPEND,
99
+ element: (<Child title="Child Two" />)
100
+ },
101
+ {
102
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseCustom',
103
+ id: 'org.openedx.frontend.widget.slotShowcase.customChild3',
104
+ op: WidgetOperationTypes.APPEND,
105
+ element: (<Child title="Child Three" />)
106
+ },
107
+
108
+ // Override custom layout
109
+ {
110
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseCustomConfig',
111
+ id: 'org.openedx.frontend.widget.slotShowcase.customConfigChild1',
112
+ op: WidgetOperationTypes.APPEND,
113
+ element: (<Child title="Child One" />)
114
+ },
115
+ {
116
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseCustomConfig',
117
+ id: 'org.openedx.frontend.widget.slotShowcase.customConfigChild2',
118
+ op: WidgetOperationTypes.APPEND,
119
+ element: (<Child title="Child Two" />)
120
+ },
121
+ {
122
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseCustomConfig',
123
+ id: 'org.openedx.frontend.widget.slotShowcase.customConfigChild3',
124
+ op: WidgetOperationTypes.APPEND,
125
+ element: (<Child title="Child Three" />)
126
+ },
127
+ {
128
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseCustomConfig',
129
+ op: LayoutOperationTypes.REPLACE,
130
+ element: <HorizontalSlotLayout />,
131
+ },
132
+
133
+ // Layout Options
134
+ {
135
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseLayoutWithOptions',
136
+ op: LayoutOperationTypes.OPTIONS,
137
+ options: {
138
+ title: (<Title title="Bar" op="LayoutOperationTypes.OPTIONS" />),
139
+ }
140
+ },
141
+ {
142
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseLayoutWithOptions',
143
+ id: 'org.openedx.frontend.widget.slotShowcase.layoutWithOptionsChild1',
144
+ op: WidgetOperationTypes.APPEND,
145
+ element: (<Child title="Child One" />)
146
+ },
147
+ {
148
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseLayoutWithOptions',
149
+ id: 'org.openedx.frontend.widget.slotShowcase.layoutWithOptionsChild2',
150
+ op: WidgetOperationTypes.APPEND,
151
+ element: (<Child title="Child Two" />)
152
+ },
153
+ {
154
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseLayoutWithOptions',
155
+ id: 'org.openedx.frontend.widget.slotShowcase.layoutWithOptionsChild3',
156
+ op: WidgetOperationTypes.APPEND,
157
+ element: (<Child title="Child Three" />)
158
+ },
159
+ {
160
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseLayoutWithOptionsDefault',
161
+ id: 'org.openedx.frontend.widget.slotShowcase.layoutWithOptionsDefaultChild1',
162
+ op: WidgetOperationTypes.APPEND,
163
+ element: (<Child title="Child One" />)
164
+ },
165
+ {
166
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseLayoutWithOptionsDefault',
167
+ id: 'org.openedx.frontend.widget.slotShowcase.layoutWithOptionsDefaultChild2',
168
+ op: WidgetOperationTypes.APPEND,
169
+ element: (<Child title="Child Two" />)
170
+ },
171
+ {
172
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseLayoutWithOptionsDefault',
173
+ id: 'org.openedx.frontend.widget.slotShowcase.layoutWithOptionsDefaultChild3',
174
+ op: WidgetOperationTypes.APPEND,
175
+ element: (<Child title="Child Three" />)
176
+ },
177
+
178
+ // TODO: Override Layout
179
+
180
+ // Prepending
181
+ {
182
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcasePrepending',
183
+ id: 'org.openedx.frontend.widget.slotShowcase.prependingChild1',
184
+ op: WidgetOperationTypes.APPEND,
185
+ element: (<Child title="Child One" op="WidgetOperationTypes.APPEND" />)
186
+ },
187
+ {
188
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcasePrepending',
189
+ id: 'org.openedx.frontend.widget.slotShowcase.prependingChild2',
190
+ op: WidgetOperationTypes.APPEND,
191
+ element: (<Child title="Child Two" op="WidgetOperationTypes.APPEND" />)
192
+ },
193
+ {
194
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcasePrepending',
195
+ id: 'org.openedx.frontend.widget.slotShowcase.prependingChild3',
196
+ op: WidgetOperationTypes.PREPEND,
197
+ element: (<Child title="Child Three" op="WidgetOperationTypes.PREPEND" />)
198
+ },
199
+
200
+ // Inserting
201
+ {
202
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseInserting',
203
+ id: 'slot-showcase.inserting.child4',
204
+ op: WidgetOperationTypes.INSERT_AFTER,
205
+ relatedId: 'org.openedx.frontend.widget.slotShowcase.insertingChild2',
206
+ element: (<Child title="Child Four" op="WidgetOperationTypes.INSERT_AFTER" />)
207
+ },
208
+ {
209
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseInserting',
210
+ id: 'slot-showcase.inserting.child5',
211
+ op: WidgetOperationTypes.INSERT_BEFORE,
212
+ relatedId: 'org.openedx.frontend.widget.slotShowcase.insertingChild2',
213
+ element: (<Child title="Child Five" op="WidgetOperationTypes.INSERT_BEFORE" />)
214
+ },
215
+ {
216
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseInserting',
217
+ id: 'org.openedx.frontend.widget.slotShowcase.insertingChild1',
218
+ op: WidgetOperationTypes.APPEND,
219
+ element: (<Child title="Child One" />)
220
+ },
221
+ {
222
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseInserting',
223
+ id: 'org.openedx.frontend.widget.slotShowcase.insertingChild2',
224
+ op: WidgetOperationTypes.APPEND,
225
+ element: (<Child title="Child Two" />)
226
+ },
227
+ {
228
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseInserting',
229
+ id: 'org.openedx.frontend.widget.slotShowcase.insertingChild3',
230
+ op: WidgetOperationTypes.APPEND,
231
+ element: (<Child title="Child Three" />)
232
+ },
233
+
234
+ // Replacing
235
+ {
236
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseReplacing',
237
+ id: 'org.openedx.frontend.widget.slotShowcase.replacingChild1',
238
+ op: WidgetOperationTypes.APPEND,
239
+ element: (<Child title="Child One" />)
240
+ },
241
+ {
242
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseReplacing',
243
+ id: 'org.openedx.frontend.widget.slotShowcase.replacingChild2',
244
+ op: WidgetOperationTypes.APPEND,
245
+ element: (<Child title="Child Two" />)
246
+ },
247
+ {
248
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseReplacing',
249
+ id: 'org.openedx.frontend.widget.slotShowcase.replacingChild3',
250
+ op: WidgetOperationTypes.APPEND,
251
+ element: (<Child title="Child Three" />)
252
+ },
253
+ {
254
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseReplacing',
255
+ id: 'org.openedx.frontend.widget.slotShowcase.replacingChild4',
256
+ op: WidgetOperationTypes.REPLACE,
257
+ relatedId: 'org.openedx.frontend.widget.slotShowcase.replacingChild2',
258
+ element: (<Child title="Child Four" op="WidgetOperationTypes.REPLACE" />)
259
+ },
260
+
261
+ // Hiding
262
+ {
263
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseRemoving',
264
+ id: 'org.openedx.frontend.widget.slotShowcase.removingChild1',
265
+ op: WidgetOperationTypes.APPEND,
266
+ element: (<Child title="Child One" />)
267
+ },
268
+ {
269
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseRemoving',
270
+ id: 'org.openedx.frontend.widget.slotShowcase.removingChild2',
271
+ op: WidgetOperationTypes.APPEND,
272
+ element: (<Child title="Child Two" />)
273
+ },
274
+ {
275
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseRemoving',
276
+ id: 'org.openedx.frontend.widget.slotShowcase.removingChild3',
277
+ op: WidgetOperationTypes.APPEND,
278
+ element: (<Child title="Child Three" />)
279
+ },
280
+ {
281
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseRemoving',
282
+ op: WidgetOperationTypes.REMOVE,
283
+ relatedId: 'org.openedx.frontend.widget.slotShowcase.removingChild2',
284
+ },
285
+
286
+ // Widget Options
287
+ {
288
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseWidgetOptions',
289
+ id: 'org.openedx.frontend.widget.slotShowcase.widgetOptionsChild1',
290
+ op: WidgetOperationTypes.APPEND,
291
+ component: WidgetWithOptions,
292
+ },
293
+ {
294
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseWidgetOptions',
295
+ id: 'org.openedx.frontend.widget.slotShowcase.widgetOptionsChild2',
296
+ op: WidgetOperationTypes.APPEND,
297
+ component: WidgetWithOptions,
298
+ },
299
+ {
300
+ slotId: 'org.openedx.frontend.slot.dev.slotShowcaseWidgetOptions',
301
+ relatedId: 'org.openedx.frontend.widget.slotShowcase.widgetOptionsChild2',
302
+ op: WidgetOperationTypes.OPTIONS,
303
+ options: {
304
+ title: (<Title title="Bar" op="WidgetOperationTypes.OPTIONS" />),
305
+ }
306
+ },
307
+
308
+ // Header
309
+ {
310
+ slotId: 'org.openedx.frontend.slot.header.primaryLinks.v1',
311
+ relatedId: 'org.openedx.frontend.widget.slotShowcase.headerLink',
312
+ op: WidgetOperationTypes.OPTIONS,
313
+ options: {
314
+ title: 'Courses (modified)',
315
+ },
316
+ condition: {
317
+ active: ['slotShowcase'],
318
+ }
319
+ },
320
+ {
321
+ slotId: 'org.openedx.frontend.slot.header.primaryLinks.v1',
322
+ id: 'org.openedx.frontend.widget.slotShowcase.headerLinkAfter3',
323
+ op: WidgetOperationTypes.INSERT_AFTER,
324
+ relatedId: 'org.openedx.frontend.widget.slotShowcase.headerLink3',
325
+ element: (<LinkMenuItem label="Link After 3" url="#" variant="navLink" />),
326
+ condition: {
327
+ active: ['slotShowcase'],
328
+ }
329
+ },
330
+ {
331
+ slotId: 'org.openedx.frontend.slot.header.primaryLinks.v1',
332
+ id: 'org.openedx.frontend.widget.slotShowcase.headerPrimaryLinksDropdownPrepend',
333
+ op: WidgetOperationTypes.PREPEND,
334
+ element: (
335
+ <NavDropdownMenuSlot id="org.openedx.frontend.slot.header.primaryLinksDropdown.v1" label="Resources" />
336
+ ),
337
+ condition: {
338
+ active: ['slotShowcase']
339
+ }
340
+ },
341
+ {
342
+ slotId: 'org.openedx.frontend.slot.header.primaryLinksDropdown.v1',
343
+ id: 'org.openedx.frontend.widget.slotShowcase.headerPrimaryLinksDropdownAppend',
344
+ op: WidgetOperationTypes.APPEND,
345
+ element: (
346
+ <LinkMenuItem label="Resource 1" url="#" variant="dropdownItem" />
347
+ ),
348
+ condition: {
349
+ active: ['slotShowcase'],
350
+ }
351
+ },
352
+ {
353
+ slotId: 'org.openedx.frontend.slot.header.primaryLinks.v1',
354
+ id: 'org.openedx.frontend.widget.slotShowcase.headerLink3',
355
+ op: WidgetOperationTypes.APPEND,
356
+ element: (<LinkMenuItem label="Link 3" url="#" variant="navLink" />),
357
+ condition: {
358
+ active: ['slotShowcase'],
359
+ }
360
+ },
361
+ {
362
+ slotId: 'org.openedx.frontend.slot.header.primaryLinks.v1',
363
+ id: 'org.openedx.frontend.widget.slotShowcase.headerLink4',
364
+ op: WidgetOperationTypes.APPEND,
365
+ element: (<LinkMenuItem label="Link 4" url="#" variant="navLink" />),
366
+ condition: {
367
+ active: ['slotShowcase'],
368
+ }
369
+ },
370
+ ]
371
+ };
372
+
373
+ export default app;
@@ -0,0 +1 @@
1
+ export { default as slotShowcaseApp } from './app';
@@ -0,0 +1,11 @@
1
+ import { useWidgets } from '../../runtime';
2
+
3
+ export default function CenterLinks() {
4
+ const widgets = useWidgets();
5
+
6
+ return (
7
+ <div className="d-flex flex-wrap column-gap-6 row-gap-4">
8
+ {widgets}
9
+ </div>
10
+ );
11
+ }
@@ -0,0 +1,36 @@
1
+ import { Hyperlink } from '@openedx/paragon';
2
+
3
+ import { useSiteConfig } from '../../runtime';
4
+
5
+ interface CopyrightNoticeProps {
6
+ title?: string,
7
+ /**
8
+ * The URL that the copyright notice should link to when a user clicks on the title. If not set,
9
+ * the title will not be a link.
10
+ */
11
+ destination?: string,
12
+ }
13
+
14
+ export default function CopyrightNotice({ title, destination }: CopyrightNoticeProps) {
15
+ const config = useSiteConfig();
16
+
17
+ // Allow overrides via props and fallback to defaults.
18
+ const finalTitle = title ?? config.siteName;
19
+
20
+ return (
21
+ <div className="text-center x-small">
22
+ <span>&copy;&nbsp;{new Date().getFullYear()}&nbsp;</span>
23
+ {destination !== undefined ? (
24
+ <Hyperlink
25
+ destination={destination}
26
+ target="_blank"
27
+ >
28
+ {finalTitle}
29
+ </Hyperlink>
30
+ ) : (
31
+ <span>{finalTitle}</span>
32
+ )}
33
+ <span>.</span>
34
+ </div>
35
+ );
36
+ }
@@ -0,0 +1,34 @@
1
+ import { Slot } from '../../runtime';
2
+ import CenterLinks from './CenterLinks';
3
+ import LeftLinks from './LeftLinks';
4
+ import LegalNotices from './LegalNotices';
5
+ import PoweredBy from './PoweredBy';
6
+ import RevealLinks from './RevealLinks';
7
+ import RightLinks from './RightLinks';
8
+
9
+ export default function Footer() {
10
+ return (
11
+ <footer className="d-flex flex-column align-items-stretch">
12
+ <Slot id="org.openedx.frontend.slot.footer.desktopTop.v1" layout={RevealLinks} />
13
+ <div className="py-3 px-3 d-flex gap-5 justify-content-between align-items-stretch">
14
+ <div className="flex-basis-0 d-flex align-items-start">
15
+ <div className="d-flex gap-3 align-items-center">
16
+ <Slot id="org.openedx.frontend.slot.footer.desktopLeftLinks.v1" layout={LeftLinks} />
17
+ </div>
18
+ </div>
19
+ <div className="flex-grow-1 flex-basis-0 d-flex justify-content-center">
20
+ <div className="d-flex flex-column justify-content-between gap-5">
21
+ <Slot id="org.openedx.frontend.slot.footer.desktopCenterLinks.v1" layout={CenterLinks} />
22
+ <Slot id="org.openedx.frontend.slot.footer.desktopLegalNotices.v1" layout={LegalNotices} />
23
+ </div>
24
+ </div>
25
+ <div className="flex-basis-0 d-flex justify-content-end">
26
+ <div className="d-flex flex-column justify-content-between">
27
+ <Slot id="org.openedx.frontend.slot.footer.desktopRightLinks.v1" layout={RightLinks} />
28
+ <PoweredBy />
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </footer>
33
+ );
34
+ }
@@ -0,0 +1,19 @@
1
+ import { useLayoutOptions, useWidgets } from '../../runtime';
2
+
3
+ export default function LabeledLinkColumn() {
4
+ const widgets = useWidgets();
5
+ const options = useLayoutOptions();
6
+
7
+ if (widgets.length === 0) {
8
+ return null;
9
+ }
10
+
11
+ return (
12
+ <div className="d-flex flex-grow-1 flex-column gap-2 small">
13
+ {typeof options.label === 'string' && (
14
+ <div className="mb-1 font-weight-bold">{options.label}</div>
15
+ )}
16
+ {widgets}
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,35 @@
1
+ import { Dropdown } from '@openedx/paragon';
2
+ import { useContext } from 'react';
3
+
4
+ import {
5
+ SiteContext,
6
+ getLocalizedLanguageName,
7
+ getSupportedLanguageList
8
+ } from '../../runtime';
9
+
10
+ import LanguageMenuItem from './LanguageMenuItem';
11
+
12
+ export default function LanguageMenu() {
13
+ const { locale } = useContext(SiteContext);
14
+
15
+ const languages = getSupportedLanguageList();
16
+ const currentLanguageName = getLocalizedLanguageName(locale);
17
+
18
+ // Hide the menu if there's only one language.
19
+ if (languages.length === 1) {
20
+ return null;
21
+ }
22
+
23
+ return (
24
+ <Dropdown>
25
+ <Dropdown.Toggle id="language-menu-dropdown-trigger" variant="outline-primary" size="sm">
26
+ {currentLanguageName}
27
+ </Dropdown.Toggle>
28
+ <Dropdown.Menu className="overflow-auto" style={{ maxHeight: '320px' }}>
29
+ {languages.map((language) => (
30
+ <LanguageMenuItem key={language.code} language={language} />
31
+ ))}
32
+ </Dropdown.Menu>
33
+ </Dropdown>
34
+ );
35
+ }
@@ -0,0 +1,23 @@
1
+ import { Dropdown } from '@openedx/paragon';
2
+ import { useCallback } from 'react';
3
+
4
+ import { updateSiteLanguage } from './data/api';
5
+
6
+ interface LanguageMenuItemProps {
7
+ language: {
8
+ code: string,
9
+ name: string,
10
+ },
11
+ }
12
+
13
+ export default function LanguageMenuItem({ language }: LanguageMenuItemProps) {
14
+ const handleClick = useCallback(() => {
15
+ updateSiteLanguage(language.code);
16
+ }, [language.code]);
17
+
18
+ return (
19
+ <Dropdown.Item key={language.code} onClick={handleClick}>
20
+ {language.name}
21
+ </Dropdown.Item>
22
+ );
23
+ }
@@ -0,0 +1,11 @@
1
+ import { useWidgets } from '../../runtime';
2
+
3
+ export default function LeftLinks() {
4
+ const widgets = useWidgets();
5
+
6
+ return (
7
+ <div className="d-flex flex-column">
8
+ {widgets}
9
+ </div>
10
+ );
11
+ }
@@ -0,0 +1,17 @@
1
+ import { useIntl } from 'react-intl';
2
+
3
+ import { useWidgets } from '../../runtime';
4
+ import messages from '../Shell.messages';
5
+
6
+ export default function LegalNotices() {
7
+ const intl = useIntl();
8
+ const widgets = useWidgets();
9
+
10
+ return (
11
+ <div className="d-flex flex-column justify-content-center mb-3">
12
+ {widgets}
13
+ {/* This footer trademark notice is a legal requirement and cannot be removed or modified. */}
14
+ <div className="text-center x-small">{intl.formatMessage(messages.footerTrademarkNotice)}</div>
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1,17 @@
1
+ import { Hyperlink, Image } from '@openedx/paragon';
2
+ import { useIntl } from 'react-intl';
3
+
4
+ import messages from '../Shell.messages';
5
+
6
+ export default function PoweredBy() {
7
+ const intl = useIntl();
8
+ return (
9
+ <Hyperlink destination="https://openedx.org">
10
+ <Image
11
+ width="120px"
12
+ alt={intl.formatMessage(messages.footerPoweredBy)}
13
+ src="https://logos.openedx.org/open-edx-logo-tag.png"
14
+ />
15
+ </Hyperlink>
16
+ );
17
+ }
@@ -0,0 +1,43 @@
1
+ import { Button, Collapsible } from '@openedx/paragon';
2
+ import { ExpandLess, ExpandMore } from '@openedx/paragon/icons';
3
+
4
+ import { useLayoutOptions, useWidgets } from '../../runtime';
5
+ import messages from '../Shell.messages';
6
+
7
+ export default function RevealLinks() {
8
+ const widgets = useWidgets();
9
+ const options = useLayoutOptions();
10
+
11
+ const label: any = options.label ?? messages['footer.revealLinks.more'];
12
+
13
+ if (widgets.length === 0) {
14
+ return (
15
+ <div className="border-top" />
16
+ );
17
+ }
18
+
19
+ return (
20
+ <Collapsible.Advanced>
21
+ <div className="d-flex align-items-center">
22
+ <div className="border-top mr-2 flex-grow-1" />
23
+ <Collapsible.Trigger>
24
+ <Button
25
+ data-testid="helpToggleButton"
26
+ variant="outline-primary"
27
+ size="sm"
28
+ >
29
+ <span className="pr-1">{label}</span>
30
+ <Collapsible.Visible whenClosed><ExpandMore /></Collapsible.Visible>
31
+ <Collapsible.Visible whenOpen><ExpandLess /></Collapsible.Visible>
32
+ </Button>
33
+ </Collapsible.Trigger>
34
+ <div className="border-top ml-2 flex-grow-1" />
35
+ </div>
36
+ <Collapsible.Body>
37
+ <div className="d-flex justify-content-center gap-3 align-items-center my-3">
38
+ {widgets}
39
+ </div>
40
+ </Collapsible.Body>
41
+ </Collapsible.Advanced>
42
+ );
43
+ }
@@ -0,0 +1,11 @@
1
+ import { useWidgets } from '../../runtime';
2
+
3
+ export default function RightLinks() {
4
+ const widgets = useWidgets();
5
+
6
+ return (
7
+ <div className="d-flex flex-column gap-3 align-items-end flex-grow-1 justify-content-between">
8
+ {widgets}
9
+ </div>
10
+ );
11
+ }