Skip to content

Commit bca75dc

Browse files
authored
Merge pull request #500 from edx/feat/add_new_studio_footer
feat: change studio footer design
2 parents dde5d8f + d7f3a5b commit bca75dc

File tree

4 files changed

+166
-227
lines changed

4 files changed

+166
-227
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
.footer-links-row {
2+
display: flex;
3+
flex-wrap: wrap;
4+
align-items: center;
5+
justify-content: flex-start;
6+
gap: 3rem;
7+
}
8+
9+
.footer-link {
10+
font-size: 14px;
11+
font-style: normal;
12+
font-weight: 400;
13+
line-height: 20px;
14+
}
15+
16+
.footer-link-color {
17+
color: #00262B;
18+
}
19+
20+
.footer-muted-text-color {
21+
color: #707070;
22+
23+
a {
24+
text-decoration-line: underline;
25+
}
26+
}
27+
28+
.footer-bottom-row {
29+
display: flex;
30+
flex-direction: row;
31+
justify-content: space-between;
32+
align-items: center;
33+
}
34+
35+
.footer-bottom-col {
36+
display: flex;
37+
flex-direction: column;
38+
gap: 0.5rem;
39+
40+
.footer-links-row {
41+
gap: 1.5rem;
42+
margin-bottom: 1rem;
43+
}
44+
}
45+
46+
47+
@media (max-width: 700px) {
48+
.footer-links-row {
49+
flex-direction: column;
50+
gap: 0.8rem;
51+
align-items: flex-start;
52+
justify-content: flex-start;
53+
}
54+
.footer-links-row > :last-child {
55+
padding-bottom: 0 !important;
56+
margin-bottom: 0 !important;
57+
}
58+
59+
.footer-bottom-row {
60+
flex-direction: column;
61+
align-items: flex-start;
62+
gap: 1rem;
63+
}
64+
65+
.footer-bottom-col {
66+
display: flex;
67+
flex-direction: column;
68+
gap: 0.5rem;
69+
70+
.footer-links-row {
71+
gap: 0.8rem;
72+
margin-bottom: 0;
73+
}
74+
}
75+
}
Lines changed: 61 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import React, { useContext, useState } from 'react';
2-
import _ from 'lodash';
3-
import { intlShape, injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
1+
import React, { useContext } from 'react';
2+
import { useIntl } from '@edx/frontend-platform/i18n';
43
import { ensureConfig } from '@edx/frontend-platform';
54
import { AppContext } from '@edx/frontend-platform/react';
65
import {
7-
ActionRow,
8-
Button,
96
Container,
107
Hyperlink,
118
Image,
12-
TransitionReplace,
9+
MailtoLink,
1310
} from '@openedx/paragon';
14-
import { ExpandLess, ExpandMore, Help } from '@openedx/paragon/icons';
11+
import './StudioFooter.css';
1512
import messages from './messages';
1613

1714
ensureConfig([
@@ -22,125 +19,69 @@ ensureConfig([
2219
'SUPPORT_EMAIL',
2320
'SITE_NAME',
2421
'STUDIO_BASE_URL',
25-
'SHOW_ACCESSIBILITY_PAGE',
22+
'LOGO_URL',
2623
], 'Studio Footer component');
2724

28-
const StudioFooter = ({
29-
// injected
30-
intl,
31-
}) => {
32-
const [isOpen, setIsOpen] = useState(false);
25+
const StudioFooter = () => {
26+
const intl = useIntl();
3327
const { config } = useContext(AppContext);
3428

3529
return (
36-
<>
37-
<div className="m-0 mt-6 row align-items-center justify-content-center">
38-
<div className="col border-top mr-2" />
39-
<Button
40-
data-testid="helpToggleButton"
41-
variant="outline-primary"
42-
onClick={() => setIsOpen(!isOpen)}
43-
iconBefore={Help}
44-
iconAfter={isOpen ? ExpandLess : ExpandMore}
45-
size="sm"
46-
>
47-
{isOpen ? intl.formatMessage(messages.closeHelpButtonLabel)
48-
: intl.formatMessage(messages.openHelpButtonLabel)}
49-
</Button>
50-
<div className="col border-top ml-2" />
30+
<footer
31+
role="contentinfo"
32+
aria-label={intl.formatMessage(messages.footerLabel)}
33+
className="border-top shadow"
34+
style={{ background: '#FFF' }}
35+
>
36+
<div>
37+
<Container size="xl" className="p-4">
38+
<div className="footer-links-row text-muted">
39+
<Hyperlink className="footer-link footer-link-color" destination={`${config.BASE_URL}/home`}>{intl.formatMessage(messages.studioLinkLabel)}</Hyperlink>
40+
<Hyperlink data-testid="LMS" className="footer-link footer-link-color" destination={config.LMS_BASE_URL}>{intl.formatMessage(messages.lmsLinkLabel)}</Hyperlink>
41+
<Hyperlink className="footer-link footer-link-color" destination={`${config.BASE_URL}/release-notes`}>{intl.formatMessage(messages.releaseNotesLinkLabel)}</Hyperlink>
42+
<Hyperlink className="footer-link footer-link-color" destination="https://docs.edx.org/">{intl.formatMessage(messages.edxDocumentationLinkLabel)}</Hyperlink>
43+
<MailtoLink className="footer-link footer-link-color" to={config.SUPPORT_EMAIL}>{intl.formatMessage(messages.contactUsLinkLabel)}</MailtoLink>
44+
</div>
45+
</Container>
46+
<div className="border-top" />
47+
<Container size="xl" className="p-4">
48+
<div className="footer-bottom-row">
49+
<div className="footer-bottom-columns">
50+
<div className="footer-bottom-col text-muted">
51+
<div className="footer-links-row">
52+
<span className="footer-link footer-muted-text-color">© {new Date().getFullYear()} <Hyperlink className="footer-link footer-link-color" destination={config.MARKETING_SITE_BASE_URL}>{config.SITE_NAME}</Hyperlink>.</span>
53+
<Hyperlink data-testid="termsOfService" className="footer-link footer-link-color" destination={config.TERMS_OF_SERVICE_URL}>{intl.formatMessage(messages.termsOfServiceLinkLabel)}</Hyperlink>
54+
<Hyperlink className="footer-link footer-link-color" destination={config.PRIVACY_POLICY_URL}>{intl.formatMessage(messages.privacyPolicyLinkLabel)}</Hyperlink>
55+
<Hyperlink className="footer-link footer-link-color" destination={`${config.STUDIO_BASE_URL}/accessibility`}>
56+
{intl.formatMessage(messages.accessibilityRequestLinkLabel)}
57+
</Hyperlink>
58+
</div>
59+
<div>
60+
{/*
61+
Site operators: Please do not remove this paragraph! this attributes back to edX and
62+
makes your acknowledgement of edX's trademarks clear.
63+
Translators: 'edX' and 'Open edX' are trademarks of 'edX Inc.'. Please do not translate
64+
any of these trademarks and company names.
65+
*/}
66+
<span className="footer-link footer-muted-text-color">{intl.formatMessage(messages.trademarkMessage)}<Hyperlink className="footer-link footer-link-color" destination="https://www.edx.org">edX Inc</Hyperlink>.</span>
67+
</div>
68+
</div>
69+
</div>
70+
<div className="">
71+
<Hyperlink destination={config.MARKETING_SITE_BASE_URL}>
72+
<Image
73+
src={config.LOGO_URL}
74+
alt="edX logo"
75+
height={32}
76+
className="footer-logo"
77+
/>
78+
</Hyperlink>
79+
</div>
80+
</div>
81+
</Container>
5182
</div>
52-
<Container size="xl" className="px-4">
53-
<TransitionReplace>
54-
{isOpen ? (
55-
<ActionRow key="help-link-button-row" className="py-4" data-testid="helpButtonRow">
56-
<ActionRow.Spacer />
57-
<Button as="a" href="https://docs.edx.org/" size="sm">
58-
<FormattedMessage {...messages.edxDocumentationButtonLabel} />
59-
</Button>
60-
<Button
61-
as="a"
62-
href="https://partners.edx.org/"
63-
size="sm"
64-
data-testid="edXPortalButton"
65-
>
66-
<FormattedMessage {...messages.parnterPortalButtonLabel} />
67-
</Button>
68-
<Button
69-
as="a"
70-
href="https://www.edx.org/course/edx101-overview-of-creating-an-edx-course#.VO4eaLPF-n1"
71-
size="sm"
72-
>
73-
<FormattedMessage {...messages.edx101ButtonLabel} />
74-
</Button>
75-
<Button
76-
as="a"
77-
href="https://www.edx.org/course/studiox-creating-a-course-with-edx-studio"
78-
size="sm"
79-
>
80-
<FormattedMessage {...messages.studioXButtonLabel} />
81-
</Button>
82-
{!_.isEmpty(config.SUPPORT_EMAIL) && (
83-
<Button
84-
as="a"
85-
href={`mailto:${config.SUPPORT_EMAIL}`}
86-
size="sm"
87-
data-testid="contactUsButton"
88-
>
89-
<FormattedMessage {...messages.contactUsButtonLabel} />
90-
</Button>
91-
)}
92-
<ActionRow.Spacer />
93-
</ActionRow>
94-
) : null}
95-
</TransitionReplace>
96-
<ActionRow className="pt-3 m-0 x-small">
97-
© {new Date().getFullYear()} <Hyperlink destination={config.MARKETING_BASE_URL} target="_blank" className="ml-2">{config.SITE_NAME}</Hyperlink>
98-
<ActionRow.Spacer />
99-
{!_.isEmpty(config.TERMS_OF_SERVICE_URL) && (
100-
<Hyperlink destination={config.TERMS_OF_SERVICE_URL} data-testid="termsOfService">
101-
{intl.formatMessage(messages.termsOfServiceLinkLabel)}
102-
</Hyperlink>
103-
)}{!_.isEmpty(config.PRIVACY_POLICY_URL) && (
104-
<Hyperlink destination={config.PRIVACY_POLICY_URL} data-testid="privacyPolicy">
105-
{intl.formatMessage(messages.privacyPolicyLinkLabel)}
106-
</Hyperlink>
107-
)}
108-
{config.SHOW_ACCESSIBILITY_PAGE === 'true' && (
109-
<Hyperlink
110-
destination={`${config.STUDIO_BASE_URL}/accessibility`}
111-
data-testid="accessibilityRequest"
112-
>
113-
{intl.formatMessage(messages.accessibilityRequestLinkLabel)}
114-
</Hyperlink>
115-
)}
116-
<Hyperlink destination={config.LMS_BASE_URL}>LMS</Hyperlink>
117-
</ActionRow>
118-
<ActionRow className="mt-3 pb-4 x-small">
119-
{/*
120-
Site operators: Please do not remove this paragraph! this attributes back to edX and
121-
makes your acknowledgement of edX's trademarks clear.
122-
Translators: 'edX' and 'Open edX' are trademarks of 'edX Inc.'. Please do not translate
123-
any of these trademarks and company names.
124-
*/}
125-
<FormattedMessage {...messages.trademarkMessage} />
126-
<Hyperlink className="ml-1" destination="https://www.edx.org">edX Inc</Hyperlink>.
127-
<ActionRow.Spacer />
128-
<Hyperlink destination="https://open.edx.org" className="float-right">
129-
<Image
130-
width="120px"
131-
alt="Powered by Open edX"
132-
src="https://logos.openedx.org/open-edx-logo-tag.png"
133-
/>
134-
</Hyperlink>
135-
</ActionRow>
136-
</Container>
137-
</>
83+
</footer>
13884
);
13985
};
14086

141-
StudioFooter.propTypes = {
142-
// injected
143-
intl: intlShape.isRequired,
144-
};
145-
146-
export default injectIntl(StudioFooter);
87+
export default StudioFooter;
Lines changed: 3 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
/* eslint-disable react/prop-types */
22
import React, { useMemo } from 'react';
3-
import { fireEvent, render, screen } from '@testing-library/react';
3+
import { render, screen } from '@testing-library/react';
44
import '@testing-library/jest-dom/extend-expect';
55
import { IntlProvider } from '@edx/frontend-platform/i18n';
66
import { AppContext } from '@edx/frontend-platform/react';
77
import StudioFooter from './StudioFooter';
8-
import messages from './messages';
98

109
const config = {
1110
LMS_BASE_URL: process.env.LMS_BASE_URL,
@@ -39,82 +38,11 @@ describe('Footer', () => {
3938
jest.clearAllMocks();
4039
currentConfig = config;
4140
});
42-
describe('help section default view', () => {
43-
it('help button should read Looking for help with Studio?', () => {
44-
render(<Component />);
45-
expect(screen.getByText(messages.openHelpButtonLabel.defaultMessage))
46-
.toBeVisible();
47-
});
48-
it('help button link row should not be visible', () => {
49-
render(<Component />);
50-
expect(screen.queryByTestId('helpButtonRow')).toBeNull();
51-
});
52-
});
53-
describe('help section expanded view', () => {
54-
it('help button should read Hide Studio help', () => {
55-
render(<Component />);
56-
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
57-
fireEvent.click(helpToggleButton);
58-
expect(screen.getByText(messages.closeHelpButtonLabel.defaultMessage))
59-
.toBeVisible();
60-
});
61-
it('help button link row should be visible', () => {
62-
render(<Component />);
63-
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
64-
fireEvent.click(helpToggleButton);
65-
expect(screen.getByTestId('helpButtonRow')).toBeVisible();
66-
});
67-
it('edX portal button should be visible', () => {
68-
render(<Component />);
69-
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
70-
fireEvent.click(helpToggleButton);
71-
expect(screen.queryByTestId('edXPortalButton')).toBeVisible();
72-
});
73-
it('should not show contact us button', () => {
74-
render(<Component />);
75-
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
76-
fireEvent.click(helpToggleButton);
77-
expect(screen.queryByTestId('contactUsButton')).toBeNull();
78-
});
79-
it('should show contact us button', () => {
80-
currentConfig = { ...config, SUPPORT_EMAIL: '[email protected]' };
81-
render(<Component />);
82-
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
83-
fireEvent.click(helpToggleButton);
84-
expect(screen.getByTestId('contactUsButton')).toBeVisible();
85-
});
86-
});
87-
describe('policy link row', () => {
88-
it('should only show LMS link', () => {
89-
render(<Component />);
90-
expect(screen.getByText('LMS')).toBeVisible();
91-
expect(screen.queryByTestId('termsOfService')).toBeNull();
92-
expect(screen.queryByTestId('privacyPolicy')).toBeNull();
93-
expect(screen.queryByTestId('accessibilityRequest')).toBeNull();
94-
});
95-
it('should show terms of service link', () => {
96-
currentConfig = { ...config, TERMS_OF_SERVICE_URL: 'termsofserviceurl' };
41+
describe('should show all links', () => {
42+
it('should only show all link', () => {
9743
render(<Component />);
9844
expect(screen.getByText('LMS')).toBeVisible();
9945
expect(screen.queryByTestId('termsOfService')).toBeVisible();
100-
expect(screen.queryByTestId('privacyPolicy')).toBeNull();
101-
expect(screen.queryByTestId('accessibilityRequest')).toBeNull();
102-
});
103-
it('should show privacy policy link', () => {
104-
currentConfig = { ...config, PRIVACY_POLICY_URL: 'privacypolicyurl' };
105-
render(<Component />);
106-
expect(screen.getByText('LMS')).toBeVisible();
107-
expect(screen.queryByTestId('termsOfService')).toBeNull();
108-
expect(screen.queryByTestId('privacyPolicy')).toBeVisible();
109-
expect(screen.queryByTestId('accessibilityRequest')).toBeNull();
110-
});
111-
it('should show accessibilty request link', () => {
112-
currentConfig = { ...config, SHOW_ACCESSIBILITY_PAGE: 'true' };
113-
render(<Component />);
114-
expect(screen.getByText('LMS')).toBeVisible();
115-
expect(screen.queryByTestId('termsOfService')).toBeNull();
116-
expect(screen.queryByTestId('privacyPolicy')).toBeNull();
117-
expect(screen.queryByTestId('accessibilityRequest')).toBeVisible();
11846
});
11947
});
12048
});

0 commit comments

Comments
 (0)