Skip to content

Commit

Permalink
feat: add studio footer
Browse files Browse the repository at this point in the history
  • Loading branch information
KristinAoki authored Oct 2, 2023
2 parents e7e3f59 + 8dd460e commit 7b2265e
Show file tree
Hide file tree
Showing 9 changed files with 1,189 additions and 218 deletions.
5 changes: 5 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ LOGO_URL=https://edx-cdn.org/v3/prod/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/prod/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/prod/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/prod/favicon.ico
TERMS_OF_SERVICE_URL=null
PRIVACY_POLICY_URL=null
SUPPORT_EMAIL=null
STUDIO_BASE_URL=http://localhost:18010
SHOW_ACCESSIBILITY_PAGE=false
5 changes: 5 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ LOGO_URL=https://edx-cdn.org/v3/prod/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/prod/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/prod/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/prod/favicon.ico
TERMS_OF_SERVICE_URL=
PRIVACY_POLICY_URL=
SUPPORT_EMAIL=
STUDIO_BASE_URL=http://localhost:18010
SHOW_ACCESSIBILITY_PAGE=false
1,051 changes: 837 additions & 214 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-build": "12.9.17",
"@edx/frontend-platform": "5.4.0",
"@edx/paragon": "21.3.1",
"@edx/reactifex": "2.2.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^12.1.1",
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
"enzyme": "3.11.0",
"husky": "8.0.3",
Expand All @@ -52,16 +53,17 @@
"redux": "4.2.1"
},
"dependencies": {
"@edx/paragon": "^21.3.1",
"@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-brands-svg-icons": "6.4.0",
"@fortawesome/free-regular-svg-icons": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.4.0",
"@fortawesome/react-fontawesome": "0.2.0",
"js-cookie": "^3.0.1"
"js-cookie": "^3.0.1",
"lodash": "^4.17.21"
},
"peerDependencies": {
"@edx/frontend-platform": "^4.0.0 || ^5.0.0",
"@edx/paragon": "^20.0.0 || ^21.0.0",
"prop-types": "^15.7.0",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
Expand Down
146 changes: 146 additions & 0 deletions src/components/studio-footer/StudioFooter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import React, { useContext, useState } from 'react';
import _ from 'lodash';
import { intlShape, injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { ensureConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react';
import {
ActionRow,
Button,
Container,
Hyperlink,
Image,
TransitionReplace,
} from '@edx/paragon';
import { ExpandLess, ExpandMore, Help } from '@edx/paragon/icons';
import messages from './messages';

ensureConfig([
'LMS_BASE_URL',
'MARKETING_SITE_BASE_URL',
'TERMS_OF_SERVICE_URL',
'PRIVACY_POLICY_URL',
'SUPPORT_EMAIL',
'SITE_NAME',
'STUDIO_BASE_URL',
'SHOW_ACCESSIBILITY_PAGE',
], 'Studio Footer component');

const StudioFooter = ({
// injected
intl,
}) => {
const [isOpen, setIsOpen] = useState(false);
const { config } = useContext(AppContext);

return (
<>
<div className="m-0 mt-6 row align-items-center justify-content-center">
<div className="col border-top mr-2" />
<Button
data-testid="helpToggleButton"
variant="outline-primary"
onClick={() => setIsOpen(!isOpen)}
iconBefore={Help}
iconAfter={isOpen ? ExpandLess : ExpandMore}
size="sm"
>
{isOpen ? intl.formatMessage(messages.closeHelpButtonLabel)
: intl.formatMessage(messages.openHelpButtonLabel)}
</Button>
<div className="col border-top ml-2" />
</div>
<Container size="xl" className="px-4">
<TransitionReplace>
{isOpen ? (
<ActionRow key="help-link-button-row" className="py-4" data-testid="helpButtonRow">
<ActionRow.Spacer />
<Button as="a" href="https://docs.edx.org/" size="sm">
<FormattedMessage {...messages.edxDocumentationButtonLabel} />
</Button>
<Button
as="a"
href="https://partners.edx.org/"
size="sm"
data-testid="edXPortalButton"
>
<FormattedMessage {...messages.parnterPortalButtonLabel} />
</Button>
<Button
as="a"
href="https://www.edx.org/course/edx101-overview-of-creating-an-edx-course#.VO4eaLPF-n1"
size="sm"
>
<FormattedMessage {...messages.edx101ButtonLabel} />
</Button>
<Button
as="a"
href="https://www.edx.org/course/studiox-creating-a-course-with-edx-studio"
size="sm"
>
<FormattedMessage {...messages.studioXButtonLabel} />
</Button>
{!_.isEmpty(config.SUPPORT_EMAIL) && (
<Button
as="a"
href={`mailto:${config.SUPPORT_EMAIL}`}
size="sm"
data-testid="contactUsButton"
>
<FormattedMessage {...messages.contactUsButtonLabel} />
</Button>
)}
<ActionRow.Spacer />
</ActionRow>
) : null}
</TransitionReplace>
<ActionRow className="pt-3 m-0 x-small">
© {new Date().getFullYear()} <Hyperlink destination={config.MARKETING_BASE_URL} target="_blank" className="ml-2">{config.SITE_NAME}</Hyperlink>
<ActionRow.Spacer />
{!_.isEmpty(config.TERMS_OF_SERVICE_URL) && (
<Hyperlink destination={config.TERMS_OF_SERVICE_URL} data-testid="termsOfService">
{intl.formatMessage(messages.termsOfServiceLinkLabel)}
</Hyperlink>
)}{!_.isEmpty(config.PRIVACY_POLICY_URL) && (
<Hyperlink destination={config.PRIVACY_POLICY_URL} data-testid="privacyPolicy">
{intl.formatMessage(messages.privacyPolicyLinkLabel)}
</Hyperlink>
)}
{config.SHOW_ACCESSIBILITY_PAGE === 'true' && (
<Hyperlink
destination={`${config.STUDIO_BASE_URL}/accessibility`}
data-testid="accessibilityRequest"
>
{intl.formatMessage(messages.accessibilityRequestLinkLabel)}
</Hyperlink>
)}
<Hyperlink destination={config.LMS_BASE_URL}>LMS</Hyperlink>
</ActionRow>
<ActionRow className="mt-3 pb-4 x-small">
{/*
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.
*/}
<FormattedMessage {...messages.trademarkMessage} />
<Hyperlink className="ml-1" destination="https://www.edx.org">edX Inc</Hyperlink>.
<ActionRow.Spacer />
<Hyperlink destination="https://open.edx.org" className="float-right">
<Image
width="120px"
alt="Powered by Open edX"
src="https://logos.openedx.org/open-edx-logo-tag.png"
/>
</Hyperlink>
</ActionRow>
</Container>
</>
);
};

StudioFooter.propTypes = {
// injected
intl: intlShape.isRequired,
};

export default injectIntl(StudioFooter);
120 changes: 120 additions & 0 deletions src/components/studio-footer/StudioFooter.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable react/prop-types */
import React, { useMemo } 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 { AppContext } from '@edx/frontend-platform/react';
import StudioFooter from './StudioFooter';
import messages from './messages';

const config = {
LMS_BASE_URL: process.env.LMS_BASE_URL,
MARKETING_SITE_BASE_URL: process.env.MARKETING_SITE_BASE_URL,
TERMS_OF_SERVICE_URL: process.env.TERMS_OF_SERVICE_URL,
PRIVACY_POLICY_URL: process.env.PRIVACY_POLICY_URL,
SUPPORT_EMAIL: process.env.SUPPORT_EMAIL,
SITE_NAME: process.env.SITE_NAME,
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL,
SHOW_ACCESSIBILITY_PAGE: process.env.SHOW_ACCESSIBILITY_PAGE,
};

let currentConfig;
const Component = () => {
const contextValue = useMemo(() => ({
authenticatedUser: null,
config: currentConfig,
}), []);

return (
<IntlProvider locale="en">
<AppContext.Provider value={contextValue}>
<StudioFooter />
</AppContext.Provider>
</IntlProvider>
);
};

describe('Footer', () => {
beforeEach(() => {
jest.clearAllMocks();
currentConfig = config;
});
describe('help section default view', () => {
it('help button should read Looking for help with Studio?', () => {
render(<Component />);
expect(screen.getByText(messages.openHelpButtonLabel.defaultMessage))
.toBeVisible();
});
it('help button link row should not be visible', () => {
render(<Component />);
expect(screen.queryByTestId('helpButtonRow')).toBeNull();
});
});
describe('help section expanded view', () => {
it('help button should read Hide Studio help', () => {
render(<Component />);
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', () => {
render(<Component />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton);
expect(screen.getByTestId('helpButtonRow')).toBeVisible();
});
it('edX portal button should be visible', () => {
render(<Component />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton);
expect(screen.queryByTestId('edXPortalButton')).toBeVisible();
});
it('should not show contact us button', () => {
render(<Component />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton);
expect(screen.queryByTestId('contactUsButton')).toBeNull();
});
it('should show contact us button', () => {
currentConfig = { ...config, SUPPORT_EMAIL: 'support@email.com' };
render(<Component />);
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', () => {
render(<Component />);
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', () => {
currentConfig = { ...config, TERMS_OF_SERVICE_URL: 'termsofserviceurl' };
render(<Component />);
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', () => {
currentConfig = { ...config, PRIVACY_POLICY_URL: 'privacypolicyurl' };
render(<Component />);
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', () => {
currentConfig = { ...config, SHOW_ACCESSIBILITY_PAGE: 'true' };
render(<Component />);
expect(screen.getByText('LMS')).toBeVisible();
expect(screen.queryByTestId('termsOfService')).toBeNull();
expect(screen.queryByTestId('privacyPolicy')).toBeNull();
expect(screen.queryByTestId('accessibilityRequest')).toBeVisible();
});
});
});
3 changes: 3 additions & 0 deletions src/components/studio-footer/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import StudioFooter from './StudioFooter';

export default StudioFooter;
61 changes: 61 additions & 0 deletions src/components/studio-footer/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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',
},
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;
8 changes: 7 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import Footer, { EVENT_NAMES } from './components/Footer';
import StudioFooter from './components/studio-footer';
import messages from './i18n/index';

// preserving sub export footer to match with all of v5 release.
export { Footer, messages, EVENT_NAMES };
export {
Footer,
messages,
EVENT_NAMES,
StudioFooter,
};
export default Footer;

0 comments on commit 7b2265e

Please sign in to comment.