diff --git a/package-lock.json b/package-lock.json
index 0e41f33..8817521 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0-semantically-released",
"license": "AGPL-3.0",
"dependencies": {
- "@edx/paragon": "20.36.0",
+ "@edx/paragon": "20.34.0",
"@fortawesome/fontawesome-svg-core": "6.3.0",
"@fortawesome/free-brands-svg-icons": "6.3.0",
"@fortawesome/free-regular-svg-icons": "6.3.0",
@@ -25,7 +25,7 @@
"@edx/frontend-build": "12.8.27",
"@edx/frontend-platform": "4.2.0",
"@edx/reactifex": "^2.1.1",
- "@testing-library/dom": "9.3.0",
+ "@testing-library/dom": "9.2.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "10.4.9",
"enzyme": "3.11.0",
@@ -3144,9 +3144,9 @@
}
},
"node_modules/@edx/paragon": {
- "version": "20.36.0",
- "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.36.0.tgz",
- "integrity": "sha512-0dn4r1HvcrHY66xmLkLTRIBD09TDrNn6vxWu1XZr2SwkGLf56cI8aGkZEySeOVs/VLWtJRMmMJaSbozCpxvLyg==",
+ "version": "20.34.0",
+ "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.34.0.tgz",
+ "integrity": "sha512-elWDy17qAHsORsqhAyp1SFOmnwKqvgHJrOvoZnw03xXUeHMn7m4j5aH3UIAHHYV/xu2au4g0YSnwni/+KDLP2A==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
@@ -6000,9 +6000,9 @@
}
},
"node_modules/@testing-library/dom": {
- "version": "9.3.0",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.0.tgz",
- "integrity": "sha512-Dffe68pGwI6WlLRYR2I0piIkyole9cSBH5jGQKCGMRpHW5RHCqAUaqc2Kv0tUyd4dU4DLPKhJIjyKOnjv4tuUw==",
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.2.0.tgz",
+ "integrity": "sha512-xTEnpUKiV/bMyEsE5bT4oYA0x0Z/colMtxzUY8bKyPXBNLn/e0V4ZjBZkEhms0xE4pv9QsPfSRu9AWS4y5wGvA==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
diff --git a/package.json b/package.json
index 0565551..f3ccc59 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"@edx/frontend-build": "12.8.27",
"@edx/frontend-platform": "4.2.0",
"@edx/reactifex": "^2.1.1",
- "@testing-library/dom": "9.3.0",
+ "@testing-library/dom": "9.2.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "10.4.9",
"enzyme": "3.11.0",
@@ -56,7 +56,7 @@
"redux-saga": "1.2.3"
},
"dependencies": {
- "@edx/paragon": "20.36.0",
+ "@edx/paragon": "20.34.0",
"@fortawesome/fontawesome-svg-core": "6.3.0",
"@fortawesome/free-brands-svg-icons": "6.3.0",
"@fortawesome/free-regular-svg-icons": "6.3.0",
diff --git a/src/DesktopHeader.jsx b/src/DesktopHeader.jsx
index 8bd142f..d7d5b62 100644
--- a/src/DesktopHeader.jsx
+++ b/src/DesktopHeader.jsx
@@ -13,6 +13,7 @@ import messages from './Header.messages';
// Assets
import { CaretIcon } from './Icons';
+import NotificationIcon from './Notifications/NotificationIcon';
class DesktopHeader extends React.Component {
constructor(props) { // eslint-disable-line no-useless-constructor
@@ -121,6 +122,7 @@ class DesktopHeader extends React.Component {
loggedIn,
intl,
appMenu,
+ notificationCounts,
} = this.props;
const logoProps = { src: logo, alt: logoAltText, href: logoDestination };
const logoClasses = getConfig().AUTHN_MINIMAL_HEADER ? 'mw-100' : null;
@@ -149,6 +151,7 @@ class DesktopHeader extends React.Component {
aria-label={intl.formatMessage(messages['header.label.secondary.nav'])}
className="nav secondary-menu-container align-items-center ml-auto"
>
+ {loggedIn && }
{loggedIn ? this.renderUserMenu() : this.renderLoggedOutItems()}
@@ -179,6 +182,10 @@ DesktopHeader.propTypes = {
avatar: PropTypes.string,
username: PropTypes.string,
loggedIn: PropTypes.bool,
+ notificationCounts: PropTypes.arrayOf(PropTypes.shape({
+ type: PropTypes.string,
+ count: PropTypes.string,
+ })),
// i18n
intl: intlShape.isRequired,
@@ -209,6 +216,7 @@ DesktopHeader.defaultProps = {
username: null,
loggedIn: false,
appMenu: null,
+ notificationCounts: [],
};
export default injectIntl(DesktopHeader);
diff --git a/src/Header.jsx b/src/Header.jsx
index c0db257..da435a0 100644
--- a/src/Header.jsx
+++ b/src/Header.jsx
@@ -88,6 +88,29 @@ const Header = ({ intl }) => {
},
];
+ const notificationCounts = [
+ {
+ type: 'total',
+ count: 25,
+ },
+ {
+ type: 'reminders',
+ count: 1,
+ },
+ {
+ type: 'discussions',
+ count: 0,
+ },
+ {
+ type: 'grades',
+ count: 0,
+ },
+ {
+ type: 'authoring',
+ count: 24,
+ },
+ ];
+
const props = {
logo: config.LOGO_URL,
logoAltText: config.SITE_NAME,
@@ -98,6 +121,7 @@ const Header = ({ intl }) => {
mainMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : mainMenu,
userMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : userMenu,
loggedOutItems: getConfig().AUTHN_MINIMAL_HEADER ? [] : loggedOutItems,
+ notificationCounts,
};
return (
diff --git a/src/Notifications/NotificationIcon.jsx b/src/Notifications/NotificationIcon.jsx
new file mode 100644
index 0000000..85ab5cf
--- /dev/null
+++ b/src/Notifications/NotificationIcon.jsx
@@ -0,0 +1,90 @@
+import React, { useState, useCallback } from 'react';
+import PropTypes from 'prop-types';
+import { NotificationsNone, Settings } from '@edx/paragon/icons';
+import {
+ Tabs, Tab, Badge, Form, Icon, IconButton, OverlayTrigger, Popover,
+} from '@edx/paragon';
+import NotificationRow from './NotificationRow';
+
+const NotificationIcon = ({ notificationCounts }) => {
+ const [showNotificationTray, setShowNotificationTray] = useState(false);
+
+ const handleNotificationTray = useCallback((value) => {
+ setShowNotificationTray(value);
+ }, []);
+
+ return (
+
+
+
+ Notifications
+
+
+
+
+
+
+
+ Hello I am the first panel.
+
+
+
+
+
+ Hello I am the third panel.
+
+
+ Hello I am the fourth panel.
+
+
+ Hello I am the fifth panel.
+
+
+ Hello I am the sixth panel.
+
+
+
+
+ )}
+ >
+ <>
+
+ {notificationCounts[0]?.count}
+
+
+ { handleNotificationTray(!showNotificationTray); }}
+ onBlur={() => { handleNotificationTray(false); }}
+ src={NotificationsNone}
+ iconAs={Icon}
+ className="d-inline-block align-bottom ml-1 bell-icon"
+ />
+
+ >
+
+
+ );
+};
+
+NotificationIcon.propTypes = {
+ notificationCounts: PropTypes.arrayOf(PropTypes.shape({
+ type: PropTypes.string,
+ count: PropTypes.string,
+ })),
+};
+
+NotificationIcon.defaultProps = {
+ notificationCounts: [],
+};
+export default NotificationIcon;
diff --git a/src/Notifications/NotificationRow.jsx b/src/Notifications/NotificationRow.jsx
new file mode 100644
index 0000000..9618ab8
--- /dev/null
+++ b/src/Notifications/NotificationRow.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { useIntl } from '@edx/frontend-platform/i18n';
+import { messages } from './messages';
+import NotificationRowItem from './NotificationRowItem';
+
+const NotificationRow = () => {
+ const intl = useIntl();
+
+ return (
+
+
+
+ {intl.formatMessage(messages.notificationTodayHeading)}
+
+
+ {intl.formatMessage(messages.notificationMarkAsRead)}
+
+
+
+
+
+
+ );
+};
+
+NotificationRow.propTypes = {
+};
+
+export default React.memo(NotificationRow);
diff --git a/src/Notifications/NotificationRowItem.jsx b/src/Notifications/NotificationRowItem.jsx
new file mode 100644
index 0000000..bd6688d
--- /dev/null
+++ b/src/Notifications/NotificationRowItem.jsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { useIntl } from '@edx/frontend-platform/i18n';
+import { Icon } from '@edx/paragon';
+// import * as timeago from 'timeago.js';
+import { messages } from './messages';
+import { PostOutline } from './icons';
+
+const NotificationRowItem = () => {
+ const intl = useIntl();
+
+ return (
+
+
+
+
+
+
+ {/*
+
+ Supply Chain Analytics
+
+ {intl.formatMessage(messages.fullStop)}
+
+
+ {timeago.format(postCreatedAt, 'time-locale')}
+
+
+
+
+
*/}
+
+
+ );
+};
+
+NotificationRowItem.propTypes = {
+};
+
+export default React.memo(NotificationRowItem);
diff --git a/src/Notifications/data/api.js b/src/Notifications/data/api.js
new file mode 100644
index 0000000..18398ba
--- /dev/null
+++ b/src/Notifications/data/api.js
@@ -0,0 +1,38 @@
+/* eslint-disable import/prefer-default-export */
+// import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
+import { getConfig } from '@edx/frontend-platform';
+
+export const getApiBaseUrl = () => getConfig().LMS_BASE_URL;
+
+export async function getCourseTopics() {
+ // const url = `${getApiBaseUrl()}/api/discussion/v1/notifications/`;
+
+ // const { data } = await getAuthenticatedHttpClient()
+ // .get(url);
+
+ const data = [{
+ TODAY: [
+ {
+ type: 'post',
+ respondingUser: 'SCM_Lead',
+ notificationContent: 'Hello and welcome to SC0x!',
+ targetUser: '',
+ courseName: 'Supply Chain Analytics',
+ URL: '',
+ status: 'unread',
+ time: '15m',
+ },
+ {
+ type: 'help',
+ respondingUser: 'MITx_Learner',
+ notificationContent: 'What grade does a student need to get in order to pass the course and earn a certificate?',
+ targetUser: '',
+ courseName: 'Supply Chain Analytics',
+ URL: '',
+ status: 'unread',
+ time: '15m',
+ },
+ ],
+ }];
+ return data;
+}
diff --git a/src/Notifications/icons/HelpOutline.jsx b/src/Notifications/icons/HelpOutline.jsx
new file mode 100644
index 0000000..53cdd48
--- /dev/null
+++ b/src/Notifications/icons/HelpOutline.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+
+const HelpOutline = () => (
+
+
+
+
+
+
+
+
+
+
+
+);
+
+export default HelpOutline;
diff --git a/src/Notifications/icons/PostOutline.jsx b/src/Notifications/icons/PostOutline.jsx
new file mode 100644
index 0000000..a7bb4d4
--- /dev/null
+++ b/src/Notifications/icons/PostOutline.jsx
@@ -0,0 +1,18 @@
+import React from 'react';
+
+const PostOutline = () => (
+
+
+
+);
+
+export default PostOutline;
diff --git a/src/Notifications/icons/index.js b/src/Notifications/icons/index.js
new file mode 100644
index 0000000..c27b06d
--- /dev/null
+++ b/src/Notifications/icons/index.js
@@ -0,0 +1,2 @@
+export { default as PostOutline } from './PostOutline';
+export { default as HelpOutline } from './HelpOutline';
diff --git a/src/Notifications/messages.js b/src/Notifications/messages.js
new file mode 100644
index 0000000..e7a3db8
--- /dev/null
+++ b/src/Notifications/messages.js
@@ -0,0 +1,25 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+// eslint-disable-next-line import/prefer-default-export
+export const messages = defineMessages({
+ notificationTodayHeading: {
+ id: 'notification.today.heading',
+ defaultMessage: 'Today',
+ description: 'Today Notifications',
+ },
+ notificationMarkAsRead: {
+ id: 'notification.mark.as.read',
+ defaultMessage: 'Mark all as read',
+ description: 'Mark all Notifications as read',
+ },
+ notificationPostedContent: {
+ id: 'notification.posted.content',
+ defaultMessage: '{respondingUser} posted {notificationContent}',
+ description: 'Display notification content for post type',
+ },
+ notificationHelpedContent: {
+ id: 'notification.helped.content',
+ defaultMessage: '{respondingUser} asked {notificationContent}',
+ description: 'Display notification content for help type',
+ },
+});
diff --git a/src/index.scss b/src/index.scss
index 8d5d162..ae08e2c 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -27,7 +27,7 @@ $white: #fff;
.learning-header {
min-width: 0;
-
+
.course-title-lockup {
min-width: 0;
@@ -118,3 +118,129 @@ $white: #fff;
border-radius: $rounded-pill;
}
}
+
+.popover .arrow{
+ display: none !important;
+ }
+.notification-title{
+ line-height: 24px;
+ font-weight: 700;
+ font-size: 18px;
+}
+.setting-icon-container{
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: 100%;
+ position: absolute;
+ margin-left: -40px;
+ margin-top: -7px;
+ span{
+ height:20px;
+ width: 20px;
+ }
+}
+
+.notification-content{
+ padding-left: 0px;
+ width: 549px;
+ }
+.notification-tabs{
+ height: 38px;
+ width:549px;
+ padding-left: 12px;
+ button{
+ font-size: 14px;
+ }
+ .dropdown-toggle{
+ height: 36px;
+ padding-top: 0px !important;
+ padding-left: 12px !important;
+ padding-right: 26px !important;
+
+ div{
+ margin-top: 4px;
+ height: 20px;
+ width: 20px;
+ }
+ }
+ .dropdown{
+ height: 36px;
+ }
+ .notification-tab, .dropdown-item{
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 12px 12px !important;
+ height: 36px;
+ font-size:14px;
+ font-weight: 500;
+ line-height: 24px;
+
+}
+.expandable{
+ height: 20px;
+ width: 20px;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ padding: 6px 7px;
+ gap: 8px;
+ position: relative;
+ margin-left: 4px;
+ }
+}
+
+.bell-container{
+
+ button{
+ position: relative;
+ background: transparent;
+ color: black;
+ border: none;
+ &:hover, &:active, &:focus{
+ background: transparent !important;
+ color: black !important;
+ border: none;
+ }
+ &::before{
+ border: none !important
+ }
+ }
+ .bell-icon-container{
+ width: 36px;
+ height: 36px;
+ border-radius: 1e+16px;
+ color: black !important;
+ &:hover{
+ background: #EAE6E5;
+ }
+ .bell-icon{
+ margin-left: -4px !important;
+ &:focus{
+ box-shadow: none !important
+ }
+ }
+
+ }
+ .badge{
+ z-index: 1;
+ border-radius: 54px;
+ border: 2px solid #FFFFFF;
+ padding: 4px 5px;
+ width: 23px;
+ height: 16px;
+ margin-top: 3px;
+ margin-left: 20px;
+ .count{
+ font-size: 9px;
+ line-height: 20px;
+ width: 13px;
+ height: 8px;
+ font-weight: 600;
+ margin-top: -3px;
+ }
+ }
+}