Compare commits

...

2 Commits

Author SHA1 Message Date
David Joy
9667a5cee4 fix: exporting logo and better proptypes 2019-07-03 15:42:48 -04:00
David Joy
9df6e12174 refactor: dev server, config, and babel the build
BREAKING CHANGE: Defensive breaking change as this drastically changes the repo and how it gets consumed.
2019-07-03 11:10:59 -04:00
43 changed files with 10113 additions and 16152 deletions

View File

@@ -1,28 +1,23 @@
{ {
"presets": [ "presets": [
["env", { [
"targets": { "@babel/preset-env",
"browsers": ["last 2 versions", "ie 11"] {
} "modules": false
}], },
"babel-preset-react" ],
"@babel/preset-react"
], ],
"plugins": [ "plugins": [
"transform-object-rest-spread" ["@babel/plugin-proposal-object-rest-spread", {}, "object-rest-spread"],
["@babel/plugin-proposal-class-properties", {}, "class-properties"]
], ],
"env": { "env": {
"test": { "test": {
"plugins": [ "presets": [
"rewire" "@babel/preset-env",
] "@babel/preset-react"
}, ],
"i18n": {
"plugins": [
["react-intl", {
"messagesDir": "./temp/babel-plugin-react-intl",
"moduleSourceName": "@edx/frontend-i18n"
}]
]
} }
} }
} }

View File

