diff --git a/package.json b/package.json
index 3c6a61133..3d31e16cb 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"build": "fedx-scripts webpack",
"i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
+ "lint:fix": "npm run lint -- --fix",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests"
diff --git a/src/index.jsx b/src/index.jsx
index cef8bc4d8..520486ea4 100755
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -15,7 +15,7 @@ import FooterSlot from '@openedx/frontend-slot-footer';
import configureStore from './data/configureStore';
import AccountSettingsPage, { NotFoundPage } from './account-settings';
-import IdVerificationPage from './id-verification';
+import IdVerificationPageSlot from './plugin-slots/IdVerificationPageSlot';
import messages from './i18n';
import './index.scss';
@@ -40,7 +40,10 @@ subscribe(APP_READY, () => {
>
} />
} />
- } />
+ }
+ />
} />
} />
} />
diff --git a/src/plugin-slots/IdVerificationPageSlot/README.md b/src/plugin-slots/IdVerificationPageSlot/README.md
new file mode 100644
index 000000000..94400bd30
--- /dev/null
+++ b/src/plugin-slots/IdVerificationPageSlot/README.md
@@ -0,0 +1,45 @@
+# Footer Slot
+
+### Slot ID: `id_verification_page_plugin`
+
+## Description
+
+This slot is used to replace/modify the IDV Page.
+
+The implementation of the `IdVerificationPageSlot` component lives in `src/plugin-slots/IdVerificationPageSlot/index.jsx`.
+
+## Example
+
+The following `env.config.jsx` will replace the default IDV Page.
+
+![Screenshot of Default IDV Page](./images/default_id-verification-page.png)
+
+```jsx
+import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
+
+const config = {
+ pluginSlots: {
+ id_verification_page_plugin: {
+ plugins: [
+ {
+ // Insert a custom IDV Page
+ op: PLUGIN_OPERATIONS.Insert,
+ widget: {
+ id: 'id_verification_page_plugin',
+ type: DIRECT_PLUGIN,
+ RenderWidget: () => (
+
+
This is the new IDV page
+
Go Home
+
+ ),
+ },
+ },
+ ],
+ },
+ },
+};
+
+export default config;
+
+```
diff --git a/src/plugin-slots/IdVerificationPageSlot/images/custom_id-verification-page.png b/src/plugin-slots/IdVerificationPageSlot/images/custom_id-verification-page.png
new file mode 100644
index 000000000..a3d8d00ea
Binary files /dev/null and b/src/plugin-slots/IdVerificationPageSlot/images/custom_id-verification-page.png differ
diff --git a/src/plugin-slots/IdVerificationPageSlot/images/default_id-verification-page.png b/src/plugin-slots/IdVerificationPageSlot/images/default_id-verification-page.png
new file mode 100644
index 000000000..8741fe7ab
Binary files /dev/null and b/src/plugin-slots/IdVerificationPageSlot/images/default_id-verification-page.png differ
diff --git a/src/plugin-slots/IdVerificationPageSlot/index.jsx b/src/plugin-slots/IdVerificationPageSlot/index.jsx
new file mode 100644
index 000000000..d207e8e09
--- /dev/null
+++ b/src/plugin-slots/IdVerificationPageSlot/index.jsx
@@ -0,0 +1,10 @@
+import { PluginSlot } from '@openedx/frontend-plugin-framework';
+import IdVerificationPage from '../../id-verification';
+
+const IdVerificationPageSlot = () => (
+
+
+
+);
+
+export default IdVerificationPageSlot;
diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md
index a38ecda48..1b7f8c2df 100644
--- a/src/plugin-slots/README.md
+++ b/src/plugin-slots/README.md
@@ -1,3 +1,4 @@
# `frontend-app-account` Plugin Slots
* [`footer_slot`](./FooterSlot/)
+* [`id_verification_page_plugin`](./IdVerificationPageSlot/)
diff --git a/src/setupTest.js b/src/setupTest.js
index 95b3726d1..e2e601f96 100755
--- a/src/setupTest.js
+++ b/src/setupTest.js
@@ -1,3 +1,11 @@
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import '@testing-library/jest-dom';
+
+import MockedPluginSlot from './tests/MockedPluginSlot';
+
+jest.mock('@openedx/frontend-plugin-framework', () => ({
+ ...jest.requireActual('@openedx/frontend-plugin-framework'),
+ Plugin: () => 'Plugin',
+ PluginSlot: MockedPluginSlot,
+}));
diff --git a/src/tests/MockedPluginSlot.jsx b/src/tests/MockedPluginSlot.jsx
new file mode 100644
index 000000000..e86952ee0
--- /dev/null
+++ b/src/tests/MockedPluginSlot.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const MockedPluginSlot = ({ children, id }) => (
+
+ PluginSlot_{id}
+ { children &&
{children}
}
+
+);
+
+MockedPluginSlot.displayName = 'PluginSlot';
+
+MockedPluginSlot.propTypes = {
+ children: PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.node),
+ PropTypes.node,
+ ]),
+ id: PropTypes.string,
+};
+
+MockedPluginSlot.defaultProps = {
+ children: undefined,
+ id: undefined,
+};
+
+export default MockedPluginSlot;
diff --git a/src/tests/MockedPluginSlot.test.jsx b/src/tests/MockedPluginSlot.test.jsx
new file mode 100644
index 000000000..b830b68fb
--- /dev/null
+++ b/src/tests/MockedPluginSlot.test.jsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import MockedPluginSlot from './MockedPluginSlot';
+
+describe('MockedPluginSlot', () => {
+ it('renders mock plugin with "PluginSlot" text', () => {
+ render();
+
+ const component = screen.getByText('PluginSlot_test_plugin');
+ expect(component).toBeInTheDocument();
+ });
+
+ it('renders as the slot children directly if there is content within', () => {
+ render(
+
+
+ How much wood could a woodchuck chuck if a woodchuck could chuck wood?
+
+
,
+ );
+
+ const component = screen.getByRole('article');
+ expect(component).toBeInTheDocument();
+
+ // Direct children
+ const quote = component.querySelector(':scope > q');
+ expect(quote.getAttribute('role')).toBe('note');
+ });
+
+ it('renders mock plugin with a data-testid ', () => {
+ render(
+
+ I am selling these fine leather jackets.
+ ,
+ );
+
+ const component = screen.getByTestId('guybrush');
+ expect(component).toBeInTheDocument();
+
+ const quote = component.querySelector('[role=note]');
+ expect(quote).toBeInTheDocument();
+ });
+});