Compare commits

...

7 Commits

Author SHA1 Message Date
Robert Raposa
66f6832887 Merge pull request #9 from edx/robrap/ARCH-460-i18n
ARCH-460: feat: initial localization of footer
2019-03-20 13:16:42 -04:00
Robert Raposa
8f35487558 feat: initial localization of footer
BREAKING CHANGE: Now requires containing app to use `react-intl`. See
update to README for details.

ARCH-460
2019-03-20 12:37:11 -04:00
Adam Butterworth
349a458617 Merge pull request #8 from edx/abutterworth/unique-ids
fix: add unique ids to Icon components
2019-03-08 14:00:06 -05:00
Adam Butterworth
abe3d03f58 fix: update snapshot to reflect new ids 2019-03-08 12:44:00 -05:00
Adam Butterworth
7af4b71faa fix: add unique ids to Icon components 2019-03-08 12:35:35 -05:00
Robert Raposa
7f2c5b32b1 Merge pull request #7 from edx/robrap/update-readme
Update README.rst
2019-03-08 09:44:26 -05:00
Robert Raposa
6e0f9cf30a fix(docs): update README.rst
Add a note to clarify that this component is not really
a flexible site footer at this time.
2019-03-06 09:36:43 -05:00
14 changed files with 881 additions and 247 deletions

View File

@@ -1,43 +1,26 @@
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "ie 11"]
}
}],
"babel-preset-react"
],
"plugins": [
"transform-object-rest-spread"
],
"env": {
"development": {
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "ie 11"]
}
}],
"babel-preset-react"
],
"plugins": [
"transform-object-rest-spread"
]
},
"test": {
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "ie 11"]
}
}],
"babel-preset-react"
],
"plugins": [
"rewire",
"transform-object-rest-spread"
"rewire"
]
},
"production": {
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "ie 11"]
}
}],
"babel-preset-react"
],
"i18n": {
"plugins": [
"transform-object-rest-spread"
["react-intl", {
"messagesDir": "./temp"
}]
]
}
}

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
coverage
dist
node_modules
temp
.idea/

8
.tx/config Normal file
View File

@@ -0,0 +1,8 @@
[main]
host = https://www.transifex.com
[edx-platform.frontend-component-footer]
file_filter = "src/i18n/messages/<lang>.json"
source_file = "src/i18n/transifex_input.json"
source_lang = en
type = KEYVALUEJSON

36
Makefile Executable file
View File

@@ -0,0 +1,36 @@
extract_translations: ## no prerequisites so we can control order of operations
echo "We have to define this target due to tooling assumptions"
echo "Also we have to npm install using this hook b/c there's no other place for it in the current setup"
npm install
npm run-script i18n_extract
i18n.extract:
# Pulling display strings from .jsx files into .json files...
npm run-script i18n_extract
i18n.concat:
# Gathering JSON messages into one file...
./src/i18n/i18n-concat.js ./temp/src ./src/i18n/transifex_input.json
i18n.pre_validate: | i18n.extract i18n.concat
git diff --exit-code ./src/i18n/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/frontend-component-footer/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/frontend-component-footer/source/
# push translations to Transifex, doing magic so we can include the translator comments
push_translations: | i18n.extract
# Adding translator comments...
# Fetching strings from Transifex...
./node_modules/reactifex/bash_scripts/get_hashed_strings.sh $(tx_url1)
# Writing out comments to file...
./src/i18n/i18n-concat.js ./temp/src --comments
# Adding comments to Transifex...
./node_modules/reactifex/bash_scripts/put_comments.sh $(tx_url2)
# pull translations from Transifex
pull_translations: ## must be exactly this name for edx tooling support, see ecommerce-scripts/transifex/pull.py
tx pull -f --mode reviewed --language="ar,fr,es_419,zh_CN"
validate-no-uncommitted-package-lock-changes:
git diff --exit-code package-lock.json

View File

@@ -7,6 +7,8 @@ frontend-component-footer
frontend-component-footer is a library containing a site footer
component for use when building edX frontend applications.
At this time, this component is hard-coded to match the legacy LMS site footer, including all of its links. As implemented, this component should probably be called the ``frontend-component-lms-footer``.
Usage
-----
@@ -15,9 +17,14 @@ To install frontend-component-footer into your project::
npm i --save @edx/frontend-component-footer
The component expects properties specifying the various URLs that are
linked in the footer. See the sample app in src/index.jsx for an example
linked in the footer. See the sample app in `src/index.jsx <src/index.jsx>`__ for an example
of how the SiteFooter component can be specified.
Requirements
------------
This component uses ``react-intl``. Any containing app must provide ``react-intl`` as a peer dependency, and be wrapped inside an ``IntlProvider`` element, whether or not your consuming application is actually localized. For a basic default locale (English) version, follow the ``IntlProvider`` example in the sample application in `src/index.jsx <src/index.jsx>`__.
Development
-----------

83
package-lock.json generated
View File

