-
Notifications
You must be signed in to change notification settings - Fork 172
feat: Add HTML and Problem editors plugin slots #2749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
2df1ba6
e3745e9
8d74dd7
9271857
4b782e7
1ea6e4c
a31aa94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| # ProblemEditorPluginSlot | ||
|
|
||
| ### Slot ID: `org.openedx.frontend.authoring.problem_editor_plugin.v1` | ||
|
|
||
| ### Slot ID Aliases | ||
| * `problem_editor_plugin_slot` | ||
|
|
||
| ### Plugin Props: | ||
|
|
||
| * `blockType` - String. The type of problem block being edited (e.g., `problem-single-select`, `problem-multi-select`, `problem`, `advanced`). | ||
|
|
||
| ## Description | ||
|
|
||
| The `ProblemEditorPluginSlot` is rendered inside the Problem Editor modal window for all major | ||
| problem XBlock types: | ||
|
|
||
| - single-select | ||
| - multi-select | ||
| - dropdown | ||
| - numerical-input | ||
| - text-input | ||
|
|
||
| It is a **generic extension point** that can host any React component, such as: | ||
|
|
||
| - **Problem authoring helpers** (validation, hints, accessibility tips) | ||
| - **Preview or analysis tools** (show how a problem will render, check grading logic) | ||
| - **Integrations** (external content sources, tagging, metadata editors) | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be screenshots after the example title?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ihor-romaniuk tell me to remove all screenshots with our
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we don’t want to mention anything about the AI Assistant, should we update the other description as well? |
||
| By default, the slot is **empty**. Widgets are attached via `env.config.jsx` using the | ||
| `@openedx/frontend-plugin-framework`. | ||
|
Comment on lines
+29
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interaction with the slot is not limited to |
||
|
|
||
| The only prop your component receives from the slot is: | ||
|
|
||
| - `blockType` – the current problem block type. | ||
|
Comment on lines
+32
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [clarify]: Given that there is already a “Plugin Props” section, do we need to explicitly list the plugin props for this plugin slot again? |
||
|
|
||
| Your component is responsible for interacting with the editor state (if needed) using | ||
| Redux, `window.tinymce`, CodeMirror, or other utilities provided by `frontend-app-authoring`. | ||
|
|
||
| The slot is available in the **visual editor** mode. Advanced / raw editing is handled | ||
| by the raw editor, and your component can still interact with the underlying state | ||
| if it chooses to. | ||
|
Comment on lines
+36
to
+41
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me, this looks like unnecessary information that can be removed |
||
|
|
||
| ## Example: Adding a component into `ProblemEditorPluginSlot` | ||
|
|
||
| The following example configuration shows how to add a custom widget to the slot: | ||
|
|
||
| ```jsx | ||
| import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; | ||
| import { MyProblemEditorHelper } from '@example/my-problem-editor-helper'; | ||
|
|
||
| const config = { | ||
| pluginSlots: { | ||
| 'org.openedx.frontend.authoring.problem_editor_plugin.v1': { | ||
| plugins: [ | ||
| { | ||
| op: PLUGIN_OPERATIONS.Insert, | ||
| widget: { | ||
| id: 'my-problem-editor-helper', | ||
| type: DIRECT_PLUGIN, | ||
| priority: 1, | ||
| RenderWidget: MyProblemEditorHelper, | ||
| }, | ||
| }, | ||
| ] | ||
| } | ||
| }, | ||
| } | ||
|
|
||
| export default config; | ||
|
Comment on lines
+47
to
+69
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we requiring users to create import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
'org.openedx.frontend.authoring.problem_editor_plugin.v1': {
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'my-problem-editor-helper',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1 style={{textAlign: 'center'}}>🦶</h1>
),
},
},
]
}
},
}
export default config;A complete example might look like this: |
||
| ``` | ||
|
|
||
| ## Example: Custom Implementation | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this example, it seems to me that the Paragon Card component is not the best choice for this. Let’s do it the following way instead: |
||
|
|
||
| The following example shows a minimal helper component that uses `blockType`: | ||
|
|
||
| ```jsx | ||
| import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; | ||
| import { Card } from '@openedx/paragon'; | ||
|
|
||
| const CustomProblemAssistant = ({ blockType }) => { | ||
| // Your custom implementation (example) | ||
| return ( | ||
| <Card> | ||
| <Card.Body> | ||
| Custom component for {blockType} problem editor 🤗🤗🤗 | ||
| </Card.Body> | ||
| </Card> | ||
| ); | ||
| }; | ||
|
|
||
| const config = { | ||
| pluginSlots: { | ||
| 'org.openedx.frontend.authoring.problem_editor_plugin.v1': { | ||
| plugins: [ | ||
| { | ||
| op: PLUGIN_OPERATIONS.Insert, | ||
| widget: { | ||
| id: 'custom-problem-editor-assistant', | ||
| priority: 1, | ||
| type: DIRECT_PLUGIN, | ||
| RenderWidget: CustomProblemAssistant, | ||
| }, | ||
| op: PLUGIN_OPERATIONS.Insert, | ||
| }, | ||
| ] | ||
| } | ||
| }, | ||
| } | ||
|
|
||
| export default config; | ||
| ``` | ||
|
|
||
| ### Example: Screenshots | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe I already mentioned that the images should be placed under the heading/description of the plugin slot use cases. Let’s remove this. |
||
|
|
||
| **Default problem editor without a widget** | ||
|
|
||
|  | ||
|
|
||
| **With a widget rendered in the slot** | ||
|
|
||
|  | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, after the refactoring, the documentation for this plugin slot could look like the following |
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,18 @@ | ||||||
| import { PluginSlot } from '@openedx/frontend-plugin-framework/dist'; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| interface ProblemEditorPluginSlotProps { | ||||||
| blockType: string | null; | ||||||
| } | ||||||
|
|
||||||
| export const ProblemEditorPluginSlot = ({ | ||||||
| blockType, | ||||||
| }: ProblemEditorPluginSlotProps) => ( | ||||||
| <PluginSlot | ||||||
| id="org.openedx.frontend.authoring.problem_editor_plugin.v1" | ||||||
| idAliases={['problem_editor_plugin_slot']} | ||||||
| pluginProps={{ | ||||||
| blockType, | ||||||
| }} | ||||||
| /> | ||||||
| ); | ||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Too many blank lines at the end of file. Max of 0 allowed |
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # TextEditorPluginSlot | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please update the documentation for this plugin slot as well, following the same approach I used for ProblemEditorPluginSlot. |
||
|
|
||
| ### Slot ID: `org.openedx.frontend.authoring.text_editor_plugin.v1` | ||
|
|
||
| ### Slot ID Aliases | ||
| * `text_editor_plugin_slot` | ||
|
|
||
| ### Plugin Props: | ||
|
|
||
| * `blockType` - String. The type of block being edited (e.g., `html`). | ||
|
|
||
| ## Description | ||
|
|
||
| The `TextEditorPluginSlot` is rendered inside the Text Editor modal window for HTML XBlocks. | ||
| It is intended as a generic extension point that can host **any React component** – for example: | ||
|
|
||
| - **Contextual helpers** (tips, validation messages, writing guides) | ||
| - **Content utilities** (templates, reusable snippets, glossary insert tools) | ||
| - **Integrations** (linking to external systems, analytics, metadata editors) | ||
|
|
||
| By default, the slot is **empty**. Widgets are attached via `env.config.jsx` using the | ||
| `@openedx/frontend-plugin-framework`. | ||
|
|
||
| The only prop your component receives from the slot is: | ||
|
|
||
| - `blockType` – the current editor block type (for this slot it will typically be `html`). | ||
|
|
||
| Your component is responsible for interacting with the editor (if needed) using Redux state, | ||
| DOM APIs, or other utilities provided by `frontend-app-authoring`. | ||
|
|
||
| ## Example: Adding a component into `TextEditorPluginSlot` | ||
|
|
||
| The following example configuration shows how to add a custom widget to the slot: | ||
|
|
||
| ```jsx | ||
| import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; | ||
| import { MyTextEditorHelper } from '@example/my-text-editor-helper'; | ||
|
|
||
| const config = { | ||
| pluginSlots: { | ||
| 'org.openedx.frontend.authoring.text_editor_plugin.v1': { | ||
| plugins: [ | ||
| { | ||
| op: PLUGIN_OPERATIONS.Insert, | ||
| widget: { | ||
| id: 'my-text-editor-helper', | ||
| type: DIRECT_PLUGIN, | ||
| priority: 1, | ||
| RenderWidget: MyTextEditorHelper, | ||
| }, | ||
| }, | ||
| ] | ||
| } | ||
| }, | ||
| } | ||
|
|
||
| export default config; | ||
| ``` | ||
|
|
||
| ## Example: Custom Implementation | ||
|
|
||
| The following example shows a minimal helper component that uses `blockType`: | ||
|
|
||
| ```jsx | ||
| import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; | ||
| import { Card } from '@openedx/paragon'; | ||
|
|
||
| const CustomTextEditorWidget = ({ blockType }) => { | ||
| // Your custom implementation (example) | ||
| return ( | ||
| <Card> | ||
| <Card.Body> | ||
| Custom widget for {blockType} editor 🤗🤗🤗 | ||
| </Card.Body> | ||
| </Card> | ||
| ); | ||
| }; | ||
|
|
||
| const config = { | ||
| pluginSlots: { | ||
| 'org.openedx.frontend.authoring.text_editor_plugin.v1': { | ||
| plugins: [ | ||
| { | ||
| widget: { | ||
| id: 'custom-text-editor-widget', | ||
| priority: 1, | ||
| type: DIRECT_PLUGIN, | ||
| RenderWidget: CustomTextEditorWidget, | ||
| }, | ||
| op: PLUGIN_OPERATIONS.Insert, | ||
| }, | ||
| ] | ||
| } | ||
| }, | ||
| } | ||
|
|
||
| export default config; | ||
| ``` | ||
|
|
||
| ### Example: Screenshots | ||
|
|
||
| **With a widget rendered in the slot** | ||
|
|
||
|  | ||
|
|
||
| **Default HTML editor without a widget** | ||
|
|
||
|  | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | ||||||
| import { PluginSlot } from '@openedx/frontend-plugin-framework/dist'; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| interface TextEditorPluginSlotProps { | ||||||
| blockType: string; | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this extra space?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More than 1 blank line not allowed |
||||||
| export const TextEditorPluginSlot = ({ | ||||||
| blockType, | ||||||
| }: TextEditorPluginSlotProps) => ( | ||||||
| <PluginSlot | ||||||
| id="org.openedx.frontend.authoring.text_editor_plugin.v1" | ||||||
| idAliases={['text_editor_plugin_slot']} | ||||||
| pluginProps={{ | ||||||
| blockType, | ||||||
| }} | ||||||
| /> | ||||||
| ); | ||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Too many blank lines at the end of file. Max of 0 allowed |
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md004.md