+
+
+ Confirm Now
+ ,
+ }
+ }
+ />
+
+
+ I've confirmed my email
+
+ }
+ hasCloseButton={false}
+ heroNode={
+
+
+
+ }
+ isOpen={[MockFunction showConfirmModal]}
+ onClose={[MockFunction closeConfirmModal]}
+ title=""
+ >
+
+ Confirm your email
+
+
+ We've sent you an email to verify your acccount. Please check your inbox and click on the big red button to confirm and keep learning.
+
+
+
+`;
+
+exports[`ConfirmEmailBanner snapshot do not show on already verified 1`] = `""`;
diff --git a/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.js b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.js
new file mode 100644
index 0000000..fc8dc5d
--- /dev/null
+++ b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.js
@@ -0,0 +1,44 @@
+import React from 'react';
+
+import { StrictDict } from 'utils';
+import { hooks as appHooks, thunkActions } from 'data/redux';
+
+import { useDispatch } from 'react-redux';
+
+import * as module from './hooks';
+
+export const state = StrictDict({
+ showPageBanner: (val) => React.useState(val), // eslint-disable-line
+ showConfirmModal: (val) => React.useState(val), // eslint-disable-line
+});
+
+export const useConfirmEmailBannerData = () => {
+ const dispatch = useDispatch();
+ const { isNeeded } = appHooks.useEmailConfirmationData();
+ const [showPageBanner, setShowPageBanner] = module.state.showPageBanner(isNeeded);
+ const [showConfirmModal, setShowConfirmModal] = module.state.showConfirmModal(false);
+ const closePageBanner = () => setShowPageBanner(false);
+ const closeConfirmModal = () => setShowConfirmModal(false);
+ const openConfirmModal = () => setShowConfirmModal(true);
+
+ const openConfirmModalButtonClick = () => {
+ dispatch(thunkActions.app.sendConfirmEmail());
+ openConfirmModal();
+ };
+
+ const userConfirmEmailButtonClick = () => {
+ closeConfirmModal();
+ closePageBanner();
+ };
+ return {
+ isNeeded,
+ showPageBanner,
+ closePageBanner,
+ showConfirmModal,
+ closeConfirmModal,
+ openConfirmModalButtonClick,
+ userConfirmEmailButtonClick,
+ };
+};
+
+export default useConfirmEmailBannerData;
diff --git a/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.test.js b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.test.js
new file mode 100644
index 0000000..50e1047
--- /dev/null
+++ b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.test.js
@@ -0,0 +1,76 @@
+import { MockUseState } from 'testUtils';
+import { hooks as appHooks, thunkActions } from 'data/redux';
+
+import * as hooks from './hooks';
+
+jest.mock('data/redux', () => ({
+ hooks: {
+ useEmailConfirmationData: jest.fn(),
+ },
+ thunkActions: {
+ app: {
+ sendConfirmEmail: jest.fn(),
+ },
+ },
+}));
+
+const emailConfirmation = {
+ isNeeded: true,
+};
+
+const state = new MockUseState(hooks);
+
+describe('ConfirmEmailBanner hooks', () => {
+ let out;
+ describe('state values', () => {
+ state.testGetter(state.keys.showPageBanner);
+ state.testGetter(state.keys.showConfirmModal);
+ });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+ describe('useEmailConfirmationData', () => {
+ beforeEach(() => state.mock());
+ afterEach(state.restore);
+
+ test('show page banner on unverified email', () => {
+ appHooks.useEmailConfirmationData.mockReturnValueOnce({ ...emailConfirmation });
+ out = hooks.useConfirmEmailBannerData();
+ expect(out.isNeeded).toEqual(emailConfirmation.isNeeded);
+ appHooks.useEmailConfirmationData.mockReturnValueOnce({ isNeeded: false });
+ });
+
+ test('hide page banner on verified email', () => {
+ appHooks.useEmailConfirmationData.mockReturnValueOnce({ isNeeded: false });
+ out = hooks.useConfirmEmailBannerData();
+ expect(out.isNeeded).toEqual(false);
+ });
+ });
+
+ describe('behavior', () => {
+ beforeEach(() => {
+ state.mock();
+ appHooks.useEmailConfirmationData.mockReturnValueOnce({ ...emailConfirmation });
+ out = hooks.useConfirmEmailBannerData();
+ });
+ afterEach(state.restore);
+ test('closePageBanner', () => {
+ out.closePageBanner();
+ expect(state.values.showPageBanner).toEqual(false);
+ });
+ test('closeConfirmModal', () => {
+ out.closeConfirmModal();
+ expect(state.values.showConfirmModal).toEqual(false);
+ });
+ test('openConfirmModalButtonClick', () => {
+ out.openConfirmModalButtonClick();
+ expect(state.values.showConfirmModal).toEqual(true);
+ expect(thunkActions.app.sendConfirmEmail).toBeCalled();
+ });
+ test('userConfirmEmailButtonClick', () => {
+ out.userConfirmEmailButtonClick();
+ expect(state.values.showConfirmModal).toEqual(false);
+ expect(state.values.showPageBanner).toEqual(false);
+ });
+ });
+});
diff --git a/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/index.jsx b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/index.jsx
new file mode 100644
index 0000000..1266a70
--- /dev/null
+++ b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/index.jsx
@@ -0,0 +1,66 @@
+/* eslint-disable max-len */
+import React from 'react';
+import {
+ PageBanner, ModalDialog, MarketingModal, Button,
+} from '@edx/paragon';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import messages from './messages';
+import './ConfirmEmailBanner.scss';
+import useConfirmEmailBannerData from './hooks';
+
+export const ConfirmEmailBanner = () => {
+ const {
+ isNeeded,
+ showConfirmModal,
+ showPageBanner,
+ closePageBanner,
+ closeConfirmModal,
+ openConfirmModalButtonClick,
+ userConfirmEmailButtonClick,
+ } = useConfirmEmailBannerData();
+ const { formatMessage } = useIntl();
+
+ if (!isNeeded) { return null; }
+
+ return (
+ <>
+
+ {formatMessage(messages.confirmEmailTextReminderBanner, {
+ confirmNowButton: (
+
+ ),
+ })}
+
+
+
+
+ )}
+ footerNode={(
+
+ )}
+ >
+ {formatMessage(messages.confirmEmailModalHeader)}
+ {formatMessage(messages.confirmEmailModalBody)}
+
+ >
+ );
+};
+ConfirmEmailBanner.propTypes = {};
+
+export default ConfirmEmailBanner;
diff --git a/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/index.test.jsx b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/index.test.jsx
new file mode 100644
index 0000000..600266e
--- /dev/null
+++ b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/index.test.jsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import hooks from './hooks';
+import ConfirmEmailBanner from '.';
+
+jest.mock('./hooks', () => ({
+ __esModule: true,
+ default: jest.fn(),
+}));
+
+const hookProps = {
+ isNeeded: true,
+ showPageBanner: jest.fn().mockName('showPageBanner'),
+ closePageBanner: jest.fn().mockName('closePageBanner'),
+ showConfirmModal: jest.fn().mockName('showConfirmModal'),
+ closeConfirmModal: jest.fn().mockName('closeConfirmModal'),
+ openConfirmModalButtonClick: jest.fn().mockName('openConfirmModalButtonClick'),
+ userConfirmEmailButtonClick: jest.fn().mockName('userConfirmEmailButtonClick'),
+};
+
+describe('ConfirmEmailBanner', () => {
+ describe('snapshot', () => {
+ test('do not show on already verified', () => {
+ hooks.mockReturnValueOnce({ ...hookProps, isNeeded: false });
+ const el = shallow(
);
+ expect(el).toMatchSnapshot();
+ });
+ test('Show on unverified', () => {
+ hooks.mockReturnValueOnce({ ...hookProps });
+ const el = shallow(
);
+ expect(el).toMatchSnapshot();
+ });
+ });
+});
diff --git a/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/messages.js b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/messages.js
new file mode 100644
index 0000000..5260f47
--- /dev/null
+++ b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/messages.js
@@ -0,0 +1,36 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+const messages = defineMessages({
+ confirmNowButton: {
+ id: 'leanerDashboard.confirmEmailBanner',
+ description: 'Button for sending confirm email and open modal',
+ defaultMessage: 'Confirm Now',
+ },
+ confirmEmailTextReminderBanner: {
+ id: 'leanerDashboard.confirmEmailTextReminderBanner',
+ description: 'Text for reminding user to confirm email',
+ defaultMessage: 'Remember to confirm your email so that you can keep learning on edX! {confirmNowButton}.',
+ },
+ verifiedConfirmEmailButton: {
+ id: 'leanerDashboard.verifiedConfirmEmailButton',
+ description: 'Button for verified confirming email',
+ defaultMessage: 'I\'ve confirmed my email',
+ },
+ confirmEmailModalHeader: {
+ id: 'leanerDashboard.confirmEmailModalHeader',
+ description: 'title for confirming email modal',
+ defaultMessage: 'Confirm your email',
+ },
+ confirmEmailModalBody: {
+ id: 'leanerDashboard.confirmEmailModalBody',
+ description: 'text hint for confirming email modal',
+ defaultMessage: 'We\'ve sent you an email to verify your acccount. Please check your inbox and click on the big red button to confirm and keep learning.',
+ },
+ confirmEmailImageAlt: {
+ id: 'leanerDashboard.confirmEmailImageAlt',
+ description: 'text alt confirm email image',
+ defaultMessage: 'confirm email background',
+ },
+});
+
+export default messages;
diff --git a/src/containers/LearnerDashboardHeader/index.jsx b/src/containers/LearnerDashboardHeader/index.jsx
index 4dba2c2..01c6cb9 100644
--- a/src/containers/LearnerDashboardHeader/index.jsx
+++ b/src/containers/LearnerDashboardHeader/index.jsx
@@ -8,12 +8,14 @@ import { Button } from '@edx/paragon';
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
import GreetingBanner from './GreetingBanner';
import messages from './messages';
+import ConfirmEmailBanner from './ConfirmEmailBanner';
export const LearnerDashboardHeader = () => {
const { authenticatedUser } = useContext(AppContext);
const { formatMessage } = useIntl();
return (
+