@@ -5,8 +5,7 @@
"error", "error",
{ {
"devDependencies": [ "devDependencies": [
"webpack.config.js", "src/lib/setupTest.js",
"src/tests/setupTest.js",
"**/*.test.jsx", "**/*.test.jsx",
"**/*.test.js" "**/*.test.js"
] ]

5
.gitignore vendored
View File

@@ -1,9 +1,6 @@
coverage coverage
dist dist
node_modules node_modules
temp
.idea/ .idea/
/.cache
src/i18n/transifex_input.json
temp/babel-plugin-react-intl

View File

@@ -8,10 +8,8 @@ install:
- npm install - npm install
script: script:
- npm run lint - npm run lint
- npm run i18n_extract
- npm run test - npm run test
- npm run build - npm run build
- npm run is-es5
after_success: after_success:
- npm run travis-deploy-once "npm run semantic-release" - npm run travis-deploy-once "npm run semantic-release"
- npm run coveralls - npm run coveralls

View File

@@ -1,8 +0,0 @@
[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

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"restructuredtext.confPath": ""
}

View File

@@ -1,50 +1,16 @@
transifex_resource = frontend-component-footer
transifex_langs = "ar,fr,es_419,zh_CN"
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
requirements: requirements:
npm install npm install
i18n.extract:
# Pulling display strings from .jsx files into .json files...
rm -rf $(transifex_temp)
npm run-script i18n_extract
i18n.concat:
# Gathering JSON messages into one file...
$(transifex_utils) $(transifex_temp) $(transifex_input)
extract_translations: | requirements i18n.extract i18n.concat
# Despite the name, we actually need this target to detect changes in the incoming translated message files as well.
detect_changed_source_translations:
# Checking for changed translations...
git diff --exit-code $(i18n)
# Pushes translations to Transifex. You must run make extract_translations first.
push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/reactifex/bash_scripts/get_hashed_strings.sh $(tx_url1)
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments
# Pushing comments to Transifex...
./node_modules/reactifex/bash_scripts/put_comments.sh $(tx_url2)
# Pulls translations from Transifex.
pull_translations:
tx pull -f --mode reviewed --language=$(transifex_langs)
# This target is used by Travis. # This target is used by Travis.
validate-no-uncommitted-package-lock-changes: validate-no-uncommitted-package-lock-changes:
# Checking for package-lock.json changes... # Checking for package-lock.json changes...
git diff --exit-code package-lock.json git diff --exit-code package-lock.json
build:
rm -rf ./dist
./node_modules/.bin/babel src --out-dir dist --source-maps --ignore **/*.test.jsx,**/*.stories.jsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files
rm -rf ./dist/example
rm -rf dist/**/*.test.jsx
rm -rf dist/**/__snapshots__
rm -rf dist/**/__mocks__
rm -rf dist/lib/setupTest.js

View File

@@ -4,10 +4,7 @@ frontend-component-footer
|Build Status| |Coveralls| |npm_version| |npm_downloads| |license| |Build Status| |Coveralls| |npm_version| |npm_downloads| |license|
|semantic-release| |semantic-release|
frontend-component-footer is a library containing a site footer frontend-component-footer is a library containing a site footer component for use when building edX frontend applications.
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 Usage
----- -----
@@ -17,13 +14,10 @@ To install frontend-component-footer into your project::
npm i --save @edx/frontend-component-footer npm i --save @edx/frontend-component-footer
The component expects properties specifying the various URLs that are The component expects properties specifying the various URLs that are
linked in the footer. See the sample app in `src/index.jsx <src/index.jsx>`__ for an example linked in the footer. See the sample app in `src/example/index.jsx <src/example/index.jsx>`__ for an example
of how the SiteFooter component can be specified. of how the SiteFooter component can be specified.
Requirements The distribution
------------
This component uses ``@edx/frontend-i18n``. Any containing app must provide ``@edx/frontend-i18n`` 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 Development
----------- -----------

View File

@@ -1,15 +0,0 @@
// This is the common Webpack config. The dev and prod Webpack configs both
// inherit config defined here.
const path = require('path');
module.exports = {
entry: {
app: path.resolve(__dirname, '../src/index.jsx'),
},
output: {
path: path.resolve(__dirname, '../dist'),
},
resolve: {
extensions: ['.js', '.jsx'],
},
};

View File

@@ -1,115 +0,0 @@
// This is the dev Webpack config. All settings here should prefer a fast build
// time at the expense of creating larger, unoptimized bundles.
const Merge = require('webpack-merge'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // eslint-disable-line import/no-extraneous-dependencies
const webpack = require('webpack'); // eslint-disable-line import/no-extraneous-dependencies
const commonConfig = require('./webpack.common.config.js');
module.exports = Merge.smart(commonConfig, {
mode: 'development',
entry: [
// enable react's custom hot dev client so we get errors reported in the browser
require.resolve('react-dev-utils/webpackHotDevClient'),
path.resolve(__dirname, '../src/index.jsx'),
],
module: {
// Specify file-by-file rules to Webpack. Some file-types need a particular kind of loader.
rules: [
// The babel-loader transforms newer ES2015+ syntax to older ES5 for older browsers.
// Babel is configured with the .babelrc file at the root of the project.
{
test: /\.(js|jsx)$/,
include: [
path.resolve(__dirname, '../src'),
],
loader: 'babel-loader',
options: {
// Caches result of loader to the filesystem. Future builds will attempt to read from the
// cache to avoid needing to run the expensive recompilation process on each run.
cacheDirectory: true,
},
},
// We are not extracting CSS from the javascript bundles in development because extracting
// prevents hot-reloading from working, it increases build time, and we don't care about
// flash-of-unstyled-content issues in development.
{
test: /(.scss|.css)$/,
use: [
'style-loader', // creates style nodes from JS strings
{
loader: 'css-loader', // translates CSS into CommonJS
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader', // compiles Sass to CSS
options: {
sourceMap: true,
includePaths: [
path.join(__dirname, '../node_modules'),
path.join(__dirname, '../src'),
],
},
},
],
},
// Webpack, by default, uses the url-loader for images and fonts that are required/included by
// files it processes, which just base64 encodes them and inlines them in the javascript
// bundles. This makes the javascript bundles ginormous and defeats caching so we will use the
// file-loader instead to copy the files directly to the output directory.
{
test: /\.(woff2?|ttf|svg|eot)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
},
{
test: /\.(jpe?g|png|gif|ico)(\?v=\d+\.\d+\.\d+)?$/,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
optimizationlevel: 7,
mozjpeg: {
progressive: true,
},
gifsicle: {
interlaced: false,
},
pngquant: {
quality: '65-90',
speed: 4,
},
},
},
],
},
],
},
// Specify additional processing or side-effects done on the Webpack output bundles as a whole.
plugins: [
// Generates an HTML file in the output directory.
new HtmlWebpackPlugin({
inject: true, // Appends script tags linking to the webpack bundles at the end of the body
template: path.resolve(__dirname, '../public/index.html'),
}),
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
// when the --hot option is not passed in as part of the command
// the HotModuleReplacementPlugin has to be specified in the Webpack configuration
// https://webpack.js.org/configuration/dev-server/#devserver-hot
new webpack.HotModuleReplacementPlugin(),
],
// This configures webpack-dev-server which serves bundles from memory and provides live
// reloading.
devServer: {
host: '0.0.0.0',
port: 3000,
historyApiFallback: true,
hot: true,
inline: true,
},
});

