Skip to content

Commit dad01fc

Browse files
Merge pull request #340 from openedx/sundas/INF-820
feat: added notification UI
2 parents de69ed3 + 30e6eed commit dad01fc

23 files changed

Lines changed: 1190 additions & 59 deletions

example/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import React from 'react';
44
import ReactDOM from 'react-dom';
55
import { initialize, getConfig, subscribe, APP_READY } from '@edx/frontend-platform';
66
import { AppContext, AppProvider } from '@edx/frontend-platform/react';
7-
import Header from '@edx/frontend-component-header';
7+
// import Header from '@edx/frontend-component-header';
8+
import { LearningHeader as Header } from '@edx/frontend-component-header';
89

910
import './index.scss';
1011

package-lock.json

Lines changed: 58 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
"react": "16.14.0",
5151
"react-dom": "16.14.0",
5252
"react-redux": "7.2.9",
53-
"react-router-dom": "5.3.4",
5453
"react-test-renderer": "16.14.0",
5554
"redux": "4.2.1",
5655
"redux-saga": "1.2.3"
@@ -62,9 +61,15 @@
6261
"@fortawesome/free-regular-svg-icons": "6.3.0",
6362
"@fortawesome/free-solid-svg-icons": "6.3.0",
6463
"@fortawesome/react-fontawesome": "^0.2.0",
64+
"@reduxjs/toolkit": "1.9.5",
6565
"babel-polyfill": "6.26.0",
66+
"classnames": "2.3.2",
67+
"lodash": "4.17.21",
68+
"react-redux": "7.2.9",
6669
"react-responsive": "8.2.0",
67-
"react-transition-group": "4.4.5"
70+
"react-transition-group": "4.4.5",
71+
"timeago.js": "4.0.2",
72+
"react-router-dom": "5.3.4"
6873
},
6974
"peerDependencies": {
7075
"@edx/frontend-platform": "^4.0.0",

src/Header.test.jsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,38 @@
22
import React from 'react';
33
import { IntlProvider } from '@edx/frontend-platform/i18n';
44
import TestRenderer from 'react-test-renderer';
5-
import { AppContext } from '@edx/frontend-platform/react';
5+
import { AppContext, AppProvider } from '@edx/frontend-platform/react';
66
import { Context as ResponsiveContext } from 'react-responsive';
7+
import { initializeMockApp } from '@edx/frontend-platform';
8+
import store from './store';
79

810
import Header from './index';
911

1012
const HeaderComponent = ({ width, contextValue }) => (
1113
<ResponsiveContext.Provider value={width}>
1214
<IntlProvider locale="en" messages={{}}>
13-
<AppContext.Provider
14-
value={contextValue}
15-
>
16-
<Header />
17-
</AppContext.Provider>
15+
<AppProvider store={store}>
16+
<AppContext.Provider
17+
value={contextValue}
18+
>
19+
<Header />
20+
</AppContext.Provider>
21+
</AppProvider>
1822
</IntlProvider>
1923
</ResponsiveContext.Provider>
2024
);
2125

2226
describe('<Header />', () => {
27+
beforeEach(async () => {
28+
initializeMockApp({
29+
authenticatedUser: {
30+
userId: '123abc',
31+
username: 'testuser',
32+
administrator: false,
33+
roles: [],
34+
},
35+
});
36+
});
2337
it('renders correctly for anonymous desktop', () => {
2438
const contextValue = {
2539
authenticatedUser: null,
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React, { useCallback } from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import { useIntl } from '@edx/frontend-platform/i18n';
4+
import PropTypes from 'prop-types';
5+
import { Icon } from '@edx/paragon';
6+
import { Link } from 'react-router-dom';
7+
import * as timeago from 'timeago.js';
8+
import { getIconByType } from './utils';
9+
import { markNotificationsAsRead } from './data/thunks';
10+
import messages from './messages';
11+
import timeLocale from '../common/time-locale';
12+
13+
const NotificationRowItem = ({
14+
id, type, contentUrl, content, courseName, createdAt, lastRead,
15+
}) => {
16+
timeago.register('time-locale', timeLocale);
17+
const intl = useIntl();
18+
const dispatch = useDispatch();
19+
20+
const handleMarkAsRead = useCallback(() => {
21+
dispatch(markNotificationsAsRead(id));
22+
}, [dispatch, id]);
23+
24+
const { icon: iconComponent, class: iconClass } = getIconByType(type);
25+
26+
return (
27+
<Link
28+
target="_blank"
29+
className="d-flex mb-2 align-items-center text-decoration-none"
30+
to={contentUrl}
31+
onClick={handleMarkAsRead}
32+
>
33+
<Icon src={iconComponent} className={`${iconClass} mr-4 notification-icon`} />
34+
<div className="d-flex w-100">
35+
<div className="d-flex align-items-center w-100">
36+
<div className="py-10px w-100 px-0 cursor-pointer">
37+
<span
38+
className="line-height-24 text-gray-700 mb-2 notification-item-content overflow-hidden content"
39+
// eslint-disable-next-line react/no-danger
40+
dangerouslySetInnerHTML={{ __html: content }}
41+
/>
42+
<div className="py-0 d-flex">
43+
<span className="font-size-12 text-gray-500 line-height-20">
44+
<span>{courseName}</span>
45+
<span className="text-light-700 px-1.5">{intl.formatMessage(messages.fullStop)}</span>
46+
<span>{timeago.format(createdAt, 'time-locale')}</span>
47+
</span>
48+
</div>
49+
</div>
50+
{!lastRead && (
51+
<div className="d-flex py-1.5 px-1.5 ml-2 cursor-pointer">
52+
<span className="bg-brand-500 rounded unread" />
53+
</div>
54+
)}
55+
</div>
56+
</div>
57+
</Link>
58+
);
59+
};
60+
61+
NotificationRowItem.propTypes = {
62+
id: PropTypes.string.isRequired,
63+
type: PropTypes.string.isRequired,
64+
contentUrl: PropTypes.string.isRequired,
65+
content: PropTypes.node.isRequired,
66+
courseName: PropTypes.string.isRequired,
67+
createdAt: PropTypes.string.isRequired,
68+
lastRead: PropTypes.string.isRequired,
69+
};
70+
71+
export default React.memo(NotificationRowItem);

0 commit comments

Comments
 (0)