@@ -914,6 +914,23 @@
}
}
},
"@babel/runtime": {
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz",
"integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.12.0"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==",
"dev": true
}
}
},
"@babel/template": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz",
@@ -3033,6 +3050,17 @@
"integrity": "sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=",
"dev": true
},
"babel-plugin-react-intl": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-3.0.1.tgz",
"integrity": "sha512-FqnEO+Tq7kJVUPKsSG3s5jaHi3pAC4RUR11IrscvjsfkOApLP2DtzNo6dtQ+tX+OzEzJx7cUms8aCw5BFyW5xg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.0.0",
"intl-messageformat-parser": "^1.2.0",
"mkdirp": "^0.5.1"
}
},
"babel-plugin-rewire": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz",
@@ -9906,6 +9934,12 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
"dev": true
},
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -10987,6 +11021,36 @@
"integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
"dev": true
},
"intl-format-cache": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.1.0.tgz",
"integrity": "sha1-BKNp/sv61tpgBbrh8UMzMy3PkxY=",
"dev": true
},
"intl-messageformat": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz",
"integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=",
"dev": true,
"requires": {
"intl-messageformat-parser": "1.4.0"
}
},
"intl-messageformat-parser": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz",
"integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=",
"dev": true
},
"intl-relativeformat": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz",
"integrity": "sha1-AQ8RBYAiUfQKxH0OPhogE0iiVd8=",
"dev": true,
"requires": {
"intl-messageformat": "^2.0.0"
}
},
"into-stream": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
@@ -20413,6 +20477,19 @@
"integrity": "sha512-xXUbDAZkU08aAkjtUvldqbvI04ogv+a1XdHxvYuHPYKIVk/42BIOD0zSKTHAWV4+gDy3yGm283z2072rA2gdtw==",
"dev": true
},
"react-intl": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.8.0.tgz",
"integrity": "sha512-1cSasNkHxZOXYYhms9Q1tSEWF8AWZQNq3nPLB/j8mYV0ZTSt2DhGQXHfKrKQMu4cgj9J1Crqg7xFPICTBgzqtQ==",
"dev": true,
"requires": {
"hoist-non-react-statics": "^2.5.5",
"intl-format-cache": "^2.0.5",
"intl-messageformat": "^2.1.0",
"intl-relativeformat": "^2.1.0",
"invariant": "^2.1.1"
}
},
"react-is": {
"version": "16.7.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.7.0.tgz",
@@ -20448,6 +20525,12 @@
"scheduler": "^0.12.0"
}
},
"reactifex": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/reactifex/-/reactifex-1.1.1.tgz",
"integrity": "sha512-HH2N/b5tRxh7ypIgCRsiBl/CTxRkTEPf9DhIstaM6hne4WiwM5/bBbWuvVlRZc/i3FdqZED3pZ//6n4mtxma4w==",
"dev": true
},
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",

View File

@@ -12,6 +12,7 @@
"gc": "commit",
"commitmsg": "commitlint -e $GIT_PARAMS",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"i18n_extract": "BABEL_ENV=i18n babel src --quiet > /dev/null",
"lint": "eslint --ext .js --ext .jsx .",
"precommit": "npm run lint",
"prepublishOnly": "npm run build",
@@ -37,9 +38,11 @@
"@commitlint/config-angular": "^6.0.2",
"@commitlint/prompt": "^6.0.2",
"@commitlint/prompt-cli": "^6.0.2",
"@edx/paragon": "^3.8.0",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-react-intl": "^3.0.1",
"babel-plugin-rewire": "^1.2.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.7.0",
@@ -47,23 +50,25 @@
"clean-webpack-plugin": "^0.1.19",
"coveralls": "^3.0.0",
"css-loader": "^0.28.9",
"@edx/paragon": "^3.8.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"eslint": "^5.2.0",
"eslint-config-edx": "^4.0.4",
"eslint-plugin-jsx-a11y": "^6.1.2",
"file-loader": "^1.1.9",
"glob": "^7.1.3",
"html-webpack-plugin": "^3.2.0",
"image-webpack-loader": "^4.2.0",
"husky": "^0.14.3",
"image-webpack-loader": "^4.2.0",
"jest": "23.6.0",
"node-sass": "^4.7.2",
"prop-types": "^15.5.10",
"react": "^16.4.2",
"react-dom": "^16.2.0",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.2.0",
"react-intl": "^2.8.0",
"react-test-renderer": "^16.6.0",
"reactifex": "^1.1.1",
"sass-loader": "^6.0.6",
"semantic-release": "^15.1.7",
"source-map-loader": "^0.2.1",
@@ -82,6 +87,7 @@
"prop-types": "^15.5.10",
"react": "^16.4.2",
"react-dom": "^16.2.0",
"react-intl": "2.x",
"webpack": "^4.19.1",
"webpack-merge": "^4.2.1"
},