View File

@@ -1,71 +0,0 @@
// This is the prod Webpack config. All settings here should prefer smaller,
// optimized bundles at the expense of a longer build time.
const Merge = require('webpack-merge'); // eslint-disable-line import/no-extraneous-dependencies
const commonConfig = require('./webpack.common.config.js');
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // eslint-disable-line import/no-extraneous-dependencies
module.exports = Merge.smart(commonConfig, {
mode: 'production',
devtool: 'source-map',
entry: './src/lib/index.js',
output: {
path: path.resolve(__dirname, '../dist'),
library: 'frontend-component-footer',
libraryTarget: 'umd',
globalObject: 'typeof self !== \'undefined\' ? self : this',
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
react: path.resolve(__dirname, './node_modules/react'),
'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
},
},
externals: {
react: {
commonjs: 'react',
commonjs2: 'react',
amd: 'React',
root: 'React',
},
'react-dom': {
commonjs: 'react-dom',
commonjs2: 'react-dom',
amd: 'ReactDOM',
root: 'ReactDOM',
},
'@edx/frontend-i18n': {
commonjs: '@edx/frontend-i18n',
commonjs2: '@edx/frontend-i18n',
amd: '@edx/frontend-i18n',
root: '@edx/frontend-i18n',
},
'@edx/frontend-logging': {
commonjs: '@edx/frontend-logging',
commonjs2: '@edx/frontend-logging',
amd: '@edx/frontend-logging',
root: '@edx/frontend-logging',
},
},
plugins: [
// Cleans the dist directory before each build
new CleanWebpackPlugin(['dist'], {
root: path.join(__dirname, '../'),
}),
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: [
path.resolve(__dirname, '../src/lib'),
],
exclude: /(node_modules)/,
use: [
{ loader: 'babel-loader' },
],
},
],
},
});

