Skip to content

Commit 31d16bb

Browse files
authored
refactor: revert default content to live in host MFE (#41)
* feat: convert default content to be defined in PluginSlot * docs: update README to reflect use of default content * fix: add error handling for if pluginSlots does not exist in config
1 parent b628cca commit 31d16bb

17 files changed

+293
-202
lines changed

README.rst

Lines changed: 24 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,12 @@ data to the plugin as part of its contract.
7676
style={{
7777
height: 700,
7878
}}
79-
/>
79+
>
80+
<SideBar
81+
propExampleA: 'edX Sidebar',
82+
propExampleB: SomeIcon,
83+
>
84+
</PluginSlot >
8085
</Route>
8186
<Route path="/page2">
8287
<OtherRouteContent />
@@ -110,18 +115,7 @@ file as well to define its plugin slots.
110115
// additional environment variables
111116
pluginSlots: {
112117
sidebar: { // plugin slot id
113-
defaultContents: [
114-
{
115-
id: 'default_sidebar_widget',
116-
type: DIRECT_PLUGIN,
117-
priority: 10,
118-
RenderWidget: SideBar,
119-
content: {
120-
propExampleA: 'edX Sidebar',
121-
propExampleB: SomeIcon,
122-
},
123-
},
124-
],
118+
keepDefault: true,
125119
plugins: [
126120
{
127121
op: PLUGIN_OPERATIONS.Insert,
@@ -134,12 +128,12 @@ file as well to define its plugin slots.
134128
},
135129
{
136130
op: PLUGIN_OPERATIONS.Wrap,
137-
widgetId: 'default_content_in_slot',
131+
widgetId: 'default_contents',
138132
wrapper: wrapWidget,
139133
},
140134
{
141135
op: PLUGIN_OPERATIONS.Modify,
142-
widgetId: 'default_content_in_slot',
136+
widgetId: 'social_media_link',
143137
fn: modifyWidget,
144138
},
145139
]
@@ -159,51 +153,23 @@ For more information on how JS based configuration works, see:
159153
.. _JavaScript-based environment configuration: https://github.com/openedx/frontend-platform/blob/master/docs/decisions/0007-javascript-file-configuration.rst
160154
.. _Promote JavaScript file configuration and deprecate environment variable configuration: https://github.com/openedx/frontend-platform/blob/master/docs/decisions/0007-javascript-file-configuration.rst
161155

156+
Priority
157+
````````
158+
159+
The priority property determines where the widgets should be placed based on a 1-100 scale. A widget with a priority of 10
160+
will appear above a widget with a priority of 20.
161+
162162
Default Content
163163
```````````````
164164

165-
The default content of a plugin slot is defined in ``env.config.js``, with some differing properties being needed for
166-
a Direct Plugin over an iFrame plugin. Note: this configuration will change soon so that the default content lives in
167-
the Host MFE as opposed to a config document.
165+
The component that is wrapped by a Plugin Slot is referred to as the "default content". In order to render this content,
166+
the ``keepDefault`` boolean in the slot should be set to ``true``. For organizations who aren't using the Plugin Slot
167+
(and therefore aren't defining a slot via JS config), ``keepDefault`` will default to ``true``, thus ensuring that the developer
168+
experience isn't affected; the only change to note is that the component is now wrapped in a Plugin Slot.
168169

169-
.. code-block::
170+
If you need to use a plugin operation (e.g. Wrap, Hide, Modify) on default content, the ``widgetId`` you would use to refer to the content is ``defaults_contents``.
170171

171-
/*
172-
* {String} id - The widget id needed for referencing when using Modify/Wrap/Hide
173-
* {String} type - The type of plugin being used
174-
* {Number} priority - The place to insert the widget based on the priority of other widgets (between 1 - 100)
175-
* {Function} RenderWidget - The React component to be used for a Direct Plugin
176-
* {Object} [content] - Any props to pass into the RenderWidget component
177-
* {String} url - The URL from a Child MFE to fetch the widget component
178-
* {String} title - The title of the iFrame that is read aloud with screen readers
179-
*/
180-
181-
defaultContents: [
182-
{
183-
id: 'default_sidebar_widget',
184-
type: DIRECT_PLUGIN,
185-
priority: 10,
186-
RenderWidget: SideBar,
187-
content: {
188-
propExampleA: 'Open edX Sidebar',
189-
propExampleB: SomeIcon,
190-
}
191-
},
192-
{
193-
id: 'iFrame_widget',
194-
type: IFRAME_PLUGIN,
195-
priority: 15,
196-
url: 'http://{child_mfe_url}/plugin_iframe',
197-
title: 'Login with XYZ',
198-
}
199-
]
200-
201-
Priority
202-
````````
203-
204-
The priority property determines where the widgets should be placed based on a 1-100 scale. A widget with a priority of 10
205-
will appear above a widget with a priority of 20. The default content will have a priority of 50, allowing for any plugins
206-
to appear before or after the default content.
172+
Note: The default content will have a priority of 50, allowing for any plugins to appear before or after the default content.
207173

208174
Plugin Operations
209175
`````````````````
@@ -282,8 +248,8 @@ or its priority. The operation requires the id of the widget that will be modifi
282248
*/
283249
284250
{
285-
op: PLUGIN_OPERATIONS.Insert,
286-
widgetId: 'default_content_in_slot',
251+
op: PLUGIN_OPERATIONS.Modify,
252+
widgetId: 'sidebar_plugin',
287253
fn: modifyWidget,
288254
}
289255
@@ -329,7 +295,7 @@ The Hide operation will simply hide whatever content is desired. This is general
329295
330296
{
331297
op: PLUGIN_OPERATIONS.Hide,
332-
widgetId: 'default_content_in_slot',
298+
widgetId: 'some_undesired_plugin',
333299
}
334300
335301
Using a Child Micro-frontend (MFE) for iFrame-based Plugins and Fallback Behavior

example/env.config.jsx

Lines changed: 76 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import {
44
IFRAME_PLUGIN,
55
PLUGIN_OPERATIONS,
66
} from '@edx/frontend-plugin-framework';
7-
import DefaultDirectWidget from './src/directPlugins/DefaultDirectWidget';
8-
import PluginDirect from './src/directPlugins/PluginDirect';
9-
import ModularDirectPlugin from './src/directPlugins/ModularDirectPlugin';
7+
import DefaultDirectWidget from './src/components/DefaultDirectWidget';
8+
import PluginDirect from './src/components/PluginDirect';
9+
import ModularComponent from './src/components/ModularComponent';
1010

1111
const modifyWidget = (widget) => {
1212
const newContent = {
13-
title: 'Modified Default Widget',
14-
uniqueText: 'Note that the default text is replaced by this one that is defined in the JS configuration.',
13+
title: 'Modified Modular Plugin',
14+
uniqueText: 'Note that the original text defined in the JS config is replaced by this modified one.',
1515
};
1616
const modifiedWidget = widget;
1717
modifiedWidget.content = newContent;
@@ -20,9 +20,10 @@ const modifyWidget = (widget) => {
2020

2121
const wrapWidget = ({ component, idx }) => (
2222
<div className="bg-warning" data-testid={`wrapper${idx + 1}`} key={idx}>
23-
<p>This is a wrapper component that is placed around the widget.</p>
23+
<p>This is a wrapper component that is placed around the default content.</p>
2424
{component}
25-
<p>With this wrapper, you can add anything before or after the widget.</p>
25+
<p>With this wrapper, you can add anything before or after a component.</p>
26+
<p>Note in the JS config that an iFrame plugin was Inserted, but a Hide operation was also used to hide it!</p>
2627
</div>
2728
);
2829

@@ -59,22 +60,17 @@ const config = {
5960
PORT: 8080,
6061
pluginSlots: {
6162
slot_with_insert_operation: {
62-
defaultContents: [
63-
{
64-
id: 'default_iframe_widget',
65-
type: IFRAME_PLUGIN,
66-
priority: 1,
67-
url: 'http://localhost:8081/default_iframe',
68-
title: 'The default iFrame widget that appears in the plugin slot',
69-
},
63+
keepDefault: true,
64+
plugins: [
7065
{
71-
id: 'default_direct_widget',
72-
type: DIRECT_PLUGIN,
73-
priority: 20,
74-
RenderWidget: DefaultDirectWidget,
66+
op: PLUGIN_OPERATIONS.Insert,
67+
widget: {
68+
id: 'inserted_direct_plugin',
69+
type: DIRECT_PLUGIN,
70+
priority: 10,
71+
RenderWidget: PluginDirect,
72+
},
7573
},
76-
],
77-
plugins: [
7874
{
7975
op: PLUGIN_OPERATIONS.Insert,
8076
widget: {
@@ -85,83 +81,105 @@ const config = {
8581
title: 'The iFrame plugin that is inserted in the slot',
8682
},
8783
},
84+
],
85+
},
86+
slot_with_modify_wrap_hidden_operations: {
87+
keepDefault: true,
88+
plugins: [
8889
{
8990
op: PLUGIN_OPERATIONS.Insert,
9091
widget: {
91-
id: 'inserted_direct_plugin',
92+
id: 'inserted_plugin',
9293
type: DIRECT_PLUGIN,
9394
priority: 10,
94-
RenderWidget: PluginDirect,
95+
RenderWidget: ModularComponent,
96+
content: {
97+
title: 'Modular Direct Plugin',
98+
uniqueText: 'This is some text that will be replaced by the Modify operation below.',
99+
},
95100
},
96101
},
97-
],
98-
},
99-
slot_with_modify_wrap_hidden_operations: {
100-
defaultContents: [
101102
{
102-
id: 'default_direct_widget',
103-
type: DIRECT_PLUGIN,
104-
priority: 1,
105-
RenderWidget: ModularDirectPlugin,
106-
content: {
107-
title: 'Default Direct Widget',
108-
uniqueText: "This widget's content will be modified by the Modify operation below.",
103+
op: PLUGIN_OPERATIONS.Insert,
104+
widget: {
105+
id: 'inserted_iframe_plugin',
106+
type: IFRAME_PLUGIN,
107+
priority: 30,
108+
url: 'http://localhost:8081/plugin_iframe',
109+
title: 'This iFrame plugin will be hidden due to the Hide operation in this config.',
109110
},
110111
},
111-
{
112-
id: 'default_iframe_widget',
113-
type: IFRAME_PLUGIN,
114-
priority: 2,
115-
url: 'http://localhost:8081/default_iframe',
116-
title: 'The default iFrame widget that appears in the plugin slot',
117-
},
118-
],
119-
plugins: [
120112
{
121113
op: PLUGIN_OPERATIONS.Wrap,
122-
widgetId: 'default_direct_widget',
114+
widgetId: 'default_contents',
123115
wrapper: wrapWidget,
124116
},
125117
{
126118
op: PLUGIN_OPERATIONS.Modify,
127-
widgetId: 'default_direct_widget',
119+
widgetId: 'inserted_plugin',
128120
fn: modifyWidget,
129121
},
130122
{
131123
op: PLUGIN_OPERATIONS.Hide,
132-
widgetId: 'default_iframe_widget',
124+
widgetId: "inserted_iframe_plugin",
133125
},
134126
],
135127
},
136128
slot_with_modular_plugins: {
137-
defaultContents: [
129+
keepDefault: true,
130+
plugins: [
138131
{
139-
id: 'default_direct_widget',
140-
type: DIRECT_PLUGIN,
141-
priority: 10,
142-
RenderWidget: ModularDirectPlugin,
143-
content: {
144-
title: 'Default Direct Widget',
145-
uniqueText: 'This is a direct widget with priority of 10, which is why it appears second in this slot.',
132+
op: PLUGIN_OPERATIONS.Insert,
133+
widget: {
134+
id: 'inserted_direct_plugin',
135+
type: DIRECT_PLUGIN,
136+
priority: 1,
137+
RenderWidget: ModularComponent,
138+
content: {
139+
title: 'Modular Direct Plugin',
140+
uniqueText: 'This is a direct plugin with priority of 1, which is why it appears first in this slot.',
141+
},
146142
},
147143
},
148144
],
145+
},
146+
slot_without_default: {
147+
keepDefault: false,
149148
plugins: [
150149
{
151150
op: PLUGIN_OPERATIONS.Insert,
152151
widget: {
153152
id: 'inserted_direct_plugin',
154153
type: DIRECT_PLUGIN,
155154
priority: 1,
156-
RenderWidget: ModularDirectPlugin,
155+
RenderWidget: ModularComponent,
157156
content: {
158-
title: 'Inserted Direct Plugin',
159-
uniqueText: 'This is a direct plugin with priority of 1, which is why it appears first in this slot.',
157+
title: 'Modular Direct Plugin With Content Defined in JS Config',
158+
uniqueText: 'This modular component receives some of its content from the JS config (such as this text).',
160159
},
161160
},
162161
},
162+
{
163+
op: PLUGIN_OPERATIONS.Insert,
164+
widget: {
165+
id: 'inserted_direct_plugin',
166+
type: DIRECT_PLUGIN,
167+
priority: 10,
168+
RenderWidget: PluginDirect,
169+
},
170+
},
171+
{
172+
op: PLUGIN_OPERATIONS.Insert,
173+
widget: {
174+
id: 'inserted_iframe_plugin',
175+
type: IFRAME_PLUGIN,
176+
priority: 30,
177+
url: 'http://localhost:8081/plugin_iframe',
178+
title: 'The iFrame plugin that is inserted in the slot',
179+
},
180+
},
163181
],
164-
},
182+
}
165183
},
166184
};
167185

example/src/ExamplePage.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react';
33
import PluginSlotWithInsert from './pluginSlots/PluginSlotWithInsert';
44
import PluginSlotWithModifyWrapHide from './pluginSlots/PluginSlotWithModifyWrapHide';
55
import PluginSlotWithModularPlugins from './pluginSlots/PluginSlotWithModularPlugins';
6+
import PluginSlotWithoutDefault from './pluginSlots/PluginSlotWithoutDefault';
67

78
export default function ExamplePage() {
89
return (
@@ -23,6 +24,7 @@ export default function ExamplePage() {
2324
<PluginSlotWithInsert />
2425
<PluginSlotWithModifyWrapHide />
2526
<PluginSlotWithModularPlugins />
27+
<PluginSlotWithoutDefault />
2628
</div>
2729
</main>
2830
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
export default function ModularComponent({ content }) {
5+
return (
6+
<section className="bg-light p-3">
7+
<h3>{ content.title }</h3>
8+
<p>
9+
This is a modular component that lives in the example app.
10+
</p>
11+
<p>
12+
<em>{content.uniqueText}</em>
13+
</p>
14+
</section>
15+
);
16+
}
17+
18+
ModularComponent.propTypes = {
19+
content: PropTypes.shape({
20+
title: PropTypes.string,
21+
uniqueText: PropTypes.string,
22+
}),
23+
};
24+
25+
ModularComponent.defaultProps = {
26+
content: {},
27+
};

0 commit comments

Comments
 (0)