57
src/i18n/i18n-concat.js Executable file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/env node
/**
* This code originally came from https://github.com/efischer19/reactifex/blob/master/main.js,
* which should be edx/reactifex. It is temporarily being copied here until we find it a new home.
*/
// NOTE: This script is called from Jenkins using devDependencies, so eslint is being
// disabled so it doesn't force you to make these real dependencies.
const fs = require('fs'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('glob'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path'); // eslint-disable-line import/no-extraneous-dependencies
// Expected input: a directory, possibly containing subdirectories, with .json files. Each .json
// file is an array of translation triplets (id, description, defaultMessage).
function gatherJson(dir) {
const ret = [];
const files = glob.sync(`${dir}/**/*.json`);
files.forEach((filename) => {
const messages = JSON.parse(fs.readFileSync(filename));
ret.push(...messages);
});
return ret;
}
const jsonDir = process.argv[2];
const messageObjects = gatherJson(jsonDir);
if (process.argv[3] === '--comments') { // prepare to handle the translator notes
const thisFile = path.basename(`${__filename}`);
const bashScriptsPath = './node_modules/reactifex/bash_scripts';
process.stdout.write(`${thisFile}: generating bash scripts...\n`);
process.stdout.write(`${thisFile}: info file at ${bashScriptsPath}/hashmap.json\n`);
const messageInfo = JSON.parse(fs.readFileSync(`${bashScriptsPath}/hashmap.json`));
const dataPath = `${bashScriptsPath}/hashed_data.txt`;
process.stdout.write(`${thisFile}: data path is ${dataPath}\n`);
fs.writeFileSync(dataPath, '');
messageObjects.forEach((message) => {
const info = messageInfo.find(mi => mi.key === message.id);
if (info) {
fs.appendFileSync(dataPath, `${info.string_hash}|${message.description}\n`);
} else {
process.stdout.write(`${thisFile}: string ${message.id} does not yet exist on transifex!\n`);
}
});
} else {
const output = {};
messageObjects.forEach((message) => {
output[message.id] = message.defaultMessage;
});
fs.writeFileSync(process.argv[3], JSON.stringify(output, null, 2));
}

View File

@@ -0,0 +1,39 @@
{
"footer.site-footer.link.about": "About",
"footer.site-footer.link.business": "{siteName} for Business",
"footer.site-footer.link.affiliates": "Affiliates",
"footer.site-footer.link.open-source": "Open {siteName}",
"footer.site-footer.link.careers": "Careers",
"footer.site-footer.link.news": "News",
"footer.site-footer.link.header.legal": "Legal",
"footer.site-footer.link.terms-of-service": "Terms of Service & Honor Code",
"footer.site-footer.link.privacy": "Privacy Policy",
"footer.site-footer.link.accessibility": "Accessibility Policy",
"footer.site-footer.link.trademark": "Trademark Policy",
"footer.site-footer.link.sitemap": "Sitemap",
"footer.site-footer.link.header.connect": "Connect",
"footer.site-footer.link.blog": "Blog",
"footer.site-footer.link.contact-us": "Contact Us",
"footer.site-footer.link.help-center": "Help Center",
"footer.site-footer.link.media-kit": "Media Kit",
"footer.site-footer.link.donate": "Donate",
"footer.site-footer.copyright-text": "{copyrightSymbol} {startDate}{endDate} {siteName} Inc.",
"footer.site-footer.trademark-text": "EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | {icpLicense}",
"footer.site-footer.site-logo.alt-text": "{siteName} logo",
"footer.site-footer.site-logo.aria-label": "{siteName} Home",
"footer.site-footer.facebook.title": "Facebook",
"footer.site-footer.facebook.screen-reader-text": "Like {siteName} on Facebook",
"footer.site-footer.twitter.title": "Twitter",
"footer.site-footer.twitter.screen-reader-text": "Follow {siteName} on Twitter",
"footer.site-footer.youtube.title": "Youtube",
"footer.site-footer.youtube.screen-reader-text": "Subscribe to the {siteName} YouTube channel",
"footer.site-footer.linkedin.title": "LinkedIn",
"footer.site-footer.linkedin.screen-reader-text": "Follow {siteName} on LinkedIn",
"footer.site-footer.google-plus.title": "Google+",
"footer.site-footer.google-plus.screen-reader-text": "Follow {siteName} on Google+",
"footer.site-footer.reddit.title": "Reddit",
"footer.site-footer.reddit.screen-reader-text": "Subscribe to the {siteName} subreddit",
"footer.site-footer.apple-app-store.alt-text": "Download the {siteName} mobile app from the Apple App Store",
"footer.site-footer.google-play.alt-text": "Download the {siteName} mobile app from Google Play",
"footer.site-footer.footer.aria-label": "Page Footer"
}

View File

@@ -1,29 +1,33 @@
import React from 'react';
import { render } from 'react-dom';
import { IntlProvider } from 'react-intl';
import SiteFooter from './lib';
import './index.scss';
import FooterLogo from './edx-footer.png';
const App = () => (
<SiteFooter
siteName="edX"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
/>
<IntlProvider locale="en">
<SiteFooter
siteName="edX"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
handleAllTrackEvents={() => {}}
/>
</IntlProvider>
);
render(<App />, document.getElementById('root'));

View File

@@ -0,0 +1,91 @@
import { defineMessages } from 'react-intl';
const messages = defineMessages({
'footer.site-footer.site-logo.alt-text': {
id: 'footer.site-footer.site-logo.alt-text',
defaultMessage: '{siteName} logo',
description: 'The alt description of the site logo',
},
'footer.site-footer.site-logo.aria-label': {
id: 'footer.site-footer.site-logo.aria-label',
defaultMessage: '{siteName} Home',
description: 'Aria label for the site logo which goes to the marketing site',
},
'footer.site-footer.facebook.title': {
id: 'footer.site-footer.facebook.title',
defaultMessage: 'Facebook',
description: 'Facebook button title',
},
'footer.site-footer.facebook.screen-reader-text': {
id: 'footer.site-footer.facebook.screen-reader-text',
defaultMessage: 'Like {siteName} on Facebook',
description: 'Facebook button screen reader text',
},
'footer.site-footer.twitter.title': {
id: 'footer.site-footer.twitter.title',
defaultMessage: 'Twitter',
description: 'Twitter button title',
},
'footer.site-footer.twitter.screen-reader-text': {
id: 'footer.site-footer.twitter.screen-reader-text',
defaultMessage: 'Follow {siteName} on Twitter',
description: 'Twitter button screen reader text',
},
'footer.site-footer.youtube.title': {
id: 'footer.site-footer.youtube.title',
defaultMessage: 'Youtube',
description: 'Youtube button title',
},
'footer.site-footer.youtube.screen-reader-text': {
id: 'footer.site-footer.youtube.screen-reader-text',
defaultMessage: 'Subscribe to the {siteName} YouTube channel',
description: 'Youtube button screen reader text',
},
'footer.site-footer.linkedin.title': {
id: 'footer.site-footer.linkedin.title',
defaultMessage: 'LinkedIn',
description: 'LinkedIn button title',
},
'footer.site-footer.linkedin.screen-reader-text': {
id: 'footer.site-footer.linkedin.screen-reader-text',
defaultMessage: 'Follow {siteName} on LinkedIn',
description: 'LinkedIn button screen reader text',
},
'footer.site-footer.google-plus.title': {
id: 'footer.site-footer.google-plus.title',
defaultMessage: 'Google+',
description: 'Google+ button title',
},
'footer.site-footer.google-plus.screen-reader-text': {
id: 'footer.site-footer.google-plus.screen-reader-text',
defaultMessage: 'Follow {siteName} on Google+',
description: 'Google+ button screen reader text',
},
'footer.site-footer.reddit.title': {
id: 'footer.site-footer.reddit.title',
defaultMessage: 'Reddit',
description: 'Reddit button title',
},
'footer.site-footer.reddit.screen-reader-text': {
id: 'footer.site-footer.reddit.screen-reader-text',
defaultMessage: 'Subscribe to the {siteName} subreddit',
description: 'Reddit button screen reader text',
},
'footer.site-footer.apple-app-store.alt-text': {
id: 'footer.site-footer.apple-app-store.alt-text',
defaultMessage: 'Download the {siteName} mobile app from the Apple App Store',
description: 'Apple App Store button alt description',
},
'footer.site-footer.google-play.alt-text': {
id: 'footer.site-footer.google-play.alt-text',
defaultMessage: 'Download the {siteName} mobile app from Google Play',
description: 'Google Play button alt description',
},
'footer.site-footer.footer.aria-label': {
id: 'footer.site-footer.footer.aria-label',
defaultMessage: 'Page Footer',
description: 'Aria label for the footer',
},
});
export default messages;

View File

@@ -1,30 +1,35 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { mount } from 'enzyme';
import { IntlProvider } from 'react-intl';
import FooterLogo from '../../../edx-footer.png';
import SiteFooter, { EVENT_NAMES } from './index';
const completeSiteFooterComponent = mockHandleAllTrackEvents =>
(<SiteFooter
siteName="example"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
handleAllTrackEvents={mockHandleAllTrackEvents}
/>);
(
<IntlProvider locale="en">
<SiteFooter
siteName="example"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
handleAllTrackEvents={mockHandleAllTrackEvents}
/>
</IntlProvider>
);
describe('<SiteFooter />', () => {
describe('renders correctly', () => {
@@ -38,53 +43,59 @@ describe('<SiteFooter />', () => {
it('does not render social links', () => {
const tree = renderer
.create(<SiteFooter
siteName="example"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
handleAllTrackEvents={jest.fn()}
showSocialLinks={false}
/>)
.toJSON();
.create((
<IntlProvider locale="en">
<SiteFooter
siteName="example"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
handleAllTrackEvents={jest.fn()}
showSocialLinks={false}
/>
</IntlProvider>
)).toJSON();
expect(tree).toMatchSnapshot();
});
it('does not render mobile links', () => {
const tree = renderer
.create(<SiteFooter
siteName="example"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
handleAllTrackEvents={jest.fn()}
showMobileLinks={false}
/>)
.toJSON();
.create((
<IntlProvider locale="en">
<SiteFooter
siteName="example"
siteLogo={FooterLogo}
marketingSiteBaseUrl="https://www.example.com"
supportUrl="https://www.example.com/support"
contactUrl="https://www.example.com/contact"
openSourceUrl="https://www.example.com/open"
termsOfServiceUrl="https://www.example.com/terms-of-service"
privacyPolicyUrl="https://www.example.com/privacy-policy"
facebookUrl="https://www.facebook.com"
twitterUrl="https://www.twitter.com"
youTubeUrl="https://www.youtube.com"
linkedInUrl="https://www.linkedin.com"
googlePlusUrl="https://plus.google.com"
redditUrl="https://reddit.com"
appleAppStoreUrl="https://store.apple.com"
googlePlayUrl="https://play.google.com"
handleAllTrackEvents={jest.fn()}
showMobileLinks={false}
/>
</IntlProvider>
)).toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@@ -37,44 +37,54 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<a
href="https://www.example.com/about-us"
>
About
<span>
About
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/enterprise"
>
example
for Business
<span>
example for Business
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/affiliate-program"
>
Affiliates
<span>
Affiliates
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/open"
>
Open
example
<span>
Open example
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/careers"
>
Careers
<span>
Careers
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/news-announcements"
>
News
<span>
News
</span>
</a>
</li>
</ul>
@@ -83,7 +93,9 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
className="area-3"
>
<h2>
Legal
<span>
Legal
</span>
</h2>
<ul
className="list-unstyled p-0 m-0"
@@ -92,35 +104,45 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<a
href="https://www.example.com/terms-of-service"
>
Terms of Service & Honor Code
<span>
Terms of Service & Honor Code
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/privacy-policy"
>
Privacy Policy
<span>
Privacy Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/accessibility"
>
Accessibility Policy
<span>
Accessibility Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/trademarks"
>
Trademark Policy
<span>
Trademark Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/sitemap"
>
Sitemap
<span>
Sitemap
</span>
</a>
</li>
</ul>
@@ -129,7 +151,9 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
className="area-4"
>
<h2>
Connect
<span>
Connect
</span>
</h2>
<ul
className="list-unstyled p-0 m-0"
@@ -138,35 +162,45 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<a
href="https://www.example.com/blog"
>
Blog
<span>
Blog
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/contact"
>
Contact Us
<span>
Contact Us
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/support"
>
Help Center
<span>
Help Center
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/media-kit"
>
Media Kit
<span>
Media Kit
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/donate"
>
Donate
<span>
Donate
</span>
</a>
</li>
</ul>
@@ -188,7 +222,7 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<span
aria-hidden={true}
className="fa fa-facebook-square fa-2x"
id="Icon1"
id="edx-footer-icon-facebook"
/>
<span
className="sr-only"
@@ -208,7 +242,7 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<span
aria-hidden={true}
className="fa fa-twitter-square fa-2x"
id="Icon1"
id="edx-footer-icon-twitter"
/>
<span
className="sr-only"
@@ -228,7 +262,7 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<span
aria-hidden={true}
className="fa fa-youtube-square fa-2x"
id="Icon1"
id="edx-footer-icon-youtube"
/>
<span
className="sr-only"
@@ -248,7 +282,7 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<span
aria-hidden={true}
className="fa fa-linkedin-square fa-2x"
id="Icon1"
id="edx-footer-icon-linkedin"
/>
<span
className="sr-only"
@@ -268,7 +302,7 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<span
aria-hidden={true}
className="fa fa-google-plus-square fa-2x"
id="Icon1"
id="edx-footer-icon-google"
/>
<span
className="sr-only"
@@ -288,7 +322,7 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<span
aria-hidden={true}
className="fa fa-reddit-square fa-2x"
id="Icon1"
id="edx-footer-icon-reddit"
/>
<span
className="sr-only"
@@ -299,13 +333,13 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
</li>
</ul>
<p>
© 2012
2019
example
Inc.
<span>
© 20122019 example Inc.
</span>
<br />
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | 粤ICP备17044299号-2
<span>
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | 粤ICP备17044299号-2
</span>
</p>
</div>
</div>
@@ -349,44 +383,54 @@ exports[`<SiteFooter /> renders correctly does not render social links 1`] = `
<a
href="https://www.example.com/about-us"
>
About
<span>
About
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/enterprise"
>
example
for Business
<span>
example for Business
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/affiliate-program"
>
Affiliates
<span>
Affiliates
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/open"
>
Open
example
<span>
Open example
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/careers"
>
Careers
<span>
Careers
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/news-announcements"
>
News
<span>
News
</span>
</a>
</li>
</ul>
@@ -395,7 +439,9 @@ exports[`<SiteFooter /> renders correctly does not render social links 1`] = `
className="area-3"
>
<h2>
Legal
<span>
Legal
</span>
</h2>
<ul
className="list-unstyled p-0 m-0"
@@ -404,35 +450,45 @@ exports[`<SiteFooter /> renders correctly does not render social links 1`] = `
<a
href="https://www.example.com/terms-of-service"
>
Terms of Service & Honor Code
<span>
Terms of Service & Honor Code
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/privacy-policy"
>
Privacy Policy
<span>
Privacy Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/accessibility"
>
Accessibility Policy
<span>
Accessibility Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/trademarks"
>
Trademark Policy
<span>
Trademark Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/sitemap"
>
Sitemap
<span>
Sitemap
</span>
</a>
</li>
</ul>
@@ -441,7 +497,9 @@ exports[`<SiteFooter /> renders correctly does not render social links 1`] = `
className="area-4"
>
<h2>
Connect
<span>
Connect
</span>
</h2>
<ul
className="list-unstyled p-0 m-0"
@@ -450,35 +508,45 @@ exports[`<SiteFooter /> renders correctly does not render social links 1`] = `
<a
href="https://www.example.com/blog"
>
Blog
<span>
Blog
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/contact"
>
Contact Us
<span>
Contact Us
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/support"
>
Help Center
<span>
Help Center
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/media-kit"
>
Media Kit
<span>
Media Kit
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/donate"
>
Donate
<span>
Donate
</span>
</a>
</li>
</ul>
@@ -519,13 +587,13 @@ exports[`<SiteFooter /> renders correctly does not render social links 1`] = `
</li>
</ul>
<p>
© 2012
2019
example
Inc.
<span>
© 20122019 example Inc.
</span>
<br />
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | 粤ICP备17044299号-2
<span>
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | 粤ICP备17044299号-2
</span>
</p>
</div>
</div>
@@ -569,44 +637,54 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<a
href="https://www.example.com/about-us"
>
About
<span>
About
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/enterprise"
>
example
for Business
<span>
example for Business
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/affiliate-program"
>
Affiliates
<span>
Affiliates
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/open"
>
Open
example
<span>
Open example
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/careers"
>
Careers
<span>
Careers
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/news-announcements"
>
News
<span>
News
</span>
</a>
</li>
</ul>
@@ -615,7 +693,9 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
className="area-3"
>
<h2>
Legal
<span>
Legal
</span>
</h2>
<ul
className="list-unstyled p-0 m-0"
@@ -624,35 +704,45 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<a
href="https://www.example.com/terms-of-service"
>
Terms of Service & Honor Code
<span>
Terms of Service & Honor Code
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/privacy-policy"
>
Privacy Policy
<span>
Privacy Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/accessibility"
>
Accessibility Policy
<span>
Accessibility Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/trademarks"
>
Trademark Policy
<span>
Trademark Policy
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/sitemap"
>
Sitemap
<span>
Sitemap
</span>
</a>
</li>
</ul>
@@ -661,7 +751,9 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
className="area-4"
>
<h2>
Connect
<span>
Connect
</span>
</h2>
<ul
className="list-unstyled p-0 m-0"
@@ -670,35 +762,45 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<a
href="https://www.example.com/blog"
>
Blog
<span>
Blog
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/contact"
>
Contact Us
<span>
Contact Us
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/support"
>
Help Center
<span>
Help Center
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/media-kit"
>
Media Kit
<span>
Media Kit
</span>
</a>
</li>
<li>
<a
href="https://www.example.com/donate"
>
Donate
<span>
Donate
</span>
</a>
</li>
</ul>
@@ -720,7 +822,7 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<span
aria-hidden={true}
className="fa fa-facebook-square fa-2x"
id="Icon1"
id="edx-footer-icon-facebook"
/>
<span
className="sr-only"
@@ -740,7 +842,7 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<span
aria-hidden={true}
className="fa fa-twitter-square fa-2x"
id="Icon1"
id="edx-footer-icon-twitter"
/>
<span
className="sr-only"
@@ -760,7 +862,7 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<span
aria-hidden={true}
className="fa fa-youtube-square fa-2x"
id="Icon1"
id="edx-footer-icon-youtube"
/>
<span
className="sr-only"
@@ -780,7 +882,7 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<span
aria-hidden={true}
className="fa fa-linkedin-square fa-2x"
id="Icon1"
id="edx-footer-icon-linkedin"
/>
<span
className="sr-only"
@@ -800,7 +902,7 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<span
aria-hidden={true}
className="fa fa-google-plus-square fa-2x"
id="Icon1"
id="edx-footer-icon-google"
/>
<span
className="sr-only"
@@ -820,7 +922,7 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<span
aria-hidden={true}
className="fa fa-reddit-square fa-2x"
id="Icon1"
id="edx-footer-icon-reddit"
/>
<span
className="sr-only"
@@ -863,13 +965,13 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
</li>
</ul>
<p>
© 2012
2019
example
Inc.
<span>
© 20122019 example Inc.
</span>
<br />
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | 粤ICP备17044299号-2
<span>
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | 粤ICP备17044299号-2
</span>
</p>
</div>
</div>

View File

@@ -1,6 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { Hyperlink, Icon } from '@edx/paragon';
import messages from './SiteFooter.messages';
const EVENT_NAMES = {
FOOTER_LINK: 'edx.bi.footer.link',
@@ -24,7 +26,13 @@ class SiteFooter extends React.Component {
renderSiteLogo() {
return (
<img src={this.props.siteLogo} alt={`${this.props.siteName} logo`} />
<img
src={this.props.siteLogo}
alt={this.props.intl.formatMessage(
messages['footer.site-footer.site-logo.alt-text'],
{ siteName: this.props.siteName },
)}
/>
);
}
@@ -34,6 +42,7 @@ class SiteFooter extends React.Component {
renderSocialLinks() {
const {
intl,
siteName,
showSocialLinks,
facebookUrl,
@@ -54,69 +63,109 @@ class SiteFooter extends React.Component {
<li>
<a
href={facebookUrl}
title="Facebook"
title={intl.formatMessage(messages['footer.site-footer.facebook.title'])}
rel="noopener noreferrer"
target="_blank"
onClick={this.externalLinkClickHandler}
>
<Icon className={['fa', 'fa-facebook-square', 'fa-2x']} screenReaderText={`Like ${siteName} on Facebook`} />
<Icon
id="edx-footer-icon-facebook"
className={['fa', 'fa-facebook-square', 'fa-2x']}
screenReaderText={intl.formatMessage(
messages['footer.site-footer.facebook.screen-reader-text'],
{ siteName },
)}
/>
</a>
</li>
<li>
<a
href={twitterUrl}
title="Twitter"
title={intl.formatMessage(messages['footer.site-footer.twitter.title'])}
rel="noopener noreferrer"
target="_blank"
onClick={this.externalLinkClickHandler}
>
<Icon className={['fa', 'fa-twitter-square', 'fa-2x']} screenReaderText={`Follow ${siteName} on Twitter`} />
<Icon
id="edx-footer-icon-twitter"
className={['fa', 'fa-twitter-square', 'fa-2x']}
screenReaderText={intl.formatMessage(
messages['footer.site-footer.twitter.screen-reader-text'],
{ siteName },
)}
/>
</a>
</li>
<li>
<a
href={youTubeUrl}
title="Youtube"
title={intl.formatMessage(messages['footer.site-footer.youtube.title'])}
rel="noopener noreferrer"
target="_blank"
onClick={this.externalLinkClickHandler}
>
<Icon className={['fa', 'fa-youtube-square', 'fa-2x']} screenReaderText={`Subscribe to the ${siteName} YouTube channel`} />
<Icon
id="edx-footer-icon-youtube"
className={['fa', 'fa-youtube-square', 'fa-2x']}
screenReaderText={intl.formatMessage(
messages['footer.site-footer.youtube.screen-reader-text'],
{ siteName },
)}
/>
</a>
</li>
<li>
<a
href={linkedInUrl}
title="LinkedIn"
title={intl.formatMessage(messages['footer.site-footer.linkedin.title'])}
rel="noopener noreferrer"
target="_blank"
onClick={this.externalLinkClickHandler}
>
<Icon className={['fa', 'fa-linkedin-square', 'fa-2x']} screenReaderText={`Follow ${siteName} on LinkedIn`} />
<Icon
id="edx-footer-icon-linkedin"
className={['fa', 'fa-linkedin-square', 'fa-2x']}
screenReaderText={intl.formatMessage(
messages['footer.site-footer.linkedin.screen-reader-text'],
{ siteName },
)}
/>
</a>
</li>
<li>
<a
href={googlePlusUrl}
title="Google+"
title={intl.formatMessage(messages['footer.site-footer.google-plus.title'])}
rel="noopener noreferrer"
target="_blank"
onClick={this.externalLinkClickHandler}
>
<Icon className={['fa', 'fa-google-plus-square', 'fa-2x']} screenReaderText={`Follow ${siteName} on Google+`} />
<Icon
id="edx-footer-icon-google"
className={['fa', 'fa-google-plus-square', 'fa-2x']}
screenReaderText={intl.formatMessage(
messages['footer.site-footer.google-plus.screen-reader-text'],
{ siteName },
)}
/>
</a>
</li>
<li>
<a
href={redditUrl}
title="Reddit"
title={intl.formatMessage(messages['footer.site-footer.reddit.title'])}
rel="noopener noreferrer"
target="_blank"
onClick={this.externalLinkClickHandler}
>
<Icon className={['fa', 'fa-reddit-square', 'fa-2x']} screenReaderText={`Subscribe to the ${siteName} subreddit`} />
<Icon
id="edx-footer-icon-reddit"
className={['fa', 'fa-reddit-square', 'fa-2x']}
screenReaderText={intl.formatMessage(
messages['footer.site-footer.reddit.screen-reader-text'],
{ siteName },
)}
/>
</a>
</li>
</ul>
@@ -127,6 +176,7 @@ class SiteFooter extends React.Component {
renderMobileLinks() {
const {
intl,
siteName,
showMobileLinks,
appleAppStoreUrl,
@@ -140,7 +190,10 @@ class SiteFooter extends React.Component {
<a href={appleAppStoreUrl} rel="noopener noreferrer" target="_blank" onClick={this.externalLinkClickHandler}>
<img
className="max-height-39"
alt={`Download the ${siteName} mobile app from the Apple App Store`}
alt={intl.formatMessage(
messages['footer.site-footer.apple-app-store.alt-text'],
{ siteName },
)}
src="https://prod-edxapp.edx-cdn.org/static/images/app/app_store_badge_135x40.d0558d910630.svg"
/>
</a>
@@ -149,7 +202,10 @@ class SiteFooter extends React.Component {
<a href={googlePlayUrl} rel="noopener noreferrer" target="_blank" onClick={this.externalLinkClickHandler}>
<img
className="max-height-39"
alt={`Download the ${siteName} mobile app from Google Play`}
alt={intl.formatMessage(
messages['footer.site-footer.google-play.alt-text'],
{ siteName },
)}
src="https://prod-edxapp.edx-cdn.org/static/images/app/google_play_badge_45.6ea466e328da.png"
/>
</a>
@@ -162,6 +218,7 @@ class SiteFooter extends React.Component {
render() {
const {
intl,
siteName,
openSourceUrl,
termsOfServiceUrl,
@@ -172,52 +229,200 @@ class SiteFooter extends React.Component {
return (
<footer
role="contentinfo"
aria-label="Page Footer"
aria-label={intl.formatMessage(messages['footer.site-footer.footer.aria-label'])}
className="footer d-flex justify-content-center border-top py-3 px-4"
>
<div className="max-width-1180 d-grid">
<div className="area-1">
<Hyperlink destination={this.renderMarketingSiteUrl('/')} content={this.renderSiteLogo()} aria-label={`${siteName} Home`} />
<Hyperlink
destination={this.renderMarketingSiteUrl('/')}
content={this.renderSiteLogo()}
aria-label={intl.formatMessage(
messages['footer.site-footer.site-logo.aria-label'],
{ siteName },
)}
/>
</div>
<div className="area-2">
<h2>{siteName}</h2>
<ul className="list-unstyled p-0 m-0">
<li><a href={this.renderMarketingSiteUrl('/about-us')}>About</a></li>
<li><a href={this.renderMarketingSiteUrl('/enterprise')}>{siteName} for Business</a></li>
<li><a href={this.renderMarketingSiteUrl('/affiliate-program')}>Affiliates</a></li>
<li><a href={openSourceUrl}>Open {siteName}</a></li>
<li><a href={this.renderMarketingSiteUrl('/careers')}>Careers</a></li>
<li><a href={this.renderMarketingSiteUrl('/news-announcements')}>News</a></li>
<li>
<a href={this.renderMarketingSiteUrl('/about-us')}>
<FormattedMessage
id="footer.site-footer.link.about"
defaultMessage="About"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/enterprise')}>
<FormattedMessage
id="footer.site-footer.link.business"
defaultMessage="{siteName} for Business"
values={{ siteName }}
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/affiliate-program')}>
<FormattedMessage
id="footer.site-footer.link.affiliates"
defaultMessage="Affiliates"
/>
</a>
</li>
<li>
<a href={openSourceUrl}>
<FormattedMessage
id="footer.site-footer.link.open-source"
defaultMessage="Open {siteName}"
values={{ siteName }}
description="Open Source link text"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/careers')}>
<FormattedMessage
id="footer.site-footer.link.careers"
defaultMessage="Careers"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/news-announcements')}>
<FormattedMessage
id="footer.site-footer.link.news"
defaultMessage="News"
/>
</a>
</li>
</ul>
</div>
<div className="area-3">
<h2>Legal</h2>
<h2>
<FormattedMessage
id="footer.site-footer.link.header.legal"
defaultMessage="Legal"
description="Header for legal links"
/>
</h2>
<ul className="list-unstyled p-0 m-0">
<li><a href={termsOfServiceUrl}>Terms of Service & Honor Code</a></li>
<li><a href={privacyPolicyUrl}>Privacy Policy</a></li>
<li><a href={this.renderMarketingSiteUrl('/accessibility')}>Accessibility Policy</a></li>
<li><a href={this.renderMarketingSiteUrl('/trademarks')}>Trademark Policy</a></li>
<li><a href={this.renderMarketingSiteUrl('/sitemap')}>Sitemap</a></li>
<li>
<a href={termsOfServiceUrl}>
<FormattedMessage
id="footer.site-footer.link.terms-of-service"
defaultMessage="Terms of Service & Honor Code"
/>
</a>
</li>
<li>
<a href={privacyPolicyUrl}>
<FormattedMessage
id="footer.site-footer.link.privacy"
defaultMessage="Privacy Policy"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/accessibility')}>
<FormattedMessage
id="footer.site-footer.link.accessibility"
defaultMessage="Accessibility Policy"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/trademarks')}>
<FormattedMessage
id="footer.site-footer.link.trademark"
defaultMessage="Trademark Policy"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/sitemap')}>
<FormattedMessage
id="footer.site-footer.link.sitemap"
defaultMessage="Sitemap"
/>
</a>
</li>
</ul>
</div>
<div className="area-4">
<h2>Connect</h2>
<h2>
<FormattedMessage
id="footer.site-footer.link.header.connect"
defaultMessage="Connect"
description="Header for connect links"
/>
</h2>
<ul className="list-unstyled p-0 m-0">
<li><a href={this.renderMarketingSiteUrl('/blog')}>Blog</a></li>
<li><a href={contactUrl}>Contact Us</a></li>
<li><a href={supportUrl}>Help Center</a></li>
<li><a href={this.renderMarketingSiteUrl('/media-kit')}>Media Kit</a></li>
<li><a href={this.renderMarketingSiteUrl('/donate')}>Donate</a></li>
<li>
<a href={this.renderMarketingSiteUrl('/blog')}>
<FormattedMessage
id="footer.site-footer.link.blog"
defaultMessage="Blog"
/>
</a>
</li>
<li>
<a href={contactUrl}>
<FormattedMessage
id="footer.site-footer.link.contact-us"
defaultMessage="Contact Us"
/>
</a>
</li>
<li>
<a href={supportUrl}>
<FormattedMessage
id="footer.site-footer.link.help-center"
defaultMessage="Help Center"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/media-kit')}>
<FormattedMessage
id="footer.site-footer.link.media-kit"
defaultMessage="Media Kit"
/>
</a>
</li>
<li>
<a href={this.renderMarketingSiteUrl('/donate')}>
<FormattedMessage
id="footer.site-footer.link.donate"
defaultMessage="Donate"
/>
</a>
</li>
</ul>
</div>
<div className="area-5">
{this.renderSocialLinks()}
{this.renderMobileLinks()}
<p>
© 2012{(new Date().getFullYear())} {siteName} Inc.
<FormattedMessage
id="footer.site-footer.copyright-text"
defaultMessage="{copyrightSymbol} {startDate}{endDate} {siteName} Inc."
values={{
copyrightSymbol: '©',
startDate: '2012',
endDate: `${new Date().getFullYear()}`,
siteName,
}}
description="Footer copyright text with copyright symbol and dates"
/>
<br />
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc.
| 粤ICP备17044299号-2
<FormattedMessage
id="footer.site-footer.trademark-text"
defaultMessage="EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | {icpLicense}"
values={{ icpLicense: '粤ICP备17044299号-2' }}
description="Footer trademark text"
/>
</p>
</div>
</div>
@@ -246,6 +451,7 @@ SiteFooter.propTypes = {
appleAppStoreUrl: PropTypes.string,
googlePlayUrl: PropTypes.string,
handleAllTrackEvents: PropTypes.func.isRequired,
intl: intlShape.isRequired,
};
SiteFooter.defaultProps = {
@@ -269,5 +475,5 @@ SiteFooter.defaultProps = {
googlePlayUrl: null,
};
export default SiteFooter;
export default injectIntl(SiteFooter);
export { EVENT_NAMES };