24218
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,28 +2,24 @@
"name": "@edx/frontend-component-footer", "name": "@edx/frontend-component-footer",
"version": "1.0.0-semantically-released", "version": "1.0.0-semantically-released",
"description": "Site footer component for use when building edX frontend applications", "description": "Site footer component for use when building edX frontend applications",
"main": "dist/main.js", "main": "dist/index.js",
"module": "dist/main.js", "module": "dist/index.js",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"scripts": { "scripts": {
"build": "NODE_ENV=production BABEL_ENV=production webpack --config=config/webpack.prod.config.js", "build": "make build",
"build:stats": "NODE_ENV=production BABEL_ENV=production webpack --config=config/webpack.prod.config.js --profile --progress --json > stats.json",
"gc": "commit", "gc": "commit",
"commitmsg": "commitlint -e $GIT_PARAMS", "commitmsg": "commitlint -e $GIT_PARAMS",
"coveralls": "cat ./coverage/lcov.info | coveralls", "coveralls": "cat ./coverage/lcov.info | coveralls",
"is-es5": "es-check es5 ./dist/*.js",
"i18n_extract": "BABEL_ENV=i18n babel src --quiet > /dev/null",
"lint": "eslint --ext .js --ext .jsx .", "lint": "eslint --ext .js --ext .jsx .",
"precommit": "npm run lint", "precommit": "npm run lint",
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"semantic-release": "semantic-release", "semantic-release": "semantic-release",
"start": "NODE_ENV=development BABEL_ENV=development node_modules/.bin/webpack-dev-server --config=config/webpack.dev.config.js --progress", "start": "./node_modules/.bin/parcel src/example/index.html",
"test": "jest --coverage", "test": "jest --coverage",
"snapshot": "jest --updateSnapshot", "snapshot": "jest --updateSnapshot",
"travis-deploy-once": "travis-deploy-once", "travis-deploy-once": "travis-deploy-once"
"watch": "watch 'npm run build' ./src"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -36,83 +32,65 @@
}, },
"homepage": "https://github.com/edx/frontend-component-footer#readme", "homepage": "https://github.com/edx/frontend-component-footer#readme",
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/plugin-proposal-object-rest-spread": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"@babel/preset-react": "^7.0.0",
"@commitlint/cli": "^7.1.2", "@commitlint/cli": "^7.1.2",
"@commitlint/config-angular": "^6.0.2", "@commitlint/config-angular": "^6.0.2",
"@commitlint/prompt": "^6.0.2", "@commitlint/prompt": "^6.0.2",
"@commitlint/prompt-cli": "^6.0.2", "@commitlint/prompt-cli": "^6.0.2",
"@edx/edx-bootstrap": "^2.2.1", "@edx/frontend-i18n": "^3.0.0",
"@edx/frontend-i18n": "^2.1.0",
"@edx/frontend-logging": "^2.0.2",
"@fortawesome/fontawesome-svg-core": "^1.2.17", "@fortawesome/fontawesome-svg-core": "^1.2.17",
"@fortawesome/free-brands-svg-icons": "^5.8.1", "@fortawesome/free-brands-svg-icons": "^5.8.1",
"@fortawesome/free-regular-svg-icons": "^5.8.1", "@fortawesome/free-regular-svg-icons": "^5.8.1",
"@fortawesome/free-solid-svg-icons": "^5.8.1", "@fortawesome/free-solid-svg-icons": "^5.8.1",
"@fortawesome/react-fontawesome": "^0.1.4", "@fortawesome/react-fontawesome": "^0.1.4",
"babel-cli": "^6.26.0", "babel-jest": "^24.8.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",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"coveralls": "^3.0.0", "coveralls": "^3.0.0",
"css-loader": "^0.28.9",
"enzyme": "^3.3.0", "enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1", "enzyme-adapter-react-16": "^1.1.1",
"es-check": "^5.0.0", "eslint": "^6.0.1",
"eslint": "^5.2.0",
"eslint-config-edx": "^4.0.4", "eslint-config-edx": "^4.0.4",
"eslint-plugin-jsx-a11y": "^6.1.2", "eslint-plugin-jsx-a11y": "^6.1.2",
"file-loader": "^1.1.9",
"glob": "^7.1.3", "glob": "^7.1.3",
"html-webpack-plugin": "^3.2.0",
"husky": "^0.14.3", "husky": "^0.14.3",
"image-webpack-loader": "^4.2.0", "jest": "^24.8.0",
"jest": "23.6.0", "parcel-bundler": "^1.12.3",
"node-sass": "^4.7.2", "prop-types": "^15.7.2",
"prop-types": "^15.5.10", "react": "^16.8.6",
"react": "^16.4.2",
"react-dev-utils": "^5.0.0", "react-dev-utils": "^5.0.0",
"react-dom": "^16.2.0", "react-dom": "^16.8.6",
"react-test-renderer": "^16.6.0", "react-test-renderer": "^16.6.0",
"reactifex": "^1.1.1", "reactifex": "^1.1.1",
"sass-loader": "^6.0.6", "sass": "^1.22.1",
"semantic-release": "^15.1.7", "semantic-release": "^15.1.7",
"source-map-loader": "^0.2.1", "travis-deploy-once": "^5.0.0"
"style-loader": "^0.20.2", },
"travis-deploy-once": "^5.0.0", "sass": {
"watch": "^1.0.2", "includePaths": [
"webpack": "^4.19.1", "./node_modules"
"webpack-cli": "^3.1.0", ]
"webpack-dev-server": "^3.1.9",
"webpack-merge": "^4.2.1"
}, },
"dependencies": { "dependencies": {
"@edx/paragon": "^6.0.2",
"lodash.pick": "^4.4.0",
"query-string": "^5.1.1" "query-string": "^5.1.1"
}, },
"peerDependencies": {
"@edx/frontend-i18n": "^2.1.0",
"@edx/frontend-logging": "^2.0.2",
"@edx/paragon": "^4.2.4",
"prop-types": "^15.5.10",
"react": "^16.4.2",
"react-dom": "^16.2.0"
},
"jest": { "jest": {
"setupFiles": [ "setupFiles": [
"./src/tests/setupTest.js" "./src/lib/setupTest.js"
], ],
"collectCoverageFrom": [ "collectCoverageFrom": [
"src/lib/**/*.{js,jsx}", "src/lib/**/*.{js,jsx}",
"!src/tests/setupTest.js", "!src/lib/setupTest.js",
"!src/index.js", "!src/lib/index.js",
"!**/node_modules/**", "!**/node_modules/**"
"!**/tests/**"
], ],
"moduleNameMapper": { "moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js", "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/src/lib/__mocks__/fileMock.js",
"\\.(css|scss)$": "identity-obj-proxy" "\\.(css|scss)$": "identity-obj-proxy"
} }
} }

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

