@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.
- package/LICENSE +661 -0
- package/README.md +112 -0
- package/config/babel/babel.config.js +28 -0
- package/config/config-helpers/createConfig.js +13 -0
- package/config/config-helpers/createLintConfig.js +16 -0
- package/config/config-helpers/getBaseConfig.js +12 -0
- package/config/defaultConfigPaths.js +35 -0
- package/config/eslint/base.eslint.config.js +113 -0
- package/config/index.js +12 -0
- package/config/jest/jest.config.js +30 -0
- package/config/tsconfig.json +32 -0
- package/config/types.js +25 -0
- package/config/webpack/common-config/all/getCodeRules.js +52 -0
- package/config/webpack/common-config/all/getFileLoaderRules.js +26 -0
- package/config/webpack/common-config/all/getIgnoreWarnings.js +14 -0
- package/config/webpack/common-config/all/getImageMinimizer.js +25 -0
- package/config/webpack/common-config/all/getStylesheetRule.js +112 -0
- package/config/webpack/common-config/dev/getDevServer.js +38 -0
- package/config/webpack/common-config/index.js +18 -0
- package/config/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
- package/config/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
- package/config/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
- package/config/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
- package/config/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
- package/config/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
- package/config/webpack/types.js +2 -0
- package/config/webpack/utils/getLocalAliases.js +65 -0
- package/config/webpack/utils/getPublicPath.js +6 -0
- package/config/webpack/utils/getResolvedSiteConfigPath.js +32 -0
- package/config/webpack/utils/paragonUtils.js +138 -0
- package/config/webpack/webpack.config.build.js +80 -0
- package/config/webpack/webpack.config.dev.js +76 -0
- package/config/webpack/webpack.config.dev.shell.js +110 -0
- package/eslint.config.js +18 -0
- package/frontend-base.d.ts +8 -0
- package/index.ts +7 -0
- package/jest.config.js +7 -0
- package/openedx-frontend-base.tgz +0 -0
- package/package.json +149 -0
- package/runtime/analytics/MockAnalyticsService.js +71 -0
- package/runtime/analytics/SegmentAnalyticsService.js +243 -0
- package/runtime/analytics/index.ts +12 -0
- package/runtime/analytics/interface.js +145 -0
- package/runtime/auth/AxiosCsrfTokenService.js +60 -0
- package/runtime/auth/AxiosJwtAuthService.js +363 -0
- package/runtime/auth/AxiosJwtTokenService.js +134 -0
- package/runtime/auth/LocalForageCache.js +76 -0
- package/runtime/auth/MockAuthService.js +278 -0
- package/runtime/auth/index.ts +19 -0
- package/runtime/auth/interceptors/createCsrfTokenProviderInterceptor.js +36 -0
- package/runtime/auth/interceptors/createJwtTokenProviderInterceptor.js +37 -0
- package/runtime/auth/interceptors/createProcessAxiosRequestErrorInterceptor.js +20 -0
- package/runtime/auth/interceptors/createRetryInterceptor.js +74 -0
- package/runtime/auth/interface.js +309 -0
- package/runtime/auth/utils.js +105 -0
- package/runtime/babel.config.js +3 -0
- package/runtime/config/index.ts +295 -0
- package/runtime/constants.ts +68 -0
- package/runtime/i18n/index.js +118 -0
- package/runtime/i18n/injectIntlWithShim.jsx +48 -0
- package/runtime/i18n/lib.ts +272 -0
- package/runtime/index.ts +134 -0
- package/runtime/initialize.js +352 -0
- package/runtime/jest.config.js +32 -0
- package/runtime/logging/MockLoggingService.js +31 -0
- package/runtime/logging/NewRelicLoggingService.js +184 -0
- package/runtime/logging/index.ts +9 -0
- package/runtime/logging/interface.js +109 -0
- package/runtime/logging/types.ts +4 -0
- package/runtime/react/AuthenticatedPageRoute.jsx +43 -0
- package/runtime/react/CombinedAppProvider.tsx +46 -0
- package/runtime/react/CurrentAppContext.tsx +25 -0
- package/runtime/react/CurrentAppProvider.tsx +46 -0
- package/runtime/react/Divider.tsx +5 -0
- package/runtime/react/ErrorBoundary.jsx +47 -0
- package/runtime/react/ErrorPage.jsx +72 -0
- package/runtime/react/LoginRedirect.jsx +16 -0
- package/runtime/react/PageWrap.jsx +24 -0
- package/runtime/react/SiteContext.tsx +32 -0
- package/runtime/react/SiteProvider.tsx +78 -0
- package/runtime/react/hooks.ts +106 -0
- package/runtime/react/index.ts +19 -0
- package/runtime/routing/index.ts +1 -0
- package/runtime/routing/utils.ts +34 -0
- package/runtime/scripts/GoogleAnalyticsLoader.ts +59 -0
- package/runtime/scripts/index.ts +1 -0
- package/runtime/setupTest.js +49 -0
- package/runtime/slots/Slot.tsx +32 -0
- package/runtime/slots/SlotContext.tsx +8 -0
- package/runtime/slots/hooks.ts +35 -0
- package/runtime/slots/index.ts +7 -0
- package/runtime/slots/layout/DefaultSlotLayout.tsx +9 -0
- package/runtime/slots/layout/hooks.ts +65 -0
- package/runtime/slots/layout/index.ts +5 -0
- package/runtime/slots/layout/types.ts +45 -0
- package/runtime/slots/layout/utils.ts +14 -0
- package/runtime/slots/types.ts +23 -0
- package/runtime/slots/utils.ts +59 -0
- package/runtime/slots/widget/WidgetContext.tsx +9 -0
- package/runtime/slots/widget/WidgetProvider.tsx +30 -0
- package/runtime/slots/widget/hooks.ts +105 -0
- package/runtime/slots/widget/iframe/IFrameContentWrapper.messages.tsx +16 -0
- package/runtime/slots/widget/iframe/IFrameContentWrapper.tsx +84 -0
- package/runtime/slots/widget/iframe/IFrameWidget.tsx +59 -0
- package/runtime/slots/widget/iframe/constants.ts +19 -0
- package/runtime/slots/widget/iframe/hooks.ts +179 -0
- package/runtime/slots/widget/iframe/index.ts +6 -0
- package/runtime/slots/widget/iframe/types.ts +7 -0
- package/runtime/slots/widget/index.ts +6 -0
- package/runtime/slots/widget/types.ts +134 -0
- package/runtime/slots/widget/utils.tsx +201 -0
- package/runtime/subscriptions.ts +60 -0
- package/runtime/testing/index.ts +9 -0
- package/runtime/testing/initializeMockApp.ts +81 -0
- package/runtime/testing/mockMessages.ts +23 -0
- package/runtime/utils.js +178 -0
- package/shell/DefaultLayout.tsx +18 -0
- package/shell/DefaultMain.tsx +7 -0
- package/shell/Logo.tsx +28 -0
- package/shell/Shell.messages.ts +61 -0
- package/shell/Shell.tsx +18 -0
- package/shell/app.scss +149 -0
- package/shell/app.ts +24 -0
- package/shell/babel.config.js +3 -0
- package/shell/dev/devFooter/app.tsx +43 -0
- package/shell/dev/devFooter/index.ts +1 -0
- package/shell/dev/devHeader/BarContext.tsx +13 -0
- package/shell/dev/devHeader/BarLink.tsx +16 -0
- package/shell/dev/devHeader/BarProvider.tsx +25 -0
- package/shell/dev/devHeader/CoursesLink.tsx +16 -0
- package/shell/dev/devHeader/FooContext.tsx +13 -0
- package/shell/dev/devHeader/FooLink.tsx +16 -0
- package/shell/dev/devHeader/FooProvider.tsx +25 -0
- package/shell/dev/devHeader/app.tsx +53 -0
- package/shell/dev/devHeader/index.ts +1 -0
- package/shell/dev/devHeader/providers.tsx +11 -0
- package/shell/dev/devHome/HomePage.tsx +28 -0
- package/shell/dev/devHome/app.ts +18 -0
- package/shell/dev/devHome/i18n/index.ts +27 -0
- package/shell/dev/devHome/index.ts +1 -0
- package/shell/dev/devHome/messages.ts +11 -0
- package/shell/dev/devUser/app.tsx +24 -0
- package/shell/dev/devUser/index.ts +1 -0
- package/shell/dev/index.ts +5 -0
- package/shell/dev/slotShowcase/HorizontalSlotLayout.tsx +11 -0
- package/shell/dev/slotShowcase/LayoutWithOptions.tsx +17 -0
- package/shell/dev/slotShowcase/SlotShowcasePage.tsx +66 -0
- package/shell/dev/slotShowcase/WidgetWithOptions.tsx +11 -0
- package/shell/dev/slotShowcase/app.tsx +373 -0
- package/shell/dev/slotShowcase/index.ts +1 -0
- package/shell/footer/CenterLinks.tsx +11 -0
- package/shell/footer/CopyrightNotice.tsx +36 -0
- package/shell/footer/Footer.tsx +34 -0
- package/shell/footer/LabeledLinkColumn.tsx +19 -0
- package/shell/footer/LanguageMenu.tsx +35 -0
- package/shell/footer/LanguageMenuItem.tsx +23 -0
- package/shell/footer/LeftLinks.tsx +11 -0
- package/shell/footer/LegalNotices.tsx +17 -0
- package/shell/footer/PoweredBy.tsx +17 -0
- package/shell/footer/RevealLinks.tsx +43 -0
- package/shell/footer/RightLinks.tsx +11 -0
- package/shell/footer/app.tsx +73 -0
- package/shell/footer/data/api.ts +48 -0
- package/shell/footer/index.ts +2 -0
- package/shell/header/AuthenticatedMenu.tsx +32 -0
- package/shell/header/Header.tsx +17 -0
- package/shell/header/anonymous-menu/AnonymousMenu.tsx +14 -0
- package/shell/header/anonymous-menu/LoginButton.tsx +14 -0
- package/shell/header/anonymous-menu/RegisterButton.tsx +15 -0
- package/shell/header/app.tsx +142 -0
- package/shell/header/desktop/DesktopLayout.tsx +22 -0
- package/shell/header/desktop/PrimaryNavLinks.tsx +10 -0
- package/shell/header/desktop/SecondaryNavLinks.tsx +10 -0
- package/shell/header/index.ts +2 -0
- package/shell/header/mobile/MobileLayout.tsx +47 -0
- package/shell/header/mobile/MobileNavLinks.tsx +10 -0
- package/shell/i18n/index.ts +25 -0
- package/shell/index.ts +7 -0
- package/shell/jest.config.js +30 -0
- package/shell/menus/LinkMenuItem.tsx +64 -0
- package/shell/menus/NavDropdownMenuSlot.tsx +29 -0
- package/shell/menus/ProfileLinkMenuItem.tsx +33 -0
- package/shell/menus/data/utils.ts +19 -0
- package/shell/public/index.html +10 -0
- package/shell/router/createRouter.ts +17 -0
- package/shell/router/getAppRoutes.ts +21 -0
- package/shell/setupTest.js +48 -0
- package/shell/site.config.dev.tsx +49 -0
- package/shell/site.tsx +41 -0
- package/test-site/app.d.ts +15 -0
- package/test-site/dist/176.436443549ebb858db483.js +2 -0
- package/test-site/dist/176.436443549ebb858db483.js.map +1 -0
- package/test-site/dist/362.536eff787d2380fe246c.js +2 -0
- package/test-site/dist/362.536eff787d2380fe246c.js.map +1 -0
- package/test-site/dist/653.486966b108d224551296.js +2 -0
- package/test-site/dist/653.486966b108d224551296.js.map +1 -0
- package/test-site/dist/74e025d3fe9a7b7f8503054e2563b353.jpg +0 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js +3 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.LICENSE.txt +106 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.map +1 -0
- package/test-site/dist/95ec738c0b7faac5b5c9126794446bbd.svg +4 -0
- package/test-site/dist/app.612058b36c74787759ac.css +61 -0
- package/test-site/dist/app.612058b36c74787759ac.css.map +1 -0
- package/test-site/dist/app.612058b36c74787759ac.js +2 -0
- package/test-site/dist/app.612058b36c74787759ac.js.map +1 -0
- package/test-site/dist/cb28cdb1468c915e27e5cec9af64f22f.svg +1 -0
- package/test-site/dist/index.html +1 -0
- package/test-site/dist/report.html +39 -0
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js +2 -0
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js.map +1 -0
- package/test-site/eslint.config.js +12 -0
- package/test-site/package-lock.json +19226 -0
- package/test-site/package.json +29 -0
- package/test-site/public/index.html +10 -0
- package/test-site/site.config.build.tsx +27 -0
- package/test-site/site.config.dev.tsx +27 -0
- package/test-site/src/authenticated-page/AuthenticatedPage.tsx +18 -0
- package/test-site/src/authenticated-page/i18n/index.ts +27 -0
- package/test-site/src/authenticated-page/index.tsx +28 -0
- package/test-site/src/example-page/ExamplePage.tsx +79 -0
- package/test-site/src/example-page/Image.tsx +11 -0
- package/test-site/src/example-page/ParagonPreview.jsx +66 -0
- package/test-site/src/example-page/apple.jpg +0 -0
- package/test-site/src/example-page/apple.svg +1 -0
- package/test-site/src/example-page/index.ts +16 -0
- package/test-site/src/i18n/README.md +3 -0
- package/test-site/src/i18n/messages/frontend-app-sample/ar.json +4 -0
- package/test-site/src/i18n/messages/frontend-app-sample/eo.json +1 -0
- package/test-site/src/i18n/messages/frontend-app-sample/es_419.json +4 -0
- package/test-site/src/i18n/messages/frontend-component-emptylangs/ar.json +1 -0
- package/test-site/src/i18n/messages/frontend-component-singlelang/ar.json +3 -0
- package/test-site/src/iframe-widget/IframeWidget.tsx +14 -0
- package/test-site/src/iframe-widget/index.ts +16 -0
- package/test-site/src/index.tsx +3 -0
- package/test-site/src/messages.js +11 -0
- package/test-site/src/site.scss +11 -0
- package/test-site/tsconfig.json +14 -0
- package/tools/babel/babel.config.js +27 -0
- package/tools/babel.config.js +3 -0
- package/tools/cli/README.md +29 -0
- package/tools/cli/commands/pack.ts +9 -0
- package/tools/cli/commands/release.ts +27 -0
- package/tools/cli/commands/serve.ts +43 -0
- package/tools/cli/intl-imports.ts +274 -0
- package/tools/cli/openedx.ts +101 -0
- package/tools/cli/transifex-utils.ts +75 -0
- package/tools/cli/utils/ensureConfigFilenameOption.ts +40 -0
- package/tools/cli/utils/formatter.ts +10 -0
- package/tools/cli/utils/getResolvedConfigPath.ts +23 -0
- package/tools/cli/utils/prettyPrintTitle.ts +15 -0
- package/tools/cli/utils/printUsage.ts +53 -0
- package/tools/config-helpers/createConfig.ts +8 -0
- package/tools/config-helpers/createLintConfig.ts +14 -0
- package/tools/config-helpers/getBaseConfig.ts +11 -0
- package/tools/defaultConfigPaths.ts +30 -0
- package/tools/dist/babel/babel.config.js +28 -0
- package/tools/dist/cli/commands/pack.js +14 -0
- package/tools/dist/cli/commands/release.js +28 -0
- package/tools/dist/cli/commands/serve.js +44 -0
- package/tools/dist/cli/intl-imports.js +233 -0
- package/tools/dist/cli/openedx.js +100 -0
- package/tools/dist/cli/transifex-utils.js +68 -0
- package/tools/dist/cli/utils/ensureConfigFilenameOption.js +42 -0
- package/tools/dist/cli/utils/formatter.js +10 -0
- package/tools/dist/cli/utils/getResolvedConfigPath.js +28 -0
- package/tools/dist/cli/utils/prettyPrintTitle.js +17 -0
- package/tools/dist/cli/utils/printUsage.js +48 -0
- package/tools/dist/config-helpers/createConfig.js +13 -0
- package/tools/dist/config-helpers/createLintConfig.js +16 -0
- package/tools/dist/config-helpers/getBaseConfig.js +12 -0
- package/tools/dist/defaultConfigPaths.js +35 -0
- package/tools/dist/eslint/base.eslint.config.js +113 -0
- package/tools/dist/eslint.config.js +11 -0
- package/tools/dist/index.js +12 -0
- package/tools/dist/jest/jest.config.js +30 -0
- package/tools/dist/jest.config.js +20 -0
- package/tools/dist/types.js +25 -0
- package/tools/dist/typescript/tsconfig.json +32 -0
- package/tools/dist/webpack/common-config/all/getCodeRules.js +52 -0
- package/tools/dist/webpack/common-config/all/getFileLoaderRules.js +26 -0
- package/tools/dist/webpack/common-config/all/getIgnoreWarnings.js +14 -0
- package/tools/dist/webpack/common-config/all/getImageMinimizer.js +25 -0
- package/tools/dist/webpack/common-config/all/getStylesheetRule.js +112 -0
- package/tools/dist/webpack/common-config/dev/getDevServer.js +38 -0
- package/tools/dist/webpack/common-config/index.js +18 -0
- package/tools/dist/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
- package/tools/dist/webpack/types.js +2 -0
- package/tools/dist/webpack/utils/getLocalAliases.js +65 -0
- package/tools/dist/webpack/utils/getPublicPath.js +6 -0
- package/tools/dist/webpack/utils/getResolvedSiteConfigPath.js +32 -0
- package/tools/dist/webpack/utils/paragonUtils.js +138 -0
- package/tools/dist/webpack/webpack.config.build.js +80 -0
- package/tools/dist/webpack/webpack.config.dev.js +76 -0
- package/tools/dist/webpack/webpack.config.dev.shell.js +110 -0
- package/tools/eslint/base.eslint.config.js +124 -0
- package/tools/eslint/modules.d.ts +5 -0
- package/tools/eslint.config.js +15 -0
- package/tools/index.ts +3 -0
- package/tools/jest/jest.config.js +30 -0
- package/tools/jest.config.js +19 -0
- package/tools/tsconfig.json +24 -0
- package/tools/types.ts +21 -0
- package/tools/typescript/tsconfig.json +32 -0
- package/tools/webpack/common-config/README.md +15 -0
- package/tools/webpack/common-config/all/getCodeRules.ts +51 -0
- package/tools/webpack/common-config/all/getFileLoaderRules.ts +23 -0
- package/tools/webpack/common-config/all/getIgnoreWarnings.ts +13 -0
- package/tools/webpack/common-config/all/getImageMinimizer.ts +26 -0
- package/tools/webpack/common-config/all/getStylesheetRule.ts +111 -0
- package/tools/webpack/common-config/dev/getDevServer.ts +35 -0
- package/tools/webpack/common-config/index.ts +6 -0
- package/tools/webpack/common-config/site/getHtmlWebpackPlugin.ts +11 -0
- package/tools/webpack/modules.d.ts +6 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.ts +102 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/LICENSE +21 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/README.md +7 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/index.js +3 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +1 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.ts +134 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/index.ts +3 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.ts +71 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.ts +72 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/index.ts +6 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.ts +131 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.ts +144 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.ts +106 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.ts +54 -0
- package/tools/webpack/types.ts +69 -0
- package/tools/webpack/utils/getLocalAliases.ts +65 -0
- package/tools/webpack/utils/getPublicPath.ts +3 -0
- package/tools/webpack/utils/getResolvedSiteConfigPath.ts +28 -0
- package/tools/webpack/utils/paragonUtils.ts +152 -0
- package/tools/webpack/webpack.config.build.ts +93 -0
- package/tools/webpack/webpack.config.dev.shell.ts +122 -0
- package/tools/webpack/webpack.config.dev.ts +90 -0
- package/tsconfig.json +23 -0
- package/types.ts +99 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #### Import members from **@openedx/frontend-base logging**
|
|
3
|
+
*
|
|
4
|
+
* Contains a shared interface for logging information. (The default implementation is in
|
|
5
|
+
* NewRelicLoggingService.js.) When in development mode, all messages will instead be sent to the console.
|
|
6
|
+
*
|
|
7
|
+
* The `initialize` function performs much of the logging configuration for you. If, however,
|
|
8
|
+
* you're not using the `initialize` function, logging (via New Relic) can be configured via:
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* import { getSiteConfig, configureLogging, NewRelicLoggingService, logInfo, logError } from '@openedx/frontend-base';
|
|
12
|
+
*
|
|
13
|
+
* configureLogging(NewRelicLoggingService, {
|
|
14
|
+
* config: getSiteConfig(),
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* logInfo('Just so you know...');
|
|
18
|
+
* logInfo(new Error('Unimportant error'), { type: 'unimportant' });
|
|
19
|
+
* logError('Uhoh!');
|
|
20
|
+
* logError(new Error('Uhoh error!'));
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* As shown in this example, logging depends on the configuration document.
|
|
24
|
+
*
|
|
25
|
+
* @module Logging
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import PropTypes from 'prop-types';
|
|
29
|
+
|
|
30
|
+
const optionsShape = {
|
|
31
|
+
config: PropTypes.object.isRequired,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const serviceShape = {
|
|
35
|
+
logInfo: PropTypes.func.isRequired,
|
|
36
|
+
logError: PropTypes.func.isRequired,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
let service = null;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
44
|
+
export function configureLogging(LoggingService, options) {
|
|
45
|
+
PropTypes.checkPropTypes(optionsShape, options, 'property', 'Logging');
|
|
46
|
+
service = new LoggingService(options);
|
|
47
|
+
PropTypes.checkPropTypes(serviceShape, service, 'property', 'LoggingService');
|
|
48
|
+
return service;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Logs a message to the 'info' log level. Can accept custom attributes as a property of the error
|
|
53
|
+
* object, or as an optional second parameter.
|
|
54
|
+
*
|
|
55
|
+
* @param {string|Error} infoStringOrErrorObject
|
|
56
|
+
* @param {Object} [customAttributes={}]
|
|
57
|
+
*/
|
|
58
|
+
export function logInfo(infoStringOrErrorObject, customAttributes) {
|
|
59
|
+
return service.logInfo(infoStringOrErrorObject, customAttributes);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Logs a message to the 'error' log level. Can accept custom attributes as a property of the error
|
|
64
|
+
* object, or as an optional second parameter.
|
|
65
|
+
*
|
|
66
|
+
* @param {string|Error} errorStringOrObject
|
|
67
|
+
* @param {Object} [customAttributes={}]
|
|
68
|
+
*/
|
|
69
|
+
export function logError(errorStringOrObject, customAttributes) {
|
|
70
|
+
return service.logError(errorStringOrObject, customAttributes);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Sets a custom attribute that will be included with all subsequent log messages.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} name
|
|
77
|
+
* @param {string|number|null} value
|
|
78
|
+
*/
|
|
79
|
+
export function setCustomAttribute(name, value) {
|
|
80
|
+
return service.setCustomAttribute(name, value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @throws {Error} Thrown if the logging service has not yet been configured via {@link configureLogging}.
|
|
86
|
+
* @returns {LoggingService}
|
|
87
|
+
*/
|
|
88
|
+
export function getLoggingService() {
|
|
89
|
+
if (!service) {
|
|
90
|
+
throw Error('You must first configure the logging service.');
|
|
91
|
+
}
|
|
92
|
+
return service;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Sets the configured logging service back to null.
|
|
97
|
+
*
|
|
98
|
+
*/
|
|
99
|
+
export function resetLoggingService() {
|
|
100
|
+
service = null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @name LoggingService
|
|
105
|
+
* @interface
|
|
106
|
+
* @memberof module:Logging
|
|
107
|
+
* @property {function} logError
|
|
108
|
+
* @property {function} logInfo
|
|
109
|
+
*/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
|
|
3
|
+
import { getLoginRedirectUrl } from '../auth';
|
|
4
|
+
import PageWrap from './PageWrap';
|
|
5
|
+
import { useAuthenticatedUser } from './hooks';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A react-router route that redirects to the login page when the route becomes active and the user
|
|
9
|
+
* is not authenticated. If the application has been initialized with `requireAuthenticatedUser`
|
|
10
|
+
* false, an authenticatedPageRoute can be used to protect a subset of the application's routes,
|
|
11
|
+
* rather than the entire application.
|
|
12
|
+
*
|
|
13
|
+
* It can optionally accept an override URL to redirect to instead of the login page.
|
|
14
|
+
*
|
|
15
|
+
* Like a `PageWrap`, also calls `sendPageEvent` when the route becomes active.
|
|
16
|
+
*
|
|
17
|
+
* @see PageWrap
|
|
18
|
+
* @see {@link module:frontend-base~sendPageEvent}
|
|
19
|
+
* @memberof module:React
|
|
20
|
+
* @param {Object} props
|
|
21
|
+
* @param {string} props.redirectUrl The URL anonymous users should be redirected to, rather than
|
|
22
|
+
* viewing the route's contents.
|
|
23
|
+
*/
|
|
24
|
+
export default function AuthenticatedPageRoute({ redirectUrl = null, children }) {
|
|
25
|
+
const authenticatedUser = useAuthenticatedUser();
|
|
26
|
+
if (authenticatedUser === null) {
|
|
27
|
+
const destination = redirectUrl || getLoginRedirectUrl(global.location.href);
|
|
28
|
+
global.location.assign(destination);
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<PageWrap>
|
|
35
|
+
{children}
|
|
36
|
+
</PageWrap>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
AuthenticatedPageRoute.propTypes = {
|
|
41
|
+
redirectUrl: PropTypes.string,
|
|
42
|
+
children: PropTypes.node.isRequired,
|
|
43
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { App, AppProvider } from '../../types';
|
|
3
|
+
import { getSiteConfig } from '../config';
|
|
4
|
+
|
|
5
|
+
const combineProviders = (providers: AppProvider[]): AppProvider => {
|
|
6
|
+
return providers.reduce(
|
|
7
|
+
(AccumulatedProviders, CurrentProvider) => {
|
|
8
|
+
// eslint-disable-next-line react/prop-types
|
|
9
|
+
const CombinedProvider: AppProvider = ({ children }) => (
|
|
10
|
+
<AccumulatedProviders>
|
|
11
|
+
<CurrentProvider>{children}</CurrentProvider>
|
|
12
|
+
</AccumulatedProviders>
|
|
13
|
+
);
|
|
14
|
+
return CombinedProvider;
|
|
15
|
+
},
|
|
16
|
+
({ children }) => <>{children}</>,
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
interface CombinedAppProviderProps {
|
|
21
|
+
children: ReactNode,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function CombinedAppProvider({ children }: CombinedAppProviderProps) {
|
|
25
|
+
const { apps } = getSiteConfig();
|
|
26
|
+
|
|
27
|
+
let providers: AppProvider[] = [];
|
|
28
|
+
|
|
29
|
+
if (apps) {
|
|
30
|
+
apps.forEach(
|
|
31
|
+
(app: App) => {
|
|
32
|
+
if (Array.isArray(app.providers)) {
|
|
33
|
+
providers = providers.concat(app.providers);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const CombinedProviders = combineProviders(providers);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<CombinedProviders>
|
|
43
|
+
{children}
|
|
44
|
+
</CombinedProviders>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
import { AppConfig } from '../../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `CurrentAppContext` provides data from `App` in a way that React components can readily consume.
|
|
6
|
+
* if it's mutable data. `CurrentAppContext` contains the following data structure:
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* {
|
|
10
|
+
* config: <THE App.config OBJECT>
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
* If the `App.config` data changes, `CurrentAppContext` will be updated
|
|
14
|
+
* accordingly and pass those changes onto React components using the context.
|
|
15
|
+
*
|
|
16
|
+
* `CurrentAppContext` is used in a React application like any other `[React Context](https://reactjs.org/docs/context.html)
|
|
17
|
+
* @memberof module:React
|
|
18
|
+
*/
|
|
19
|
+
const CurrentAppContext = createContext<{
|
|
20
|
+
appConfig: AppConfig,
|
|
21
|
+
}>({
|
|
22
|
+
appConfig: {},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export default CurrentAppContext;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ReactNode, useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { getAppConfig } from '../config';
|
|
4
|
+
import { CONFIG_CHANGED } from '../constants';
|
|
5
|
+
|
|
6
|
+
import CurrentAppContext from './CurrentAppContext';
|
|
7
|
+
import { useSiteEvent } from './hooks';
|
|
8
|
+
|
|
9
|
+
interface CurrentAppProviderProps {
|
|
10
|
+
appId: string,
|
|
11
|
+
children: ReactNode,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A wrapper component for React-based micro-frontends to initialize a number of common data/
|
|
16
|
+
* context providers.
|
|
17
|
+
*
|
|
18
|
+
* ```
|
|
19
|
+
* <CurrentAppProvider appId="my.app">
|
|
20
|
+
* <HelloWorld />
|
|
21
|
+
* </CurrentAppProvider>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* This will provide the following to HelloWorld:
|
|
25
|
+
* - A `CurrentAppContext` provider for React context data.
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} props
|
|
28
|
+
* @memberof module:React
|
|
29
|
+
*/
|
|
30
|
+
export default function CurrentAppProvider({ appId, children }: CurrentAppProviderProps) {
|
|
31
|
+
const [appConfig, setAppConfig] = useState(getAppConfig(appId));
|
|
32
|
+
|
|
33
|
+
useSiteEvent(CONFIG_CHANGED, () => {
|
|
34
|
+
setAppConfig(getAppConfig(appId));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const appContextValue = useMemo(() => ({
|
|
38
|
+
appConfig,
|
|
39
|
+
}), [appConfig]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<CurrentAppContext.Provider value={appContextValue}>
|
|
43
|
+
{children}
|
|
44
|
+
</CurrentAppContext.Provider>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import { logError } from '../logging';
|
|
5
|
+
|
|
6
|
+
import ErrorPage from './ErrorPage';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error boundary component used to log caught errors and display the error page.
|
|
10
|
+
*
|
|
11
|
+
* @memberof module:React
|
|
12
|
+
* @extends {Component}
|
|
13
|
+
*/
|
|
14
|
+
class ErrorBoundary extends Component {
|
|
15
|
+
constructor(props) {
|
|
16
|
+
super(props);
|
|
17
|
+
this.state = { hasError: false };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static getDerivedStateFromError() {
|
|
21
|
+
// Update state so the next render will show the fallback UI.
|
|
22
|
+
return { hasError: true };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
componentDidCatch(error, info) {
|
|
26
|
+
logError(error, { stack: info.componentStack });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
render() {
|
|
30
|
+
if (this.state.hasError) {
|
|
31
|
+
return this.props.fallbackComponent || <ErrorPage />;
|
|
32
|
+
}
|
|
33
|
+
return this.props.children;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ErrorBoundary.propTypes = {
|
|
38
|
+
children: PropTypes.node,
|
|
39
|
+
fallbackComponent: PropTypes.node,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
ErrorBoundary.defaultProps = {
|
|
43
|
+
children: null,
|
|
44
|
+
fallbackComponent: undefined,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default ErrorBoundary;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import {
|
|
4
|
+
Button, Container, Row, Col,
|
|
5
|
+
} from '@openedx/paragon';
|
|
6
|
+
|
|
7
|
+
import { useSiteEvent } from './hooks';
|
|
8
|
+
import {
|
|
9
|
+
FormattedMessage,
|
|
10
|
+
IntlProvider,
|
|
11
|
+
getMessages,
|
|
12
|
+
getLocale,
|
|
13
|
+
LOCALE_CHANGED,
|
|
14
|
+
} from '../i18n';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* An error page that displays a generic message for unexpected errors. Also contains a "Try
|
|
18
|
+
* Again" button to refresh the page.
|
|
19
|
+
*
|
|
20
|
+
* @memberof module:React
|
|
21
|
+
* @extends {Component}
|
|
22
|
+
*/
|
|
23
|
+
function ErrorPage({
|
|
24
|
+
message = null,
|
|
25
|
+
}) {
|
|
26
|
+
const [locale, setLocale] = useState(getLocale());
|
|
27
|
+
|
|
28
|
+
useSiteEvent(LOCALE_CHANGED, () => {
|
|
29
|
+
setLocale(getLocale());
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/* istanbul ignore next */
|
|
33
|
+
const reload = () => {
|
|
34
|
+
global.location.reload();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<IntlProvider locale={locale} messages={getMessages()}>
|
|
39
|
+
<Container fluid className="py-5 justify-content-center align-items-start text-center" data-testid="error-page">
|
|
40
|
+
<Row>
|
|
41
|
+
<Col>
|
|
42
|
+
<p className="text-muted">
|
|
43
|
+
<FormattedMessage
|
|
44
|
+
id="unexpected.error.message.text"
|
|
45
|
+
defaultMessage="An unexpected error occurred. Please click the button below to refresh the page."
|
|
46
|
+
description="error message when an unexpected error occurs"
|
|
47
|
+
/>
|
|
48
|
+
</p>
|
|
49
|
+
{message && (
|
|
50
|
+
<div role="alert" className="my-4">
|
|
51
|
+
<p>{message}</p>
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
54
|
+
<Button onClick={reload}>
|
|
55
|
+
<FormattedMessage
|
|
56
|
+
id="unexpected.error.button.text"
|
|
57
|
+
defaultMessage="Try again"
|
|
58
|
+
description="text for button that tries to reload the app by refreshing the page"
|
|
59
|
+
/>
|
|
60
|
+
</Button>
|
|
61
|
+
</Col>
|
|
62
|
+
</Row>
|
|
63
|
+
</Container>
|
|
64
|
+
</IntlProvider>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ErrorPage.propTypes = {
|
|
69
|
+
message: PropTypes.string,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default ErrorPage;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { redirectToLogin } from '../auth';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A React component that, when rendered, redirects to the login page as a side effect. Uses
|
|
6
|
+
* `redirectToLogin` to perform the redirect.
|
|
7
|
+
*
|
|
8
|
+
* @see {@link module:frontend-base~redirectToLogin}
|
|
9
|
+
* @memberof module:React
|
|
10
|
+
*/
|
|
11
|
+
export default function LoginRedirect() {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
redirectToLogin(global.location.href);
|
|
14
|
+
}, []);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* eslint-disable react/prop-types */
|
|
2
|
+
// eslint-disable-next-line no-unused-vars
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { useLocation } from 'react-router-dom';
|
|
5
|
+
|
|
6
|
+
import { sendPageEvent } from '../analytics';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A Wrapper component that calls `sendPageEvent` when it becomes active.
|
|
10
|
+
*
|
|
11
|
+
* @see {@link module:frontend-base~sendPageEvent}
|
|
12
|
+
* @memberof module:React
|
|
13
|
+
* @param {Object} props
|
|
14
|
+
*/
|
|
15
|
+
export default function PageWrap({ children }) {
|
|
16
|
+
const location = useLocation();
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
sendPageEvent();
|
|
20
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
21
|
+
}, [location.pathname]);
|
|
22
|
+
|
|
23
|
+
return children;
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
import { SiteConfig, User } from '../../types';
|
|
3
|
+
import { getSiteConfig } from '../config';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `SiteContext` provides data from `App` in a way that React components can readily consume, even
|
|
7
|
+
* if it's mutable data. `SiteContext` contains the following data structure:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* {
|
|
11
|
+
* authenticatedUser
|
|
12
|
+
* siteConfig
|
|
13
|
+
* locale
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
* If `authenticatedUser`, `siteConfig`, or `locale` data changes, `SiteContext` will be updated
|
|
17
|
+
* accordingly and pass those changes onto React components using the context.
|
|
18
|
+
*
|
|
19
|
+
* `SiteContext` is used in a React application like any other `[React Context](https://reactjs.org/docs/context.html)
|
|
20
|
+
* @memberof module:React
|
|
21
|
+
*/
|
|
22
|
+
const SiteContext = createContext<{
|
|
23
|
+
authenticatedUser: User | null,
|
|
24
|
+
siteConfig: SiteConfig,
|
|
25
|
+
locale: string,
|
|
26
|
+
}>({
|
|
27
|
+
authenticatedUser: null,
|
|
28
|
+
siteConfig: getSiteConfig(),
|
|
29
|
+
locale: 'en',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export default SiteContext;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ReactNode, useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { AUTHENTICATED_USER_CHANGED, getAuthenticatedUser } from '../auth';
|
|
4
|
+
import { getSiteConfig } from '../config';
|
|
5
|
+
import { CONFIG_CHANGED } from '../constants';
|
|
6
|
+
import {
|
|
7
|
+
getLocale,
|
|
8
|
+
getMessages,
|
|
9
|
+
IntlProvider,
|
|
10
|
+
LOCALE_CHANGED,
|
|
11
|
+
} from '../i18n';
|
|
12
|
+
|
|
13
|
+
import CombinedAppProvider from './CombinedAppProvider';
|
|
14
|
+
import ErrorBoundary from './ErrorBoundary';
|
|
15
|
+
import SiteContext from './SiteContext';
|
|
16
|
+
import { useSiteEvent } from './hooks';
|
|
17
|
+
|
|
18
|
+
interface SiteProviderProps {
|
|
19
|
+
children: ReactNode,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A wrapper component for React-based micro-frontends to initialize a number of common data/
|
|
24
|
+
* context providers.
|
|
25
|
+
*
|
|
26
|
+
* ```
|
|
27
|
+
* subscribe(SITE_READY, () => {
|
|
28
|
+
* ReactDOM.render(
|
|
29
|
+
* <SiteProvider>
|
|
30
|
+
* <HelloWorld />
|
|
31
|
+
* </SiteProvider>
|
|
32
|
+
* )
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* This will provide the following to HelloWorld:
|
|
37
|
+
* - An error boundary as described above.
|
|
38
|
+
* - An `SiteContext` provider for React context data.
|
|
39
|
+
* - IntlProvider for @edx/frontend-i18n internationalization
|
|
40
|
+
*
|
|
41
|
+
* @param {Object} props
|
|
42
|
+
* @memberof module:React
|
|
43
|
+
*/
|
|
44
|
+
export default function SiteProvider({ children }: SiteProviderProps) {
|
|
45
|
+
const [siteConfig, setSiteConfig] = useState(getSiteConfig());
|
|
46
|
+
const [authenticatedUser, setAuthenticatedUser] = useState(getAuthenticatedUser());
|
|
47
|
+
const [locale, setLocale] = useState(getLocale());
|
|
48
|
+
|
|
49
|
+
useSiteEvent(AUTHENTICATED_USER_CHANGED, () => {
|
|
50
|
+
setAuthenticatedUser(getAuthenticatedUser());
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
useSiteEvent(CONFIG_CHANGED, () => {
|
|
54
|
+
setSiteConfig(getSiteConfig());
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
useSiteEvent(LOCALE_CHANGED, () => {
|
|
58
|
+
setLocale(getLocale());
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const siteContextValue = useMemo(() => ({
|
|
62
|
+
authenticatedUser,
|
|
63
|
+
siteConfig,
|
|
64
|
+
locale
|
|
65
|
+
}), [authenticatedUser, siteConfig, locale]);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<IntlProvider locale={locale} messages={getMessages()}>
|
|
69
|
+
<ErrorBoundary>
|
|
70
|
+
<SiteContext.Provider value={siteContextValue}>
|
|
71
|
+
<CombinedAppProvider>
|
|
72
|
+
{children}
|
|
73
|
+
</CombinedAppProvider>
|
|
74
|
+
</SiteContext.Provider>
|
|
75
|
+
</ErrorBoundary>
|
|
76
|
+
</IntlProvider>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { useCallback, useContext, useEffect, useState } from 'react';
|
|
2
|
+
import { useMatches } from 'react-router';
|
|
3
|
+
|
|
4
|
+
import { sendTrackEvent } from '../analytics';
|
|
5
|
+
import { getActiveRoles, setActiveRouteRoles } from '../config';
|
|
6
|
+
import { ACTIVE_ROLES_CHANGED } from '../constants';
|
|
7
|
+
import { isRoleRouteObject } from '../routing';
|
|
8
|
+
import { subscribe, unsubscribe } from '../subscriptions';
|
|
9
|
+
|
|
10
|
+
import SiteContext from './SiteContext';
|
|
11
|
+
import CurrentAppContext from './CurrentAppContext';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A React hook that allows functional components to subscribe to application events. This should
|
|
15
|
+
* be used sparingly - for the most part, Context should be used higher-up in the application to
|
|
16
|
+
* provide necessary data to a given component, rather than utilizing a non-React-like Pub/Sub
|
|
17
|
+
* mechanism.
|
|
18
|
+
*
|
|
19
|
+
* @memberof module:React
|
|
20
|
+
* @param {string} type
|
|
21
|
+
* @param {function} callback
|
|
22
|
+
*/
|
|
23
|
+
export const useSiteEvent = (type, callback) => {
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
subscribe(type, callback);
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
unsubscribe(type, callback);
|
|
29
|
+
};
|
|
30
|
+
}, [callback, type]);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A React hook that tracks user's preferred color scheme (light or dark) and sends respective
|
|
35
|
+
* event to the tracking service.
|
|
36
|
+
*
|
|
37
|
+
* @memberof module:React
|
|
38
|
+
*/
|
|
39
|
+
export const useTrackColorSchemeChoice = () => {
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const trackColorSchemeChoice = ({ matches }) => {
|
|
42
|
+
const preferredColorScheme = matches ? 'dark' : 'light';
|
|
43
|
+
sendTrackEvent('openedx.ui.frontend-base.prefers-color-scheme.selected', { preferredColorScheme });
|
|
44
|
+
};
|
|
45
|
+
const colorSchemeQuery = window.matchMedia?.('(prefers-color-scheme: dark)');
|
|
46
|
+
if (colorSchemeQuery) {
|
|
47
|
+
// send user's initial choice
|
|
48
|
+
trackColorSchemeChoice(colorSchemeQuery);
|
|
49
|
+
colorSchemeQuery.addEventListener('change', trackColorSchemeChoice);
|
|
50
|
+
}
|
|
51
|
+
return () => {
|
|
52
|
+
if (colorSchemeQuery) {
|
|
53
|
+
colorSchemeQuery.removeEventListener('change', trackColorSchemeChoice);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}, []);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export function useAuthenticatedUser() {
|
|
60
|
+
const { authenticatedUser } = useContext(SiteContext);
|
|
61
|
+
return authenticatedUser;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function useSiteConfig() {
|
|
65
|
+
const { siteConfig } = useContext(SiteContext);
|
|
66
|
+
return siteConfig;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function useAppConfig() {
|
|
70
|
+
const { appConfig } = useContext(CurrentAppContext);
|
|
71
|
+
return appConfig;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function useActiveRouteRoleWatcher() {
|
|
75
|
+
const matches = useMatches();
|
|
76
|
+
|
|
77
|
+
// We create this callback so we can use it right away to populate the default state value.
|
|
78
|
+
const findActiveRouteRoles = useCallback(() => {
|
|
79
|
+
// Starts with the widget roles and adds the others in.
|
|
80
|
+
const roles: string[] = [];
|
|
81
|
+
|
|
82
|
+
// Route roles
|
|
83
|
+
for (const match of matches) {
|
|
84
|
+
if (isRoleRouteObject(match)) {
|
|
85
|
+
if (!roles.includes(match.handle.role)) {
|
|
86
|
+
roles.push(match.handle.role);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return roles;
|
|
92
|
+
}, [matches]);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
setActiveRouteRoles(findActiveRouteRoles());
|
|
96
|
+
}, [matches, findActiveRouteRoles]);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function useActiveRoles() {
|
|
100
|
+
const [roles, setRoles] = useState<string[]>(getActiveRoles());
|
|
101
|
+
useSiteEvent(ACTIVE_ROLES_CHANGED, () => {
|
|
102
|
+
setRoles(getActiveRoles());
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return roles;
|
|
106
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #### Import members from **@openedx/frontend-base** React
|
|
3
|
+
* The React module provides a variety of React components, hooks, and contexts for use in an
|
|
4
|
+
* application.
|
|
5
|
+
*
|
|
6
|
+
* @module React
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { default as CurrentAppContext } from './CurrentAppContext';
|
|
10
|
+
export { default as CurrentAppProvider } from './CurrentAppProvider';
|
|
11
|
+
export { default as SiteContext } from './SiteContext';
|
|
12
|
+
export { default as SiteProvider } from './SiteProvider';
|
|
13
|
+
export { default as AuthenticatedPageRoute } from './AuthenticatedPageRoute';
|
|
14
|
+
export { default as Divider } from './Divider';
|
|
15
|
+
export { default as ErrorBoundary } from './ErrorBoundary';
|
|
16
|
+
export { default as ErrorPage } from './ErrorPage';
|
|
17
|
+
export { useSiteEvent, useAuthenticatedUser, useSiteConfig, useAppConfig } from './hooks';
|
|
18
|
+
export { default as LoginRedirect } from './LoginRedirect';
|
|
19
|
+
export { default as PageWrap } from './PageWrap';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './utils';
|