diff --git a/docs/decisions/0005-moment-to-dayjs.rst b/docs/decisions/0005-moment-to-dayjs.rst
new file mode 100644
index 0000000000..edc8a2cdf3
--- /dev/null
+++ b/docs/decisions/0005-moment-to-dayjs.rst
@@ -0,0 +1,46 @@
+5. Replacing Moment.js library with Day.js
+============================================================
+
+Status
+******
+
+In progress
+
+Context
+*******
+
+Moment.js is a widely used time and date library, but the creators have decided to make it a legacy project that no
+longer fixes bugs or adds new features. Because of this, and because of the large size that the Moment.js package takes up
+(4.23 MB according to npm and the minified size is 6.3KB), we are choosing to replace all instances of Moment.js
+in our enterprise repositories.
+
+Decisions
+*********
+
+In its stead, we are choosing to replace this library with the Day.js project. This is one of the projects that were
+explicitly recommended from the Moment.js team as a recommended alternative. Out of the box, it supports basic usage,
+and additional plugins have been identified based on a look through our current usages of Moment.js in our codebase.
+The plugins that we will need to add are Duration, UTC, and Timezone, with more possibilities available for future use.
+
+Day.js also has almost identical functions to Moment, and was definitely created with this in mind. So most files will
+just need to replace the package name and nothing else
+
+``moment(date).format('MMMM D, YYYY') -> dayjs(date).format('MMMM D, YYYY')``
+
+Consequences
+************
+
+By choosing Day.js over another, larger library like date-fns, we sacrifice more of the out of the box functionality
+that comes from having a bigger library. Also, day.js does not support tree-shaking like the date-fns library does.
+However, by only installing needed plugins and starting from a much smaller package size, Day.js will considerably
+decrease the JS bundle size while still maintaining the core functionality.
+
+Alternatives Considered
+***********************
+
+date-fns
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Date-fns has a 162.1 kB bundle size compared to 6.4 kB of day.js bundle. Since the majority of the functionality
+we are using with these libraries is basic, and the intended goal is to ultimately decrease the bundle size,
+opting for the more lightweight library was the preferred path forward.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 7cb996d04f..ea06117091 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,11 +17,6 @@
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
- "@fortawesome/fontawesome-svg-core": "1.2.35",
- "@fortawesome/free-brands-svg-icons": "5.15.3",
- "@fortawesome/free-regular-svg-icons": "5.15.3",
- "@fortawesome/free-solid-svg-icons": "5.15.3",
- "@fortawesome/react-fontawesome": "0.1.14",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
@@ -29,6 +24,7 @@
"color-contrast-checker": "^2.1.0",
"core-js": "3.7.0",
"dash-embedded-component": "file:packages/dash-embedded-component-2.0.2.tgz",
+ "dayjs": "^1.11.9",
"file-saver": "1.3.8",
"font-awesome": "4.7.0",
"frontend-platform-shim": "file:packages/frontend-platform-shim",
@@ -37,7 +33,6 @@
"jest-environment-jsdom": "26.6.1",
"lodash": "4.17.21",
"lodash.debounce": "4.0.8",
- "moment": "2.27.0",
"prop-types": "15.7.2",
"react": "16.14.0",
"react-dom": "16.13.1",
@@ -3994,6 +3989,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
"hasInstallScript": true,
+ "peer": true,
"engines": {
"node": ">=6"
}
@@ -4003,30 +3999,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz",
"integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==",
"hasInstallScript": true,
- "dependencies": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@fortawesome/free-brands-svg-icons": {
- "version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.3.tgz",
- "integrity": "sha512-1hirPcbjj72ZJtFvdnXGPbAbpn3Ox6mH3g5STbANFp3vGSiE5u5ingAKV06mK6ZVqNYxUPlh4DlTnaIvLtF2kw==",
- "hasInstallScript": true,
- "dependencies": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@fortawesome/free-regular-svg-icons": {
- "version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz",
- "integrity": "sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ==",
- "hasInstallScript": true,
+ "peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.35"
},
@@ -4039,6 +4012,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz",
"integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==",
"hasInstallScript": true,
+ "peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.35"
},
@@ -4050,6 +4024,7 @@
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz",
"integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==",
+ "peer": true,
"dependencies": {
"prop-types": "^15.7.2"
},
@@ -9285,6 +9260,11 @@
"node": ">=10"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.9",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz",
+ "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -16838,14 +16818,6 @@
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
- "node_modules/moment": {
- "version": "2.27.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
- "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==",
- "engines": {
- "node": "*"
- }
- },
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
diff --git a/package.json b/package.json
index b38707ffd8..c358cbf26d 100644
--- a/package.json
+++ b/package.json
@@ -33,11 +33,6 @@
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
- "@fortawesome/fontawesome-svg-core": "1.2.35",
- "@fortawesome/free-brands-svg-icons": "5.15.3",
- "@fortawesome/free-regular-svg-icons": "5.15.3",
- "@fortawesome/free-solid-svg-icons": "5.15.3",
- "@fortawesome/react-fontawesome": "0.1.14",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
@@ -45,6 +40,7 @@
"color-contrast-checker": "^2.1.0",
"core-js": "3.7.0",
"dash-embedded-component": "file:packages/dash-embedded-component-2.0.2.tgz",
+ "dayjs": "^1.11.9",
"file-saver": "1.3.8",
"font-awesome": "4.7.0",
"frontend-platform-shim": "file:packages/frontend-platform-shim",
@@ -53,7 +49,6 @@
"jest-environment-jsdom": "26.6.1",
"lodash": "4.17.21",
"lodash.debounce": "4.0.8",
- "moment": "2.27.0",
"prop-types": "15.7.2",
"react": "16.14.0",
"react-dom": "16.13.1",
diff --git a/src/components/Admin/AdminCards.jsx b/src/components/Admin/AdminCards.jsx
index d1afe4ac95..b367dff596 100644
--- a/src/components/Admin/AdminCards.jsx
+++ b/src/components/Admin/AdminCards.jsx
@@ -2,6 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import {
+ Award, Check, Groups, RemoveRedEye,
+} from '@edx/paragon/icons';
import NumberCard from '../NumberCard';
@@ -17,7 +20,7 @@ class AdminCards extends React.Component {
id: 'adminPortal.cards.registeredLearners',
defaultMessage: 'total number of learners registered',
}),
- iconClassName: 'fa fa-users',
+ icon: Groups,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.registeredUnenrolledLearners',
@@ -32,7 +35,7 @@ class AdminCards extends React.Component {
id: 'adminPortal.cards.enrolledOneCourse',
defaultMessage: 'learners enrolled in at least one course',
}),
- iconClassName: 'fa fa-check',
+ icon: Check,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.enrolledLearners',
@@ -53,7 +56,7 @@ class AdminCards extends React.Component {
id: 'adminPortal.cards.activeLearnersPastWeek',
defaultMessage: 'active learners in the past week',
}),
- iconClassName: 'fa fa-eye',
+ icon: RemoveRedEye,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.learnersActiveWeek',
@@ -77,7 +80,7 @@ class AdminCards extends React.Component {
courseCompletions: {
ref: React.createRef(),
description: 'course completions',
- iconClassName: 'fa fa-trophy',
+ icon: Award,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.completedLearners',
@@ -107,7 +110,7 @@ class AdminCards extends React.Component {
id={cardKey}
title={title}
description={card.description}
- iconClassName={card.iconClassName}
+ icon={card.icon}
detailActions={card.actions}
/>
diff --git a/src/components/Admin/AdminSearchForm.jsx b/src/components/Admin/AdminSearchForm.jsx
index 92d14f7ed8..3fcec0a454 100644
--- a/src/components/Admin/AdminSearchForm.jsx
+++ b/src/components/Admin/AdminSearchForm.jsx
@@ -1,10 +1,10 @@
/* eslint-disable camelcase */
import React from 'react';
-import moment from 'moment';
+import dayjs from 'dayjs';
import PropTypes from 'prop-types';
-import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { Form } from '@edx/paragon';
+import { Info } from '@edx/paragon/icons';
import SearchBar from '../SearchBar';
import { updateUrl } from '../../utils';
@@ -80,7 +80,7 @@ class AdminSearchForm extends React.Component {
Filter by start date
@@ -101,7 +101,7 @@ class AdminSearchForm extends React.Component {
value={date}
key={date}
>
- {moment(date).format('MMMM D, YYYY')}
+ {dayjs(date).format('MMMM D, YYYY')}
))}
diff --git a/src/components/Admin/EmbeddedSubscription.jsx b/src/components/Admin/EmbeddedSubscription.jsx
index 22d73e3a28..24bf7575bc 100644
--- a/src/components/Admin/EmbeddedSubscription.jsx
+++ b/src/components/Admin/EmbeddedSubscription.jsx
@@ -1,7 +1,7 @@
import React, { useContext, useState } from 'react';
+import dayjs from 'dayjs';
import { useParams, Link } from 'react-router-dom';
import { Form, Icon } from '@edx/paragon';
-import moment from 'moment';
import { Lightbulb, ArrowOutward } from '@edx/paragon/icons';
import ConnectedSubscriptionDetailPage from './SubscriptionDetailPage';
import { SubscriptionContext } from '../subscriptions/SubscriptionData';
@@ -15,7 +15,7 @@ const EmbeddedSubscription = () => {
const [subscriptionUUID, setSubscriptionUUID] = useState(null);
const [firstLoad, setFirstLoad] = useState(true);
const sortedSubscriptions = sortSubscriptionsByStatus(subscriptions);
- const activeSubscriptions = sortedSubscriptions.filter(c => !moment().isAfter(c.expirationDate));
+ const activeSubscriptions = sortedSubscriptions.filter(c => !dayjs().isAfter(c.expirationDate));
const match = { params: { subscriptionUUID } };
if (!loading && activeSubscriptions.length > 0 && firstLoad) {
match.params.subscriptionUUID = activeSubscriptions[0].uuid;
diff --git a/src/components/Admin/SubscriptionDetails.jsx b/src/components/Admin/SubscriptionDetails.jsx
index 71d556ac3e..2c94bedf5b 100644
--- a/src/components/Admin/SubscriptionDetails.jsx
+++ b/src/components/Admin/SubscriptionDetails.jsx
@@ -1,7 +1,7 @@
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import moment from 'moment';
+import dayjs from 'dayjs';
import {
Row, Col, Toast, Button,
} from '@edx/paragon';
@@ -37,7 +37,7 @@ const SubscriptionDetails = ({ enterpriseSlug }) => {
- {moment(subscription.startDate).format('MMMM D, YYYY')} - {moment(subscription.expirationDate).format('MMMM D, YYYY')}
+ {dayjs(subscription.startDate).format('MMMM D, YYYY')} - {dayjs(subscription.expirationDate).format('MMMM D, YYYY')}
diff --git a/src/components/Admin/__snapshots__/Admin.test.jsx.snap b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
index 13fb8a3fa2..fd763719cf 100644
--- a/src/components/Admin/__snapshots__/Admin.test.jsx.snap
+++ b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
@@ -219,10 +219,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+ Show details
+
@@ -312,10 +340,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+ Show details
+
@@ -421,10 +477,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+ Show details
+
@@ -546,10 +630,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+ Show details
+
@@ -682,10 +794,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
Reset to
Full Report
@@ -724,11 +850,19 @@ exports[` renders correctly with dashboard analytics data renders # cou
onClick={[Function]}
type="button"
>
-
+
Download current report (CSV)
@@ -798,10 +932,24 @@ exports[` renders correctly with dashboard analytics data renders # of
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -891,10 +1053,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -1000,10 +1190,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -1125,10 +1343,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -1261,10 +1507,24 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
Reset to
Full Report
@@ -1303,11 +1563,19 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
type="button"
>
-
+
Download current report (CSV)
@@ -1377,10 +1645,24 @@ exports[` renders correctly with dashboard analytics data renders # of
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -1470,10 +1766,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -1579,10 +1903,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -1704,10 +2056,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+ Show details
+
@@ -1840,10 +2220,24 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
Reset to
Full Report
@@ -1886,11 +2280,19 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
type="button"
>
-
+
Download current report (CSV)
@@ -1960,10 +2362,24 @@ exports[` renders correctly with dashboard analytics data renders colla
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+ Show details
+
@@ -2053,10 +2483,24 @@ exports[` renders correctly with dashboard analytics data renders colla
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+ Show details
+
@@ -2162,10 +2620,24 @@ exports[` renders correctly with dashboard analytics data renders colla
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+ Show details
+
@@ -2287,10 +2773,24 @@ exports[` renders correctly with dashboard analytics data renders colla
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+ Show details
+
@@ -2451,11 +2965,19 @@ exports[` renders correctly with dashboard analytics data renders colla
onClick={[Function]}
type="button"
>
-
+
Download full report (CSV)
@@ -2477,7 +2999,7 @@ exports[` renders correctly with dashboard analytics data renders colla
>
@@ -2486,7 +3008,7 @@ exports[` renders correctly with dashboard analytics data renders colla
>
renders correctly with dashboard analytics data renders colla
>
renders correctly with dashboard analytics data renders colla
renders correctly with dashboard analytics data renders colla
>