192
src/config/index.jsx Normal file
View File

@@ -0,0 +1,192 @@
/* eslint-disable */
import React from 'react';
import pick from 'lodash.pick';
import FooterLogo from './edx-footer.png';
/* TODO: This file is incomplete/non-functional. It needs some thougth about how to get
* messages/intl into it so that we can localize our titles here. I think the right answer is
* for this file to directly import messages.js (next door) and be passed the app's `intl`
* object, as shown below. Then it can call formatMessage on `intl`. Also, the JSX below
* should be evaluated with the IntlProvider when it's actually rendered by the footer, so
* that _should_ work.
*/
let config = {
MARKETING_SITE_BASE_URL: null,
};
let intl = null;
function validateConfiguration(newConfig) {
Object.keys(config).forEach((key) => {
if (newConfig[key] === undefined) {
throw new Error(`Footer configuration error: ${key} is required.`);
}
});
}
export function getDefaultConfiguration(newConfig, newIntl) {
validateConfiguration(newConfig);
config = pick(newConfig, Object.keys(config));
intl = newIntl;
const edXLinks = [
{
title: 'About',
url: `${config.MARKETING_SITE_BASE_URL}/about-us`,
},
{
title: 'edX for Business',
url: config.ENTERPRISE_MARKETING_URL,
queryParams: {
utm_campaign: config.ENTERPRISE_MARKETING_UTM_CAMPAIGN,
utm_medium: config.ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM,
utm_source: config.ENTERPRISE_MARKETING_UTM_SOURCE,
},
},
{
title: 'Affiliates',
url: `${config.MARKETING_SITE_BASE_URL}/affiliate-program`,
},
{
title: 'Open edX',
url: `${config.OPEN_SOURCE_URL}`,
},
{
title: 'Careers',
url: `${config.MARKETING_SITE_BASE_URL}/careers`,
},
{
title: 'News',
url: `${config.MARKETING_SITE_BASE_URL}/news-announcements`,
},
];
const legalLinks = [
{
title: 'Terms of Service & Honor Code',
url: `${config.MARKETING_SITE_BASE_URL}/edx-terms-service`,
},
{
title: 'Privacy Policy',
url: `${config.MARKETING_SITE_BASE_URL}/edx-privacy-policy`,
},
{
title: 'Accessibility Policy',
url: `${config.MARKETING_SITE_BASE_URL}/accessibility`,
},
{
title: 'Trademark Policy',
url: `${config.MARKETING_SITE_BASE_URL}/trademarks`,
},
{
title: 'Sitemap',
url: `${config.MARKETING_SITE_BASE_URL}/sitemap`,
},
];
const connectLinks = [
{
title: 'Blog',
url: `${config.MARKETING_SITE_BASE_URL}/blog`,
},
{
title: 'Contact Us',
url: `${config.LMS_BASE_URL}/support/contact_us`,
},
{
title: 'Help Center',
url: config.SUPPORT_URL,
},
{
title: 'Media Kit',
url: `${config.MARKETING_SITE_BASE_URL}/media-kit`,
},
{
title: 'Donate',
url: `${config.MARKETING_SITE_BASE_URL}/donate`,
},
];
const socialLinks = [
{
title: 'Facebook',
url: config.FACEBOOK_URL,
icon: <FontAwesomeIcon icon={faFacebookSquare} className="social-icon" size="2x" />,
screenReaderText: 'Like edX on Facebook',
},
{
title: 'Twitter',
url: config.TWITTER_URL,
icon: <FontAwesomeIcon icon={faTwitterSquare} className="social-icon" size="2x" />,
screenReaderText: 'Follow edX on Twitter',
},
{
title: 'Youtube',
url: config.YOU_TUBE_URL,
icon: <FontAwesomeIcon icon={faYoutubeSquare} className="social-icon" size="2x" />,
screenReaderText: 'Subscribe to the edX YouTube channel',
},
{
title: 'LinkedIn',
url: config.LINKED_IN_URL,
icon: <FontAwesomeIcon icon={faLinkedin} className="social-icon" size="2x" />,
screenReaderText: 'Follow edX on LinkedIn',
},
{
title: 'Reddit',
url: config.REDDIT_URL,
icon: <FontAwesomeIcon icon={faRedditSquare} className="social-icon" size="2x" />,
screenReaderText: 'Subscribe to the edX subreddit',
},
];
const copyright = <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"
/>
const trademark = <FormattedMessage
id="footer.site-footer.trademark-text"
defaultMessage="EdX, Open edX, and MicroMasters are registered trademarks of edX Inc. | 深圳市恒宇博科技有限公司 {icpLicense}"
values={{ icpLicense: <a href="http://www.beian.miit.gov.cn">粤ICP备17044299号-2</a> }}
description="Footer trademark text"
/>
return {
ariaLabel: "Page Footer",
siteLogo: FooterLogo,
marketingSiteBaseUrl: config.MARKETING_SITE_BASE_URL,
linkSectionOne: edXLinks,
linkSectionTwo: legalLinks,
linkSectionThree: connectLinks,
socialLinks,
showMobileLinks: true,
appleAppStore: {
url: config.APPLE_APP_STORE_URL,
altText: "Download the edX mobile app from the Apple App Store",
},
googlePlayUrl: {
url: config.GOOGLE_PLAY_URL,
altText: "Download the edX mobile app from Google Play",
},
supportedLanguages: [],
languageForm: null,
copyright,
trademark,
}
};
export default getDefaultConfiguration;

