@opencraft/frontend-plugin-sandbox 1.1.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.
@@ -0,0 +1,56 @@
1
+ name: Build NPM package and upload as artifact to PR for testing
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ jobs:
7
+ build-and-pack:
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - name: Checkout repository
12
+ uses: actions/checkout@v6
13
+ - name: Setup Node.js
14
+ uses: actions/setup-node@v6
15
+ with:
16
+ node-version: '24'
17
+ cache: 'npm'
18
+ - name: Install dependencies
19
+ run: npm ci
20
+ - name: Build
21
+ run: npm run build
22
+ - name: Pack package
23
+ run: npm pack
24
+ - name: Upload package
25
+ id: upload-package
26
+ uses: actions/upload-artifact@v7
27
+ with:
28
+ name: frontend-plugin-sandbox-pr${{ github.event.pull_request.number }}
29
+ path: "*.tgz"
30
+ archive: false
31
+ retention-days: 21
32
+ - name: Comment with link to artifact
33
+ uses: thollander/actions-comment-pull-request@v3
34
+ with:
35
+ message: |
36
+ You can install this into an MFE using:
37
+ ```bash
38
+ RUN --mount=type=cache,target=/root/.npm,sharing=shared npm install 'https://github.com/open-craft/frontend-plugin-sandbox-headers-and-footers/archive/refs/heads/${{ github.head_ref }}.tar.gz'
39
+ ```
40
+
41
+ You can test this on a local tutor environment using the following plugin code:
42
+ ```python
43
+ hooks.Filters.ENV_PATCHES.add_item(
44
+ (
45
+ f"mfe-dockerfile-post-npm-install-MFE_NAME",
46
+ """
47
+ RUN --mount=type=cache,target=/root/.npm,sharing=shared npm install '@edx/frontend-component-header@latest'
48
+ ADD https://api.github.com/repos/open-craft/frontend-plugin-sandbox-headers-and-footers/git/refs/heads/${{ github.head_ref }} /tmp/gitref
49
+ RUN --mount=type=cache,target=/root/.npm,sharing=shared npm install 'https://github.com/open-craft/frontend-plugin-sandbox-headers-and-footers/archive/refs/heads/${{ github.head_ref }}.tar.gz'
50
+ """,
51
+ )
52
+ )
53
+ ```
54
+
55
+ If you face issues with installing from GitHub, you can install the packaged tgz file available [here](${{ steps.upload-package.outputs.artifact-url }}).
56
+ comment-tag: artifact-link
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 20
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # frontend-plugin-sandbox
2
+
3
+ ## Purpose
4
+
5
+ This repo contains plug-in code for OpenCraft's sandboxes.
6
+
7
+ ## Installation
8
+
9
+ The package can be installed from npm using:
10
+
11
+ ```bash
12
+ npm install @opencraft/frontend-plugin-sandbox-headers-and-footers
13
+ ```
14
+
15
+ Or to install from GitHub directly:
16
+
17
+ ```bash
18
+ npm install 'git+https://github.com/open-craft/frontend-plugin-sandbox-headers-and-footers.git#branch_tag_or_commit'
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ This project exposes all slots and their configuration to simplify testing. You
24
+ can add the following to your `env.config.tsx` file to enable all the slots.
25
+
26
+ ```jsx
27
+ import { slotSettings } from "@opencraft/frontend-plugin-sandbox-headers-and-footers";
28
+
29
+ const config = {
30
+ pluginSlots: slotSettings,
31
+ };
32
+
33
+ export default config;
34
+ ```
35
+
36
+ For installation using Tutor you can use the following code as reference:
37
+
38
+
39
+ ```python
40
+ from tutor import hooks
41
+
42
+ from tutormfe.hooks import PLUGIN_SLOTS
43
+
44
+ for mfe_app in ["account", "discussions", "learner-dashboard", "learning", "profile", "catalog"]:
45
+ hooks.Filters.ENV_PATCHES.add_item(
46
+ (
47
+ f"mfe-dockerfile-post-npm-install-{mfe_app}",
48
+ """
49
+ RUN --mount=type=cache,target=/root/.npm,sharing=shared npm install '@edx/frontend-component-header@latest'
50
+ RUN --mount=type=cache,target=/root/.npm,sharing=shared npm install '@opencraft/frontend-plugin-sandbox-headers-and-footers'
51
+ """,
52
+ )
53
+ )
54
+ hooks.Filters.ENV_PATCHES.add_item(
55
+ (
56
+ f"mfe-env-config-runtime-definitions-{mfe_app}",
57
+ """
58
+ const { WhiteLogo, MenuWrapper, SandboxFooter } = await import(
59
+ /* webpackFetchPriority: "eager" */
60
+ '@opencraft/frontend-plugin-sandbox-headers-and-footers'
61
+ );
62
+ """,
63
+ )
64
+ )
65
+
66
+ PLUGIN_SLOTS.add_items(
67
+ [
68
+ (
69
+ mfe_app,
70
+ "org.openedx.frontend.layout.footer.v1",
71
+ """
72
+ {
73
+ op: PLUGIN_OPERATIONS.Hide,
74
+ widgetId: 'default_contents',
75
+ }""",
76
+ ),
77
+ (
78
+ mfe_app,
79
+ "org.openedx.frontend.layout.footer.v1",
80
+ """
81
+ {
82
+ op: PLUGIN_OPERATIONS.Insert,
83
+ widget: {
84
+ id: 'custom_footer',
85
+ type: DIRECT_PLUGIN,
86
+ RenderWidget: SandboxFooter,
87
+ },
88
+ }""",
89
+ ),
90
+ (
91
+ mfe_app,
92
+ "org.openedx.frontend.layout.header_logo.v1",
93
+ """
94
+ {
95
+ op: PLUGIN_OPERATIONS.Insert,
96
+ widget: {
97
+ id: 'custom_logo',
98
+ type: DIRECT_PLUGIN,
99
+ RenderWidget: WhiteLogo,
100
+ },
101
+
102
+ }""",
103
+ ),
104
+ (
105
+ mfe_app,
106
+ "org.openedx.frontend.layout.header_desktop_user_menu.v1",
107
+ """
108
+ {
109
+ op: PLUGIN_OPERATIONS.Wrap,
110
+ widgetId: 'default_contents',
111
+ wrapper: MenuWrapper,
112
+ }""",
113
+ ),
114
+ (
115
+ mfe_app,
116
+ "org.openedx.frontend.layout.header_mobile_user_menu.v1",
117
+ """
118
+ {
119
+ op: PLUGIN_OPERATIONS.Wrap,
120
+ widgetId: 'default_contents',
121
+ wrapper: MenuWrapper,
122
+ }""",
123
+ ),
124
+ ]
125
+ )
126
+
127
+
128
+ hooks.Filters.ENV_PATCHES.add_item(
129
+ (
130
+ f"mfe-env-config-runtime-definitions-catalog",
131
+ """
132
+ const { CatalogBanner } = await import('@opencraft/frontend-plugin-sandbox-headers-and-footers');
133
+ """,
134
+ )
135
+ )
136
+
137
+ PLUGIN_SLOTS.add_items(
138
+ [
139
+ (
140
+ "catalog",
141
+ "org.openedx.frontend.catalog.home_page.banner",
142
+ """
143
+ {
144
+ op: PLUGIN_OPERATIONS.Hide,
145
+ widgetId: 'default_contents',
146
+ }""",
147
+ ), (
148
+ "catalog",
149
+ "org.openedx.frontend.catalog.home_page.banner",
150
+ """
151
+ {
152
+ op: PLUGIN_OPERATIONS.Insert,
153
+ widget: {
154
+ id: "opencraft_banner",
155
+ type: DIRECT_PLUGIN,
156
+
157
+ RenderWidget: CatalogBanner,
158
+ },
159
+ }""",
160
+ ),
161
+ ]
162
+ )
163
+ ```
@@ -0,0 +1,6 @@
1
+ const { createConfig } = require('@openedx/frontend-build');
2
+
3
+ const config = createConfig('babel-preserve-modules');
4
+ config.presets.push('@babel/preset-typescript');
5
+
6
+ module.exports = config;
Binary file
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+ import { Image, Row, Stack } from "@openedx/paragon";
3
+ import bannerBg from "../assets/banner-bg.png";
4
+ export const CatalogBanner = () => /*#__PURE__*/React.createElement(Row, {
5
+ direction: "horizontal",
6
+ className: "bg-primary"
7
+ }, /*#__PURE__*/React.createElement(Stack, {
8
+ direct: "vertical",
9
+ className: "justify-content-center px-6",
10
+ style: {
11
+ maxWidth: "55rem"
12
+ }
13
+ }, /*#__PURE__*/React.createElement("h1", {
14
+ className: "display-2 text-light-100"
15
+ }, "You\u2019re viewing a live", /*#__PURE__*/React.createElement("span", {
16
+ className: "text-brand-500"
17
+ }, " Open edX® "), "demo"), /*#__PURE__*/React.createElement("p", {
18
+ className: "h4 text-light-100 "
19
+ }, "OpenClass is a demo site by OpenCraft where you can explore the Open edX platform as a learner or course author.")), /*#__PURE__*/React.createElement(Stack, {
20
+ className: "justify-content-end",
21
+ style: {
22
+ backgroundImg: "url(../assets/banner-bg.png)"
23
+ }
24
+ }, /*#__PURE__*/React.createElement(Image, {
25
+ src: bannerBg,
26
+ style: {
27
+ height: "100%",
28
+ width: "100%",
29
+ maxHeight: "550px",
30
+ objectFit: "contain",
31
+ objectPosition: "right top"
32
+ },
33
+ alt: "illustration of a workspace with a computer screen displaying charts and graphs, accompanied by a desk lamp, coffee cup, and potted plant"
34
+ })));
35
+ //# sourceMappingURL=CatalogBanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CatalogBanner.js","names":["React","Image","Row","Stack","bannerBg","CatalogBanner","createElement","direction","className","direct","style","maxWidth","backgroundImg","src","height","width","maxHeight","objectFit","objectPosition","alt"],"sources":["../../src/components/CatalogBanner.tsx"],"sourcesContent":["import React from \"react\";\nimport { Image, Row, Stack } from \"@openedx/paragon\";\nimport bannerBg from \"../assets/banner-bg.png\";\n\nexport const CatalogBanner = () => (\n <Row\n direction=\"horizontal\"\n className=\"bg-primary\"\n >\n <Stack\n direct=\"vertical\"\n className=\"justify-content-center px-6\"\n style={{ maxWidth: \"55rem\" }}\n >\n <h1 className=\"display-2 text-light-100\">\n You’re viewing a live\n <span className=\"text-brand-500\">\n {\" Open edX® \"}\n </span>\n demo\n </h1>\n <p className=\"h4 text-light-100 \">\n OpenClass is a demo site by OpenCraft where you can explore the\n Open edX platform as a learner or course author.\n </p>\n </Stack>\n <Stack\n className=\"justify-content-end\"\n style={{\n backgroundImg: \"url(../assets/banner-bg.png)\",\n }}\n >\n <Image\n src={bannerBg}\n style={{\n height: \"100%\",\n width: \"100%\",\n maxHeight: \"550px\",\n objectFit: \"contain\",\n objectPosition: \"right top\",\n }}\n alt=\"illustration of a workspace with a computer screen displaying charts and graphs, accompanied by a desk lamp, coffee cup, and potted plant\"\n />\n </Stack>\n </Row>\n);\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,KAAK,EAAEC,GAAG,EAAEC,KAAK,QAAQ,kBAAkB;AACpD,OAAOC,QAAQ,MAAM,yBAAyB;AAE9C,OAAO,MAAMC,aAAa,GAAGA,CAAA,kBACzBL,KAAA,CAAAM,aAAA,CAACJ,GAAG;EACAK,SAAS,EAAC,YAAY;EACtBC,SAAS,EAAC;AAAY,gBAEtBR,KAAA,CAAAM,aAAA,CAACH,KAAK;EACFM,MAAM,EAAC,UAAU;EACjBD,SAAS,EAAC,6BAA6B;EACvCE,KAAK,EAAE;IAAEC,QAAQ,EAAE;EAAQ;AAAE,gBAE7BX,KAAA,CAAAM,aAAA;EAAIE,SAAS,EAAC;AAA0B,GAAC,4BAErC,eAAAR,KAAA,CAAAM,aAAA;EAAME,SAAS,EAAC;AAAgB,GAC3B,aACC,CAAC,QAEP,CAAC,eACLR,KAAA,CAAAM,aAAA;EAAGE,SAAS,EAAC;AAAoB,GAAC,kHAG/B,CACA,CAAC,eACRR,KAAA,CAAAM,aAAA,CAACH,KAAK;EACFK,SAAS,EAAC,qBAAqB;EAC/BE,KAAK,EAAE;IACHE,aAAa,EAAE;EACnB;AAAE,gBAEFZ,KAAA,CAAAM,aAAA,CAACL,KAAK;EACFY,GAAG,EAAET,QAAS;EACdM,KAAK,EAAE;IACHI,MAAM,EAAE,MAAM;IACdC,KAAK,EAAE,MAAM;IACbC,SAAS,EAAE,OAAO;IAClBC,SAAS,EAAE,SAAS;IACpBC,cAAc,EAAE;EACpB,CAAE;EACFC,GAAG,EAAC;AAA2I,CAClJ,CACE,CACN,CACR","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ import React, { useContext } from 'react';
2
+ import Avatar from '@edx/frontend-component-header/dist/Avatar';
3
+ import { DesktopUserMenuTogglePropTypes } from '@edx/frontend-component-header/dist/desktop-header/DesktopUserMenuToggle';
4
+ import { AppContext } from '@edx/frontend-platform/react';
5
+ export const DropDownToggler = ({
6
+ avatar
7
+ }) => {
8
+ const {
9
+ authenticatedUser
10
+ } = useContext(AppContext);
11
+ return /*#__PURE__*/React.createElement(Avatar, {
12
+ size: "1.5em",
13
+ src: avatar,
14
+ alt: authenticatedUser.username
15
+ });
16
+ };
17
+ DropDownToggler.propTypes = DesktopUserMenuTogglePropTypes;
18
+ //# sourceMappingURL=DropDownToggler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropDownToggler.js","names":["React","useContext","Avatar","DesktopUserMenuTogglePropTypes","AppContext","DropDownToggler","avatar","authenticatedUser","createElement","size","src","alt","username","propTypes"],"sources":["../../src/components/DropDownToggler.tsx"],"sourcesContent":["import React, { useContext } from 'react';\nimport Avatar from '@edx/frontend-component-header/dist/Avatar';\nimport { DesktopUserMenuTogglePropTypes } from '@edx/frontend-component-header/dist/desktop-header/DesktopUserMenuToggle';\nimport { AppContext } from '@edx/frontend-platform/react';\nimport { AppContextInterface } from '../types';\n\nexport const DropDownToggler = ({ avatar }): { avatar: string } => {\n const { authenticatedUser } = useContext<AppContextInterface>(AppContext);\n return (\n <Avatar size=\"1.5em\" src={avatar} alt={authenticatedUser.username} />\n );\n};\n\nDropDownToggler.propTypes = DesktopUserMenuTogglePropTypes;\n"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,UAAU,QAAQ,OAAO;AACzC,OAAOC,MAAM,MAAM,4CAA4C;AAC/D,SAASC,8BAA8B,QAAQ,0EAA0E;AACzH,SAASC,UAAU,QAAQ,8BAA8B;AAGzD,OAAO,MAAMC,eAAe,GAAGA,CAAC;EAAEC;AAAO,CAAC,KAAyB;EACjE,MAAM;IAAEC;EAAkB,CAAC,GAAGN,UAAU,CAAsBG,UAAU,CAAC;EACzE,oBACEJ,KAAA,CAAAQ,aAAA,CAACN,MAAM;IAACO,IAAI,EAAC,OAAO;IAACC,GAAG,EAAEJ,MAAO;IAACK,GAAG,EAAEJ,iBAAiB,CAACK;EAAS,CAAE,CAAC;AAEzE,CAAC;AAEDP,eAAe,CAACQ,SAAS,GAAGV,8BAA8B","ignoreList":[]}
@@ -0,0 +1,24 @@
1
+ import React, { useContext } from 'react';
2
+ import { AppContext } from '@edx/frontend-platform/react';
3
+ import Avatar from '@edx/frontend-component-header/dist/Avatar';
4
+ export const MenuWrapper = ({
5
+ component
6
+ }) => {
7
+ const {
8
+ authenticatedUser
9
+ } = useContext(AppContext);
10
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
11
+ className: "dropdown-header profile-label",
12
+ role: "heading",
13
+ "aria-level": 1
14
+ }, /*#__PURE__*/React.createElement("div", {
15
+ className: "d-flex"
16
+ }, /*#__PURE__*/React.createElement(Avatar, {
17
+ size: "1.5rem"
18
+ }), /*#__PURE__*/React.createElement("div", {
19
+ className: "pl-2 align-self-center"
20
+ }, authenticatedUser.username))), /*#__PURE__*/React.createElement("div", {
21
+ className: "dropdown-divider"
22
+ }), component);
23
+ };
24
+ //# sourceMappingURL=MenuWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MenuWrapper.js","names":["React","useContext","AppContext","Avatar","MenuWrapper","component","authenticatedUser","createElement","Fragment","className","role","size","username"],"sources":["../../src/components/MenuWrapper.tsx"],"sourcesContent":["import React, { ReactElement, useContext } from 'react';\nimport { AppContext } from '@edx/frontend-platform/react';\nimport Avatar from '@edx/frontend-component-header/dist/Avatar';\nimport { AppContextInterface } from '../types';\n\nexport const MenuWrapper = ({ component }: { component: ReactElement }) => {\n const { authenticatedUser } = useContext<AppContextInterface>(AppContext);\n return (\n <>\n <div className=\"dropdown-header profile-label\" role=\"heading\" aria-level={1}>\n <div className=\"d-flex\">\n <Avatar size=\"1.5rem\" />\n <div className=\"pl-2 align-self-center\">{authenticatedUser.username}</div>\n </div>\n </div>\n <div className=\"dropdown-divider\" />\n {component}\n </>\n );\n};\n"],"mappings":"AAAA,OAAOA,KAAK,IAAkBC,UAAU,QAAQ,OAAO;AACvD,SAASC,UAAU,QAAQ,8BAA8B;AACzD,OAAOC,MAAM,MAAM,4CAA4C;AAG/D,OAAO,MAAMC,WAAW,GAAGA,CAAC;EAAEC;AAAuC,CAAC,KAAK;EACzE,MAAM;IAAEC;EAAkB,CAAC,GAAGL,UAAU,CAAsBC,UAAU,CAAC;EACzE,oBACEF,KAAA,CAAAO,aAAA,CAAAP,KAAA,CAAAQ,QAAA,qBACER,KAAA,CAAAO,aAAA;IAAKE,SAAS,EAAC,+BAA+B;IAACC,IAAI,EAAC,SAAS;IAAC,cAAY;EAAE,gBAC1EV,KAAA,CAAAO,aAAA;IAAKE,SAAS,EAAC;EAAQ,gBACrBT,KAAA,CAAAO,aAAA,CAACJ,MAAM;IAACQ,IAAI,EAAC;EAAQ,CAAE,CAAC,eACxBX,KAAA,CAAAO,aAAA;IAAKE,SAAS,EAAC;EAAwB,GAAEH,iBAAiB,CAACM,QAAc,CACtE,CACF,CAAC,eACNZ,KAAA,CAAAO,aAAA;IAAKE,SAAS,EAAC;EAAkB,CAAE,CAAC,EACnCJ,SACD,CAAC;AAEP,CAAC","ignoreList":[]}
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ export const SandboxFooter = () => /*#__PURE__*/React.createElement("footer", {
3
+ className: "sandbox-footer py-5 px-5"
4
+ }, /*#__PURE__*/React.createElement("div", {
5
+ className: "d-flex pb-3 flex-column flex-md-row"
6
+ }, /*#__PURE__*/React.createElement("div", {
7
+ className: "text-center"
8
+ }, /*#__PURE__*/React.createElement("img", {
9
+ alt: "OpenCraft Logo",
10
+ className: "sandbox-logo pt-1",
11
+ src: "https://opencraft.com/wp-content/uploads/opencraft-logo.png"
12
+ })), /*#__PURE__*/React.createElement("div", {
13
+ className: "pr-5 d-none d-md-block"
14
+ }), /*#__PURE__*/React.createElement("div", {
15
+ className: "pt-3 d-block d-md-none"
16
+ }), /*#__PURE__*/React.createElement("div", {
17
+ className: "align-self-top text-center text-md-left"
18
+ }, "Hosted by ", /*#__PURE__*/React.createElement("a", {
19
+ href: "https://opencraft.com/"
20
+ }, "OpenCraft."), /*#__PURE__*/React.createElement("br", null), "A verified Open edX\xAE partner."), /*#__PURE__*/React.createElement("div", {
21
+ className: "flex-grow-1 text-right d-none d-md-block"
22
+ }, /*#__PURE__*/React.createElement("img", {
23
+ className: "openedx-logo",
24
+ alt: "Powered by Open edX",
25
+ src: "https://openedx.org/wp-content/uploads/2025/07/Powered-by-Open-edX-WHITE.png"
26
+ }))), /*#__PURE__*/React.createElement("hr", null), /*#__PURE__*/React.createElement("div", {
27
+ className: "text-center d-block d-md-none my-4"
28
+ }, /*#__PURE__*/React.createElement("img", {
29
+ className: "openedx-logo",
30
+ alt: "Powered by Open edX",
31
+ src: "https://openedx.org/wp-content/uploads/2025/07/Powered-by-Open-edX-WHITE.png"
32
+ })), /*#__PURE__*/React.createElement("div", {
33
+ className: "text-center text-md-left"
34
+ }, /*#__PURE__*/React.createElement("small", null, "\xA9 OpenCraft 2025 | edX and Open edX\xAE are trademarks of edX Inc")));
35
+ //# sourceMappingURL=SandboxFooter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SandboxFooter.js","names":["React","SandboxFooter","createElement","className","alt","src","href"],"sources":["../../src/components/SandboxFooter.tsx"],"sourcesContent":["import React from 'react';\n\nexport const SandboxFooter = () => (\n <footer className=\"sandbox-footer py-5 px-5\">\n <div className=\"d-flex pb-3 flex-column flex-md-row\">\n <div className=\"text-center\">\n <img alt=\"OpenCraft Logo\" className=\"sandbox-logo pt-1\" src=\"https://opencraft.com/wp-content/uploads/opencraft-logo.png\" />\n </div>\n <div className=\"pr-5 d-none d-md-block\" />\n <div className=\"pt-3 d-block d-md-none\" />\n <div className=\"align-self-top text-center text-md-left\">\n Hosted by <a href=\"https://opencraft.com/\">OpenCraft.</a><br />\n A verified Open edX&reg; partner.\n </div>\n <div className=\"flex-grow-1 text-right d-none d-md-block\">\n <img className=\"openedx-logo\" alt=\"Powered by Open edX\" src=\"https://openedx.org/wp-content/uploads/2025/07/Powered-by-Open-edX-WHITE.png\" />\n </div>\n </div>\n <hr />\n <div className=\"text-center d-block d-md-none my-4\">\n <img className=\"openedx-logo\" alt=\"Powered by Open edX\" src=\"https://openedx.org/wp-content/uploads/2025/07/Powered-by-Open-edX-WHITE.png\" />\n </div>\n <div className=\"text-center text-md-left\"><small>&copy; OpenCraft 2025 | edX and Open edX&reg; are trademarks of edX Inc</small></div>\n </footer>\n);\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AAEzB,OAAO,MAAMC,aAAa,GAAGA,CAAA,kBAC3BD,KAAA,CAAAE,aAAA;EAAQC,SAAS,EAAC;AAA0B,gBAC1CH,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAAqC,gBAClDH,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAAa,gBAC1BH,KAAA,CAAAE,aAAA;EAAKE,GAAG,EAAC,gBAAgB;EAACD,SAAS,EAAC,mBAAmB;EAACE,GAAG,EAAC;AAA6D,CAAE,CACxH,CAAC,eACNL,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAAwB,CAAE,CAAC,eAC1CH,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAAwB,CAAE,CAAC,eAC1CH,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAAyC,GAAC,YAC7C,eAAAH,KAAA,CAAAE,aAAA;EAAGI,IAAI,EAAC;AAAwB,GAAC,YAAa,CAAC,eAAAN,KAAA,CAAAE,aAAA,WAAK,CAAC,oCAE5D,CAAC,eACNF,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAA0C,gBACvDH,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC,cAAc;EAACC,GAAG,EAAC,qBAAqB;EAACC,GAAG,EAAC;AAA8E,CAAE,CACzI,CACF,CAAC,eACNL,KAAA,CAAAE,aAAA,WAAK,CAAC,eACNF,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAAoC,gBACjDH,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC,cAAc;EAACC,GAAG,EAAC,qBAAqB;EAACC,GAAG,EAAC;AAA8E,CAAE,CACzI,CAAC,eACNL,KAAA,CAAAE,aAAA;EAAKC,SAAS,EAAC;AAA0B,gBAACH,KAAA,CAAAE,aAAA,gBAAO,wEAAgF,CAAM,CACjI,CACT","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ import React, { useContext } from 'react';
2
+ import { AppContext } from '@edx/frontend-platform/react';
3
+ import { ensureConfig } from '@edx/frontend-platform';
4
+ ensureConfig(['LOGO_WHITE_URL'], 'White Header Logo');
5
+ export const WhiteLogo = () => {
6
+ const {
7
+ config
8
+ } = useContext(AppContext);
9
+ return /*#__PURE__*/React.createElement("a", {
10
+ className: "logo",
11
+ href: `${config.LMS_BASE_URL}/dashboard`
12
+ }, /*#__PURE__*/React.createElement("img", {
13
+ className: "d-block",
14
+ src: config.LOGO_WHITE_URL,
15
+ alt: config.SITE_NAME
16
+ }));
17
+ };
18
+ //# sourceMappingURL=WhiteLogo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WhiteLogo.js","names":["React","useContext","AppContext","ensureConfig","WhiteLogo","config","createElement","className","href","LMS_BASE_URL","src","LOGO_WHITE_URL","alt","SITE_NAME"],"sources":["../../src/components/WhiteLogo.tsx"],"sourcesContent":["import React, { useContext } from 'react';\nimport { AppContext } from '@edx/frontend-platform/react';\nimport { ensureConfig } from '@edx/frontend-platform';\n\nensureConfig([\n 'LOGO_WHITE_URL',\n], 'White Header Logo');\n\nexport const WhiteLogo = () => {\n const { config } = useContext<AppContextInterface>(AppContext);\n return (\n <a className=\"logo\" href={`${config.LMS_BASE_URL}/dashboard`}>\n <img className=\"d-block\" src={config.LOGO_WHITE_URL} alt={config.SITE_NAME} />\n </a>\n );\n};\n"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,UAAU,QAAQ,OAAO;AACzC,SAASC,UAAU,QAAQ,8BAA8B;AACzD,SAASC,YAAY,QAAQ,wBAAwB;AAErDA,YAAY,CAAC,CACX,gBAAgB,CACjB,EAAE,mBAAmB,CAAC;AAEvB,OAAO,MAAMC,SAAS,GAAGA,CAAA,KAAM;EAC7B,MAAM;IAAEC;EAAO,CAAC,GAAGJ,UAAU,CAAsBC,UAAU,CAAC;EAC9D,oBACEF,KAAA,CAAAM,aAAA;IAAGC,SAAS,EAAC,MAAM;IAACC,IAAI,EAAE,GAAGH,MAAM,CAACI,YAAY;EAAa,gBAC3DT,KAAA,CAAAM,aAAA;IAAKC,SAAS,EAAC,SAAS;IAACG,GAAG,EAAEL,MAAM,CAACM,cAAe;IAACC,GAAG,EAAEP,MAAM,CAACQ;EAAU,CAAE,CAC5E,CAAC;AAER,CAAC","ignoreList":[]}
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
2
+ import { WhiteLogo } from './components/WhiteLogo';
3
+ import { MenuWrapper } from './components/MenuWrapper';
4
+ import { SandboxFooter } from './components/SandboxFooter';
5
+ import { DropDownToggler } from './components/DropDownToggler';
6
+ import { CatalogBanner } from './components/CatalogBanner';
7
+ export { WhiteLogo, MenuWrapper, SandboxFooter, DropDownToggler, CatalogBanner };
8
+ // Example slot settings you can use in your deployment. You may be able to import these
9
+ // for quick development usage.
10
+ export const slotSettings = {
11
+ logo_slot: {
12
+ keepDefault: false,
13
+ plugins: [{
14
+ op: PLUGIN_OPERATIONS.Insert,
15
+ widget: {
16
+ id: 'custom_logo',
17
+ type: DIRECT_PLUGIN,
18
+ RenderWidget: WhiteLogo
19
+ }
20
+ }]
21
+ },
22
+ 'org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1': {
23
+ keepDefault: false,
24
+ plugins: [{
25
+ op: PLUGIN_OPERATIONS.Insert,
26
+ widget: {
27
+ id: 'custom_desktop_toggle',
28
+ type: DIRECT_PLUGIN,
29
+ RenderWidget: DropDownToggler
30
+ }
31
+ }]
32
+ },
33
+ desktop_user_menu_slot: {
34
+ keepDefault: true,
35
+ plugins: [{
36
+ op: PLUGIN_OPERATIONS.Wrap,
37
+ widgetId: 'default_contents',
38
+ wrapper: MenuWrapper
39
+ }]
40
+ },
41
+ mobile_user_menu_slot: {
42
+ keepDefault: true,
43
+ plugins: [{
44
+ op: PLUGIN_OPERATIONS.Wrap,
45
+ widgetId: 'default_contents',
46
+ wrapper: MenuWrapper
47
+ }]
48
+ },
49
+ footer_slot: {
50
+ plugins: [{
51
+ op: PLUGIN_OPERATIONS.Hide,
52
+ widgetId: 'default_contents'
53
+ }, {
54
+ op: PLUGIN_OPERATIONS.Insert,
55
+ widget: {
56
+ id: 'custom_footer',
57
+ type: DIRECT_PLUGIN,
58
+ RenderWidget: SandboxFooter
59
+ }
60
+ }]
61
+ },
62
+ "org.openedx.frontend.catalog.home_page.banner": {
63
+ plugins: [{
64
+ op: PLUGIN_OPERATIONS.Hide,
65
+ widgetId: 'default_contents'
66
+ }, {
67
+ op: PLUGIN_OPERATIONS.Insert,
68
+ widget: {
69
+ id: 'custom_catalog_banner',
70
+ type: DIRECT_PLUGIN,
71
+ RenderWidget: CatalogBanner
72
+ }
73
+ }]
74
+ }
75
+ };
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["DIRECT_PLUGIN","PLUGIN_OPERATIONS","WhiteLogo","MenuWrapper","SandboxFooter","DropDownToggler","CatalogBanner","slotSettings","logo_slot","keepDefault","plugins","op","Insert","widget","id","type","RenderWidget","desktop_user_menu_slot","Wrap","widgetId","wrapper","mobile_user_menu_slot","footer_slot","Hide"],"sources":["../src/index.ts"],"sourcesContent":["import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';\nimport { WhiteLogo } from './components/WhiteLogo';\nimport { MenuWrapper } from './components/MenuWrapper';\nimport { SandboxFooter } from './components/SandboxFooter';\nimport { DropDownToggler } from './components/DropDownToggler';\nimport { CatalogBanner } from './components/CatalogBanner';\nexport {\n WhiteLogo, MenuWrapper, SandboxFooter, DropDownToggler, CatalogBanner\n};\n// Example slot settings you can use in your deployment. You may be able to import these\n// for quick development usage.\nexport const slotSettings = {\n logo_slot: {\n keepDefault: false,\n plugins: [\n {\n op: PLUGIN_OPERATIONS.Insert,\n widget: {\n id: 'custom_logo',\n type: DIRECT_PLUGIN,\n RenderWidget: WhiteLogo,\n },\n },\n ],\n },\n 'org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1': {\n keepDefault: false,\n plugins: [\n {\n op: PLUGIN_OPERATIONS.Insert,\n widget: {\n id: 'custom_desktop_toggle',\n type: DIRECT_PLUGIN,\n RenderWidget: DropDownToggler,\n },\n },\n ],\n },\n desktop_user_menu_slot: {\n keepDefault: true,\n plugins: [\n {\n op: PLUGIN_OPERATIONS.Wrap,\n widgetId: 'default_contents',\n wrapper: MenuWrapper,\n },\n ],\n },\n mobile_user_menu_slot: {\n keepDefault: true,\n plugins: [\n {\n op: PLUGIN_OPERATIONS.Wrap,\n widgetId: 'default_contents',\n wrapper: MenuWrapper,\n },\n ],\n },\n footer_slot: {\n plugins: [\n {\n op: PLUGIN_OPERATIONS.Hide,\n widgetId: 'default_contents',\n },\n {\n op: PLUGIN_OPERATIONS.Insert,\n widget: {\n id: 'custom_footer',\n type: DIRECT_PLUGIN,\n RenderWidget: SandboxFooter,\n },\n },\n ],\n },\n \"org.openedx.frontend.catalog.home_page.banner\": {\n plugins: [\n {\n op: PLUGIN_OPERATIONS.Hide,\n widgetId: 'default_contents',\n },\n {\n op: PLUGIN_OPERATIONS.Insert,\n widget: {\n id: 'custom_catalog_banner',\n type: DIRECT_PLUGIN,\n RenderWidget: CatalogBanner,\n },\n },\n ],\n }\n};\n"],"mappings":"AAAA,SAASA,aAAa,EAAEC,iBAAiB,QAAQ,oCAAoC;AACrF,SAASC,SAAS,QAAQ,wBAAwB;AAClD,SAASC,WAAW,QAAQ,0BAA0B;AACtD,SAASC,aAAa,QAAQ,4BAA4B;AAC1D,SAASC,eAAe,QAAQ,8BAA8B;AAC9D,SAASC,aAAa,QAAQ,4BAA4B;AAC1D,SACEJ,SAAS,EAAEC,WAAW,EAAEC,aAAa,EAAEC,eAAe,EAAEC,aAAa;AAEvE;AACA;AACA,OAAO,MAAMC,YAAY,GAAG;EAC1BC,SAAS,EAAE;IACTC,WAAW,EAAE,KAAK;IAClBC,OAAO,EAAE,CACP;MACEC,EAAE,EAAEV,iBAAiB,CAACW,MAAM;MAC5BC,MAAM,EAAE;QACNC,EAAE,EAAE,aAAa;QACjBC,IAAI,EAAEf,aAAa;QACnBgB,YAAY,EAAEd;MAChB;IACF,CAAC;EAEL,CAAC;EACD,gEAAgE,EAAE;IAChEO,WAAW,EAAE,KAAK;IAClBC,OAAO,EAAE,CACP;MACEC,EAAE,EAAEV,iBAAiB,CAACW,MAAM;MAC5BC,MAAM,EAAE;QACNC,EAAE,EAAE,uBAAuB;QAC3BC,IAAI,EAAEf,aAAa;QACnBgB,YAAY,EAAEX;MAChB;IACF,CAAC;EAEL,CAAC;EACDY,sBAAsB,EAAE;IACtBR,WAAW,EAAE,IAAI;IACjBC,OAAO,EAAE,CACP;MACEC,EAAE,EAAEV,iBAAiB,CAACiB,IAAI;MAC1BC,QAAQ,EAAE,kBAAkB;MAC5BC,OAAO,EAAEjB;IACX,CAAC;EAEL,CAAC;EACDkB,qBAAqB,EAAE;IACrBZ,WAAW,EAAE,IAAI;IACjBC,OAAO,EAAE,CACP;MACEC,EAAE,EAAEV,iBAAiB,CAACiB,IAAI;MAC1BC,QAAQ,EAAE,kBAAkB;MAC5BC,OAAO,EAAEjB;IACX,CAAC;EAEL,CAAC;EACDmB,WAAW,EAAE;IACXZ,OAAO,EAAE,CACP;MACEC,EAAE,EAAEV,iBAAiB,CAACsB,IAAI;MAC1BJ,QAAQ,EAAE;IACZ,CAAC,EACD;MACER,EAAE,EAAEV,iBAAiB,CAACW,MAAM;MAC5BC,MAAM,EAAE;QACNC,EAAE,EAAE,eAAe;QACnBC,IAAI,EAAEf,aAAa;QACnBgB,YAAY,EAAEZ;MAChB;IACF,CAAC;EAEL,CAAC;EACD,+CAA+C,EAAE;IAC/CM,OAAO,EAAE,CACP;MACEC,EAAE,EAAEV,iBAAiB,CAACsB,IAAI;MAC1BJ,QAAQ,EAAE;IACZ,CAAC,EACD;MACER,EAAE,EAAEV,iBAAiB,CAACW,MAAM;MAC5BC,MAAM,EAAE;QACNC,EAAE,EAAE,uBAAuB;QAC3BC,IAAI,EAAEf,aAAa;QACnBgB,YAAY,EAAEV;MAChB;IACF,CAAC;EAEL;AACF,CAAC","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.js","names":[],"sources":["../src/types.d.ts"],"sourcesContent":["export interface AppContextInterface {\n config: {\n LMS_BASE_URL: string,\n LOGO_WHITE_URL: string,\n SITE_NAME: string,\n },\n authenticatedUser: {\n username: string,\n }\n}\n"],"mappings":"","ignoreList":[]}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@opencraft/frontend-plugin-sandbox",
3
+ "version": "1.1.0",
4
+ "description": "Plugin for custom components in OpenCraft's sandboxes",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/open-craft/frontend-plugin-sandbox.git"
8
+ },
9
+ "browserslist": [
10
+ "extends @edx/browserslist-config"
11
+ ],
12
+ "main": "dist/index.js",
13
+ "author": "OpenCraft",
14
+ "license": "AGPL-3.0",
15
+ "homepage": "https://github.com/open-craft/frontend-plugin-sandbox#readme",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/open-craft/frontend-plugin-sandbox/issues"
21
+ },
22
+ "peerDependencies": {
23
+ "@edx/frontend-component-header": ">=6.6.0",
24
+ "@edx/frontend-platform": ">=7.0.0",
25
+ "@openedx/frontend-build": ">=13.0.0",
26
+ "@openedx/frontend-plugin-framework": "^1.2.1",
27
+ "@openedx/paragon": "^23.14.2",
28
+ "@types/react": ">=17.0.39",
29
+ "classnames": "*",
30
+ "react": ">=17.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@edx/browserslist-config": "^1.1.1",
34
+ "@openedx/frontend-build": "^14.2.0"
35
+ },
36
+ "scripts": {
37
+ "postinstall": "npm run build",
38
+ "build": "fedx-scripts babel -x .ts,.tsx src --out-dir dist --source-maps --ignore **/*.test.jsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files",
39
+ "lint": "fedx-scripts eslint --ext .ts --ext .tsx src/",
40
+ "lint:fix": "fedx-scripts eslint --fix --ext .ts --ext .tsx src/",
41
+ "test": "fedx-scripts jest --coverage --passWithNoTests"
42
+ }
43
+ }
Binary file
@@ -0,0 +1,46 @@
1
+ import React from "react";
2
+ import { Image, Row, Stack } from "@openedx/paragon";
3
+ import bannerBg from "../assets/banner-bg.png";
4
+
5
+ export const CatalogBanner = () => (
6
+ <Row
7
+ direction="horizontal"
8
+ className="bg-primary"
9
+ >
10
+ <Stack
11
+ direct="vertical"
12
+ className="justify-content-center px-6"
13
+ style={{ maxWidth: "55rem" }}
14
+ >
15
+ <h1 className="display-2 text-light-100">
16
+ You’re viewing a live
17
+ <span className="text-brand-500">
18
+ {" Open edX® "}
19
+ </span>
20
+ demo
21
+ </h1>
22
+ <p className="h4 text-light-100 ">
23
+ OpenClass is a demo site by OpenCraft where you can explore the
24
+ Open edX platform as a learner or course author.
25
+ </p>
26
+ </Stack>
27
+ <Stack
28
+ className="justify-content-end"
29
+ style={{
30
+ backgroundImg: "url(../assets/banner-bg.png)",
31
+ }}
32
+ >
33
+ <Image
34
+ src={bannerBg}
35
+ style={{
36
+ height: "100%",
37
+ width: "100%",
38
+ maxHeight: "550px",
39
+ objectFit: "contain",
40
+ objectPosition: "right top",
41
+ }}
42
+ alt="illustration of a workspace with a computer screen displaying charts and graphs, accompanied by a desk lamp, coffee cup, and potted plant"
43
+ />
44
+ </Stack>
45
+ </Row>
46
+ );
@@ -0,0 +1,14 @@
1
+ import React, { useContext } from 'react';
2
+ import Avatar from '@edx/frontend-component-header/dist/Avatar';
3
+ import { DesktopUserMenuTogglePropTypes } from '@edx/frontend-component-header/dist/desktop-header/DesktopUserMenuToggle';
4
+ import { AppContext } from '@edx/frontend-platform/react';
5
+ import { AppContextInterface } from '../types';
6
+
7
+ export const DropDownToggler = ({ avatar }): { avatar: string } => {
8
+ const { authenticatedUser } = useContext<AppContextInterface>(AppContext);
9
+ return (
10
+ <Avatar size="1.5em" src={avatar} alt={authenticatedUser.username} />
11
+ );
12
+ };
13
+
14
+ DropDownToggler.propTypes = DesktopUserMenuTogglePropTypes;
@@ -0,0 +1,20 @@
1
+ import React, { ReactElement, useContext } from 'react';
2
+ import { AppContext } from '@edx/frontend-platform/react';
3
+ import Avatar from '@edx/frontend-component-header/dist/Avatar';
4
+ import { AppContextInterface } from '../types';
5
+
6
+ export const MenuWrapper = ({ component }: { component: ReactElement }) => {
7
+ const { authenticatedUser } = useContext<AppContextInterface>(AppContext);
8
+ return (
9
+ <>
10
+ <div className="dropdown-header profile-label" role="heading" aria-level={1}>
11
+ <div className="d-flex">
12
+ <Avatar size="1.5rem" />
13
+ <div className="pl-2 align-self-center">{authenticatedUser.username}</div>
14
+ </div>
15
+ </div>
16
+ <div className="dropdown-divider" />
17
+ {component}
18
+ </>
19
+ );
20
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+
3
+ export const SandboxFooter = () => (
4
+ <footer className="sandbox-footer py-5 px-5">
5
+ <div className="d-flex pb-3 flex-column flex-md-row">
6
+ <div className="text-center">
7
+ <img alt="OpenCraft Logo" className="sandbox-logo pt-1" src="https://opencraft.com/wp-content/uploads/opencraft-logo.png" />
8
+ </div>
9
+ <div className="pr-5 d-none d-md-block" />
10
+ <div className="pt-3 d-block d-md-none" />
11
+ <div className="align-self-top text-center text-md-left">
12
+ Hosted by <a href="https://opencraft.com/">OpenCraft.</a><br />
13
+ A verified Open edX&reg; partner.
14
+ </div>
15
+ <div className="flex-grow-1 text-right d-none d-md-block">
16
+ <img className="openedx-logo" alt="Powered by Open edX" src="https://openedx.org/wp-content/uploads/2025/07/Powered-by-Open-edX-WHITE.png" />
17
+ </div>
18
+ </div>
19
+ <hr />
20
+ <div className="text-center d-block d-md-none my-4">
21
+ <img className="openedx-logo" alt="Powered by Open edX" src="https://openedx.org/wp-content/uploads/2025/07/Powered-by-Open-edX-WHITE.png" />
22
+ </div>
23
+ <div className="text-center text-md-left"><small>&copy; OpenCraft 2025 | edX and Open edX&reg; are trademarks of edX Inc</small></div>
24
+ </footer>
25
+ );
@@ -0,0 +1,16 @@
1
+ import React, { useContext } from 'react';
2
+ import { AppContext } from '@edx/frontend-platform/react';
3
+ import { ensureConfig } from '@edx/frontend-platform';
4
+
5
+ ensureConfig([
6
+ 'LOGO_WHITE_URL',
7
+ ], 'White Header Logo');
8
+
9
+ export const WhiteLogo = () => {
10
+ const { config } = useContext<AppContextInterface>(AppContext);
11
+ return (
12
+ <a className="logo" href={`${config.LMS_BASE_URL}/dashboard`}>
13
+ <img className="d-block" src={config.LOGO_WHITE_URL} alt={config.SITE_NAME} />
14
+ </a>
15
+ );
16
+ };
package/src/index.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
2
+ import { WhiteLogo } from './components/WhiteLogo';
3
+ import { MenuWrapper } from './components/MenuWrapper';
4
+ import { SandboxFooter } from './components/SandboxFooter';
5
+ import { DropDownToggler } from './components/DropDownToggler';
6
+ import { CatalogBanner } from './components/CatalogBanner';
7
+ export {
8
+ WhiteLogo, MenuWrapper, SandboxFooter, DropDownToggler, CatalogBanner
9
+ };
10
+ // Example slot settings you can use in your deployment. You may be able to import these
11
+ // for quick development usage.
12
+ export const slotSettings = {
13
+ logo_slot: {
14
+ keepDefault: false,
15
+ plugins: [
16
+ {
17
+ op: PLUGIN_OPERATIONS.Insert,
18
+ widget: {
19
+ id: 'custom_logo',
20
+ type: DIRECT_PLUGIN,
21
+ RenderWidget: WhiteLogo,
22
+ },
23
+ },
24
+ ],
25
+ },
26
+ 'org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1': {
27
+ keepDefault: false,
28
+ plugins: [
29
+ {
30
+ op: PLUGIN_OPERATIONS.Insert,
31
+ widget: {
32
+ id: 'custom_desktop_toggle',
33
+ type: DIRECT_PLUGIN,
34
+ RenderWidget: DropDownToggler,
35
+ },
36
+ },
37
+ ],
38
+ },
39
+ desktop_user_menu_slot: {
40
+ keepDefault: true,
41
+ plugins: [
42
+ {
43
+ op: PLUGIN_OPERATIONS.Wrap,
44
+ widgetId: 'default_contents',
45
+ wrapper: MenuWrapper,
46
+ },
47
+ ],
48
+ },
49
+ mobile_user_menu_slot: {
50
+ keepDefault: true,
51
+ plugins: [
52
+ {
53
+ op: PLUGIN_OPERATIONS.Wrap,
54
+ widgetId: 'default_contents',
55
+ wrapper: MenuWrapper,
56
+ },
57
+ ],
58
+ },
59
+ footer_slot: {
60
+ plugins: [
61
+ {
62
+ op: PLUGIN_OPERATIONS.Hide,
63
+ widgetId: 'default_contents',
64
+ },
65
+ {
66
+ op: PLUGIN_OPERATIONS.Insert,
67
+ widget: {
68
+ id: 'custom_footer',
69
+ type: DIRECT_PLUGIN,
70
+ RenderWidget: SandboxFooter,
71
+ },
72
+ },
73
+ ],
74
+ },
75
+ "org.openedx.frontend.catalog.home_page.banner": {
76
+ plugins: [
77
+ {
78
+ op: PLUGIN_OPERATIONS.Hide,
79
+ widgetId: 'default_contents',
80
+ },
81
+ {
82
+ op: PLUGIN_OPERATIONS.Insert,
83
+ widget: {
84
+ id: 'custom_catalog_banner',
85
+ type: DIRECT_PLUGIN,
86
+ RenderWidget: CatalogBanner,
87
+ },
88
+ },
89
+ ],
90
+ }
91
+ };
package/src/types.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export interface AppContextInterface {
2
+ config: {
3
+ LMS_BASE_URL: string,
4
+ LOGO_WHITE_URL: string,
5
+ SITE_NAME: string,
6
+ },
7
+ authenticatedUser: {
8
+ username: string,
9
+ }
10
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "@edx/typescript-config",
3
+ "compilerOptions": {
4
+ "jsx": "react",
5
+ "outDir": "dist",
6
+ "baseUrl": "./src",
7
+ "paths": {
8
+ "*": ["*"]
9
+ }
10
+ },
11
+ "include": ["*.js", ".eslintrc.js", "src/**/*", ],
12
+ "exclude": ["dist", "node_modules"]
13
+ }