From 3586307ee766201d3a9d103bc633a5f5a4dfcc6c Mon Sep 17 00:00:00 2001
From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
Date: Tue, 11 Jul 2023 10:30:52 -0400
Subject: [PATCH] feat: react based studio footer (#359)
---
package-lock.json | 8 +-
package.json | 2 +-
src/footer/Footer.jsx | 166 +++++++++++++++++++++++++++++++++++++
src/footer/Footer.test.jsx | 120 +++++++++++++++++++++++++++
src/footer/index.jsx | 3 +
src/footer/messages.js | 66 +++++++++++++++
src/index.jsx | 2 +
7 files changed, 362 insertions(+), 5 deletions(-)
create mode 100644 src/footer/Footer.jsx
create mode 100644 src/footer/Footer.test.jsx
create mode 100644 src/footer/index.jsx
create mode 100644 src/footer/messages.js
diff --git a/package-lock.json b/package-lock.json
index ba2af872f..0bd3b0d77 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -45,7 +45,7 @@
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-build": "12.8.27",
"@edx/frontend-platform": "4.2.0",
- "@edx/paragon": "^20.32.0",
+ "@edx/paragon": "^20.45.0",
"@edx/reactifex": "^2.1.1",
"@testing-library/dom": "^8.13.0",
"@testing-library/jest-dom": "^5.16.5",
@@ -2439,9 +2439,9 @@
}
},
"node_modules/@edx/paragon": {
- "version": "20.32.0",
- "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.32.0.tgz",
- "integrity": "sha512-d2rlYoWyRLvzPMo7MfCojqU/Qvk0cXnBpXW7aOOOQYhTvC3m3Y3yNWjQ8DJhbnWyL1tFEjZG2mjlgXHDHBqtLQ==",
+ "version": "20.45.0",
+ "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.45.0.tgz",
+ "integrity": "sha512-9lHcnSJ36sQ+bsYFhydf/Pvp3Bo5N3go8R3ORPTNtvYnwiKSfjlv11QpURC/xHobXsT2eYHiwl2gNmq1yP09BA==",
"dev": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
diff --git a/package.json b/package.json
index fd6c545e7..af9f07691 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-build": "12.8.27",
"@edx/frontend-platform": "4.2.0",
- "@edx/paragon": "^20.32.0",
+ "@edx/paragon": "^20.45.0",
"@edx/reactifex": "^2.1.1",
"@testing-library/dom": "^8.13.0",
"@testing-library/jest-dom": "^5.16.5",
diff --git a/src/footer/Footer.jsx b/src/footer/Footer.jsx
new file mode 100644
index 000000000..e8bd1b2d3
--- /dev/null
+++ b/src/footer/Footer.jsx
@@ -0,0 +1,166 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import _ from 'lodash-es';
+import { intlShape, injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
+import {
+ ActionRow,
+ Button,
+ Hyperlink,
+ Image,
+ TransitionReplace,
+} from '@edx/paragon';
+import { ExpandLess, ExpandMore, Help } from '@edx/paragon/icons';
+import messages from './messages';
+
+const Footer = ({
+ marketingBaseUrl,
+ termsOfServiceUrl,
+ privacyPolicyUrl,
+ supportEmail,
+ platformName,
+ lmsBaseUrl,
+ studioBaseUrl,
+ showAccessibilityPage,
+ // injected
+ intl,
+}) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ return (
+ <>
+
+
+
+
+
+
+ {isOpen ? (
+
+
+
+ {platformName === 'edX' ? (
+
+ ) : (
+
+ )}
+
+
+ {!_.isEmpty(supportEmail) && (
+
+ )}
+
+
+ ) : null}
+
+
+ © {new Date().getFullYear()} {platformName}
+
+ {!_.isEmpty(termsOfServiceUrl) && (
+
+ {intl.formatMessage(messages.termsOfServiceLinkLabel)}
+
+ )}{!_.isEmpty(privacyPolicyUrl) && (
+
+ {intl.formatMessage(messages.privacyPolicyLinkLabel)}
+
+ )}
+ {showAccessibilityPage && (
+
+ {intl.formatMessage(messages.accessibilityRequestLinkLabel)}
+
+ )}
+ LMS
+
+
+ {/*
+ Site operators: Please do not remove this paragraph! this attributes back to edX and
+ makes your acknowledgement of edX's trademarks clear.
+ Translators: 'edX' and 'Open edX' are trademarks of 'edX Inc.'. Please do not translate
+ any of these trademarks and company names.
+ */}
+
+ edX Inc.
+
+
+
+
+
+ >
+ );
+};
+
+Footer.defaultProps = {
+ marketingBaseUrl: null,
+ termsOfServiceUrl: null,
+ privacyPolicyUrl: null,
+ spanishPrivacyPolicy: null,
+ supportEmail: null,
+};
+
+Footer.propTypes = {
+ marketingBaseUrl: PropTypes.string,
+ termsOfServiceUrl: PropTypes.string,
+ privacyPolicyUrl: PropTypes.string,
+ spanishPrivacyPolicy: PropTypes.string,
+ supportEmail: PropTypes.string,
+ platformName: PropTypes.string.isRequired,
+ lmsBaseUrl: PropTypes.string.isRequired,
+ studioBaseUrl: PropTypes.string.isRequired,
+ showAccessibilityPage: PropTypes.bool.isRequired,
+ // injected
+ intl: intlShape.isRequired,
+};
+
+export default injectIntl(Footer);
diff --git a/src/footer/Footer.test.jsx b/src/footer/Footer.test.jsx
new file mode 100644
index 000000000..ae8c46336
--- /dev/null
+++ b/src/footer/Footer.test.jsx
@@ -0,0 +1,120 @@
+import React from 'react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { formatMessage } from '../testUtils';
+import Footer from './Footer';
+import messages from './messages';
+
+const renderComponent = (
+ termsOfServiceUrl,
+ privacyPolicyUrl,
+ supportEmail,
+ showAccessibilityPage,
+ platformName,
+) => {
+ render(
+
+
+ ,
+ );
+};
+
+jest.unmock('@edx/paragon');
+
+describe('Footer', () => {
+ describe('help section default view', () => {
+ it('help button should read Looking for help with Studio?', () => {
+ renderComponent();
+ expect(screen.getByText(messages.openHelpButtonLabel.defaultMessage))
+ .toBeVisible();
+ });
+ it('help button link row should not be visible', () => {
+ renderComponent();
+ expect(screen.queryByTestId('helpButtonRow')).toBeNull();
+ });
+ });
+ describe('help section expanded view', () => {
+ it('help button should read Hide Studio help', () => {
+ renderComponent();
+ const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
+ fireEvent.click(helpToggleButton);
+ expect(screen.getByText(messages.closeHelpButtonLabel.defaultMessage))
+ .toBeVisible();
+ });
+ it('help button link row should be visible', () => {
+ renderComponent();
+ const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
+ fireEvent.click(helpToggleButton);
+ expect(screen.getByTestId('helpButtonRow')).toBeVisible();
+ });
+ describe('portal button', () => {
+ it('should equal edX partner portal and edX equals platform name', () => {
+ renderComponent(null, null, null, false, 'edX');
+ const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
+ fireEvent.click(helpToggleButton);
+ expect(screen.getByTestId('edXPortalButton')).toBeVisible();
+ expect(screen.queryByTestId('openEdXPortalButton')).toBeNull();
+ });
+ it('should equal Open edX portal and edX does not equal platform name', () => {
+ renderComponent();
+ const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
+ fireEvent.click(helpToggleButton);
+ expect(screen.queryByTestId('edXPortalButton')).toBeNull();
+ expect(screen.getByTestId('openEdXPortalButton')).toBeVisible();
+ });
+ });
+ it('should not show contact us button', () => {
+ renderComponent();
+ const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
+ fireEvent.click(helpToggleButton);
+ expect(screen.queryByTestId('contactUsButton')).toBeNull();
+ });
+ it('should show contact us button', () => {
+ renderComponent(null, null, 'support@email.com', false, null);
+ const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
+ fireEvent.click(helpToggleButton);
+ expect(screen.getByTestId('contactUsButton')).toBeVisible();
+ });
+ });
+ describe('policy link row', () => {
+ it('should only show LMS link', () => {
+ renderComponent();
+ expect(screen.getByText('LMS')).toBeVisible();
+ expect(screen.queryByTestId('termsOfService')).toBeNull();
+ expect(screen.queryByTestId('privacyPolicy')).toBeNull();
+ expect(screen.queryByTestId('accessibilityRequest')).toBeNull();
+ });
+ it('should show terms of service link', () => {
+ renderComponent('termsofserviceurl', null, null, false, null);
+ expect(screen.getByText('LMS')).toBeVisible();
+ expect(screen.queryByTestId('termsOfService')).toBeVisible();
+ expect(screen.queryByTestId('privacyPolicy')).toBeNull();
+ expect(screen.queryByTestId('accessibilityRequest')).toBeNull();
+ });
+ it('should show privacy policy link', () => {
+ renderComponent(null, 'privacypolicyurl', null, false, null);
+ expect(screen.getByText('LMS')).toBeVisible();
+ expect(screen.queryByTestId('termsOfService')).toBeNull();
+ expect(screen.queryByTestId('privacyPolicy')).toBeVisible();
+ expect(screen.queryByTestId('accessibilityRequest')).toBeNull();
+ });
+ it('should show accessibilty request link', () => {
+ renderComponent(null, null, null, true, null);
+ expect(screen.getByText('LMS')).toBeVisible();
+ expect(screen.queryByTestId('termsOfService')).toBeNull();
+ expect(screen.queryByTestId('privacyPolicy')).toBeNull();
+ expect(screen.queryByTestId('accessibilityRequest')).toBeVisible();
+ });
+ });
+});
diff --git a/src/footer/index.jsx b/src/footer/index.jsx
new file mode 100644
index 000000000..ced11e525
--- /dev/null
+++ b/src/footer/index.jsx
@@ -0,0 +1,3 @@
+import Footer from './Footer';
+
+export default Footer;
diff --git a/src/footer/messages.js b/src/footer/messages.js
new file mode 100644
index 000000000..10078c85a
--- /dev/null
+++ b/src/footer/messages.js
@@ -0,0 +1,66 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+const messages = defineMessages({
+ openHelpButtonLabel: {
+ id: 'authoring.footer.help.openHelp.button.label',
+ defaultMessage: 'Looking for help with Studio?',
+ description: 'Label for button that opens the collapsed section with help buttons',
+ },
+ closeHelpButtonLabel: {
+ id: 'authoring.footer.help.closeHelp.button.label',
+ defaultMessage: 'Hide Studio help',
+ description: 'Label for button that closes the collapsed section with help buttons',
+ },
+ edxDocumentationButtonLabel: {
+ id: 'authoring.footer.help.edxDocumentation.button.label',
+ defaultMessage: 'edX documentation',
+ description: 'Label for button that links to the edX documentation site',
+ },
+ parnterPortalButtonLabel: {
+ id: 'authoring.footer.help.parnterPortal.button.label',
+ defaultMessage: 'edX partner portal',
+ description: 'Label for button that links to the edX partner portal',
+ },
+ openEdxPortalButtonLabel: {
+ id: 'authoring.footer.help.openEdxPortal.button.label',
+ defaultMessage: 'Open edX portal',
+ description: 'Label for button that links to the Open edX portal',
+ },
+ edx101ButtonLabel: {
+ id: 'authoring.footer.help.edx101.button.label',
+ defaultMessage: 'Enroll in edX 101',
+ description: 'Label for button that links to the edX 101 course',
+ },
+ studioXButtonLabel: {
+ id: 'authoring.footer.help.studioX.button.label',
+ defaultMessage: 'Enroll in StudioX',
+ description: 'Label for button that links to the edX StudioX course',
+ },
+ contactUsButtonLabel: {
+ id: 'authoring.footer.help.contactUs.button.label',
+ defaultMessage: 'Contact us',
+ description: 'Label for button that links to the email for partner support',
+ },
+ termsOfServiceLinkLabel: {
+ id: 'authoring.footer.termsOfService.link.label',
+ defaultMessage: 'Terms of Service',
+ description: 'Label for button that links to the terms of service page',
+ },
+ privacyPolicyLinkLabel: {
+ id: 'authoring.footer.privacyPolicy.link.label',
+ defaultMessage: 'Privacy Policy',
+ description: 'Label for button that links to the privacy policy page',
+ },
+ accessibilityRequestLinkLabel: {
+ id: 'authoring.footer.accessibilityRequest.link.label',
+ defaultMessage: 'Accessibility Accomodation Request',
+ description: 'Label for button that links to the accessibility accomodation requests page',
+ },
+ trademarkMessage: {
+ id: 'authoring.footer.trademark.message',
+ defaultMessage: 'edX and Open edX, and the edX and Open edX logos are registered trademarks of',
+ description: 'Message about the use of logos and names edX and Open edX',
+ },
+});
+
+export default messages;
diff --git a/src/index.jsx b/src/index.jsx
index 3dcab4445..682dfec98 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -4,6 +4,7 @@ import EditorPage from './editors/EditorPage';
import VideoSelectorPage from './editors/VideoSelectorPage';
import DraggableList, { SortableItem } from './editors/sharedComponents/DraggableList';
import ErrorAlert from './editors/sharedComponents/ErrorAlerts/ErrorAlert';
+import Footer from './footer';
export {
messages,
@@ -12,5 +13,6 @@ export {
DraggableList,
SortableItem,
ErrorAlert,
+ Footer,
};
export default Placeholder;