91
src/config/messages.js Normal file
View File

@@ -0,0 +1,91 @@
import { defineMessages } from '@edx/frontend-i18n';
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,11 +1,12 @@
<!doctype html> <!doctype html>
<html lang="en-us"> <html lang="en-us">
<head> <head>
<title>Footer | edX</title> <title>Example Footer</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script src="./index.js"></script>
</body> </body>
</html> </html>

View File

@@ -13,9 +13,9 @@ import { faLanguage } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
/* eslint-enable import/no-extraneous-dependencies */ /* eslint-enable import/no-extraneous-dependencies */
import SiteFooter from './lib'; import SiteFooter from '../lib/';
import './index.scss'; import './index.scss';
import FooterLogo from './edx-footer.png'; import FooterLogo from '../config/edx-footer.png';
const edXLinks = [ const edXLinks = [

2
src/example/index.scss Normal file
View File

@@ -0,0 +1,2 @@
@import "~@edx/paragon/scss/edx/theme";
@import '../lib/SiteFooter.scss';

View File

@@ -1,32 +0,0 @@
import arMessages from './messages/ar.json';
import caMessages from './messages/ca.json';
// no need to import en messages-- they are in the defaultMessage field
import es419Messages from './messages/es_419.json';
import frMessages from './messages/fr.json';
import zhcnMessages from './messages/zh_CN.json';
import heMessages from './messages/he.json';
import idMessages from './messages/id.json';
import kokrMessages from './messages/ko_kr.json';
import plMessages from './messages/pl.json';
import ptbrMessages from './messages/pt_br.json';
import ruMessages from './messages/ru.json';
import thMessages from './messages/th.json';
import ukMessages from './messages/uk.json';
const messages = {
ar: arMessages,
'es-419': es419Messages,
fr: frMessages,
'zh-cn': zhcnMessages,
ca: caMessages,
he: heMessages,
id: idMessages,
'ko-kr': kokrMessages,
pl: plMessages,
'pt-br': ptbrMessages,
ru: ruMessages,
th: thMessages,
uk: ukMessages,
};
export default messages;

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

5
src/index.js Normal file
View File

@@ -0,0 +1,5 @@
import SiteFooter from './lib';
export { default as FooterLogo } from './config/edx-footer.png';
export { default as config } from './config';
export default SiteFooter;

View File

@@ -1,4 +0,0 @@
@import "~@edx/edx-bootstrap/scss/edx/theme";
@import "~bootstrap/scss/bootstrap";
@import './lib/scss/_site-footer.scss';

View File

@@ -2,12 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import qs from 'query-string'; import qs from 'query-string';
const EVENT_NAMES = { export const EVENT_NAMES = {
FOOTER_LINK: 'edx.bi.footer.link', FOOTER_LINK: 'edx.bi.footer.link',
}; };
export default class SiteFooter extends React.Component {
class SiteFooter extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.externalLinkClickHandler = this.externalLinkClickHandler.bind(this); this.externalLinkClickHandler = this.externalLinkClickHandler.bind(this);
@@ -198,7 +197,7 @@ SiteFooter.propTypes = {
src: PropTypes.node, src: PropTypes.node,
altText: PropTypes.string, altText: PropTypes.string,
ariaLabel: PropTypes.string, ariaLabel: PropTypes.string,
}), }).isRequired,
marketingSiteBaseUrl: PropTypes.string, marketingSiteBaseUrl: PropTypes.string,
linkSectionOne: linkSectionShape, linkSectionOne: linkSectionShape,
linkSectionTwo: linkSectionShape, linkSectionTwo: linkSectionShape,
@@ -246,13 +245,12 @@ const linkSectionDefault = {
}; };
SiteFooter.defaultProps = { SiteFooter.defaultProps = {
siteLogo: null,
marketingSiteBaseUrl: null, marketingSiteBaseUrl: null,
linkSectionOne: linkSectionDefault, linkSectionOne: linkSectionDefault,
linkSectionTwo: linkSectionDefault, linkSectionTwo: linkSectionDefault,
linkSectionThree: linkSectionDefault, linkSectionThree: linkSectionDefault,
socialLinks: [], socialLinks: [],
showMobileLinks: true, showMobileLinks: false,
appleAppStore: null, appleAppStore: null,
googlePlay: null, googlePlay: null,
supportedLanguages: [], supportedLanguages: [],
@@ -260,6 +258,3 @@ SiteFooter.defaultProps = {
copyright: null, copyright: null,
trademark: null, trademark: null,
}; };
export default SiteFooter;
export { EVENT_NAMES };

View File

@@ -12,8 +12,8 @@ import {
import { faLanguage } from '@fortawesome/free-solid-svg-icons'; import { faLanguage } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import FooterLogo from '../../../edx-footer.png'; import FooterLogo from '../config/edx-footer.png';
import SiteFooter, { EVENT_NAMES } from './index'; import SiteFooter, { EVENT_NAMES } from './SiteFooter';
const edXLinks = [ const edXLinks = [

View File

@@ -1,3 +1,3 @@
import SiteFooter from './components/SiteFooter'; import SiteFooter from './SiteFooter';
export default SiteFooter; export default SiteFooter;

1275
stats.json

File diff suppressed because one or more lines are too long