Compare commits

...

7 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
AlasdairSwan
31e255ebe9 Merge pull request #44 from edx/alasdair/remove-paragon
perf(paragon): remove paragon to reduce bundle size
2019-06-21 11:14:29 -04:00
AlasdairSwan
349f83f686 perf(paragon): remove paragon to reduce bundle size 2019-06-21 11:11:01 -04:00
David Joy
e0c205ce14 fix: non deprecated use of hyperlink (#43) 2019-06-21 09:56:05 -04:00
albemarle
81398c8cfd Merge pull request #42 from edx/albemarle/different-diff
fix(i18n): make detect_changed_source_translations work for both push and pull jobs
2019-06-11 13:51:08 -04:00
albemarle
3fcd76b455 fix(i18n): make detect_changed_source_translations work for both push and pull jobs 2019-06-11 13:34:27 -04:00
43 changed files with 7071 additions and 13526 deletions

View File

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

View File

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

5
.gitignore vendored
View File

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

View File

@@ -8,10 +8,8 @@ install:
- npm install
script:
- npm run lint
- npm run i18n_extract
- npm run test
- npm run build
- npm run is-es5
after_success:
- npm run travis-deploy-once "npm run semantic-release"
- 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,46 +1,16 @@
transifex_resource = frontend-component-footer
transifex_langs = "ar,fr,es_419,zh_CN"
transifex_utils = ./node_modules/.bin/transifex-utils.js
transifex_input = ./src/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:
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
detect_changed_source_translations:
# Checking for changed translations...
git diff --exit-code $(transifex_input)
# This target is used by Travis.
validate-no-uncommitted-package-lock-changes:
# Checking for package-lock.json changes...
git diff --exit-code package-lock.json
# Push translations to Transifex. 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)
# Pull translations from Transifex
pull_translations:
tx pull -f --mode reviewed --language=$(transifex_langs)
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|
|semantic-release|
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``.
frontend-component-footer is a library containing a site footer component for use when building edX frontend applications.
Usage
-----
@@ -17,13 +14,10 @@ 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 <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.
Requirements
------------
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>`__.
The distribution
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,77 +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',
},
'@edx/paragon': {
commonjs: '@edx/paragon',
commonjs2: '@edx/paragon',
amd: 'Paragon',
root: 'Paragon',
},
},
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' },
],
},
],
},
});

18529
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",
"version": "1.0.0-semantically-released",
"description": "Site footer component for use when building edX frontend applications",
"main": "dist/main.js",
"module": "dist/main.js",
"main": "dist/index.js",
"module": "dist/index.js",
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "NODE_ENV=production BABEL_ENV=production webpack --config=config/webpack.prod.config.js",
"build:stats": "NODE_ENV=production BABEL_ENV=production webpack --config=config/webpack.prod.config.js --profile --progress --json > stats.json",
"build": "make build",
"gc": "commit",
"commitmsg": "commitlint -e $GIT_PARAMS",
"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 .",
"precommit": "npm run lint",
"prepublishOnly": "npm run build",
"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",
"snapshot": "jest --updateSnapshot",
"travis-deploy-once": "travis-deploy-once",
"watch": "watch 'npm run build' ./src"
"travis-deploy-once": "travis-deploy-once"
},
"repository": {
"type": "git",
@@ -36,84 +32,65 @@
},
"homepage": "https://github.com/edx/frontend-component-footer#readme",
"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/config-angular": "^6.0.2",
"@commitlint/prompt": "^6.0.2",
"@commitlint/prompt-cli": "^6.0.2",
"@edx/edx-bootstrap": "^2.2.1",
"@edx/frontend-i18n": "^2.1.0",
"@edx/frontend-logging": "^2.0.2",
"@edx/paragon": "^4.2.4",
"@edx/frontend-i18n": "^3.0.0",
"@fortawesome/fontawesome-svg-core": "^1.2.17",
"@fortawesome/free-brands-svg-icons": "^5.8.1",
"@fortawesome/free-regular-svg-icons": "^5.8.1",
"@fortawesome/free-solid-svg-icons": "^5.8.1",
"@fortawesome/react-fontawesome": "^0.1.4",
"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",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"babel-jest": "^24.8.0",
"coveralls": "^3.0.0",
"css-loader": "^0.28.9",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"es-check": "^5.0.0",
"eslint": "^5.2.0",
"eslint": "^6.0.1",
"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",
"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",
"jest": "^24.8.0",
"parcel-bundler": "^1.12.3",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.2.0",
"react-dom": "^16.8.6",
"react-test-renderer": "^16.6.0",
"reactifex": "^1.1.1",
"sass-loader": "^6.0.6",
"sass": "^1.22.1",
"semantic-release": "^15.1.7",
"source-map-loader": "^0.2.1",
"style-loader": "^0.20.2",
"travis-deploy-once": "^5.0.0",
"watch": "^1.0.2",
"webpack": "^4.19.1",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.9",
"webpack-merge": "^4.2.1"
"travis-deploy-once": "^5.0.0"
},
"sass": {
"includePaths": [
"./node_modules"
]
},
"dependencies": {
"@edx/paragon": "^6.0.2",
"lodash.pick": "^4.4.0",
"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": {
"setupFiles": [
"./src/tests/setupTest.js"
"./src/lib/setupTest.js"
],
"collectCoverageFrom": [
"src/lib/**/*.{js,jsx}",
"!src/tests/setupTest.js",
"!src/index.js",
"!**/node_modules/**",
"!**/tests/**"
"!src/lib/setupTest.js",
"!src/lib/index.js",
"!**/node_modules/**"
],
"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"
}
}

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>
<html lang="en-us">
<head>
<title>Footer | edX</title>
<title>Example Footer</title>
<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>
<body>
<div id="root"></div>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -13,9 +13,9 @@ import { faLanguage } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
/* eslint-enable import/no-extraneous-dependencies */
import SiteFooter from './lib';
import SiteFooter from '../lib/';
import './index.scss';
import FooterLogo from './edx-footer.png';
import FooterLogo from '../config/edx-footer.png';
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/sass/edx/theme";
@import "~bootstrap/scss/bootstrap";
@import './lib/scss/_site-footer.scss';

View File

@@ -1,14 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Hyperlink } from '@edx/paragon';
import qs from 'query-string';
const EVENT_NAMES = {
export const EVENT_NAMES = {
FOOTER_LINK: 'edx.bi.footer.link',
};
class SiteFooter extends React.Component {
export default class SiteFooter extends React.Component {
constructor(props) {
super(props);
this.externalLinkClickHandler = this.externalLinkClickHandler.bind(this);
@@ -118,11 +116,12 @@ class SiteFooter extends React.Component {
>
<div className="max-width-1180 d-grid">
<div className="area-1">
<Hyperlink
destination={marketingSiteBaseUrl}
content={<img src={siteLogo.src} alt={siteLogo.altText} />}
<a
href={marketingSiteBaseUrl}
aria-label={siteLogo.ariaLabel}
/>
>
<img src={siteLogo.src} alt={siteLogo.altText} />
</a>
{showLanguageSelector &&
<div className="i18n d-flex mt-2">
<form
@@ -198,7 +197,7 @@ SiteFooter.propTypes = {
src: PropTypes.node,
altText: PropTypes.string,
ariaLabel: PropTypes.string,
}),
}).isRequired,
marketingSiteBaseUrl: PropTypes.string,
linkSectionOne: linkSectionShape,
linkSectionTwo: linkSectionShape,
@@ -246,13 +245,12 @@ const linkSectionDefault = {
};
SiteFooter.defaultProps = {
siteLogo: null,
marketingSiteBaseUrl: null,
linkSectionOne: linkSectionDefault,
linkSectionTwo: linkSectionDefault,
linkSectionThree: linkSectionDefault,
socialLinks: [],
showMobileLinks: true,
showMobileLinks: false,
appleAppStore: null,
googlePlay: null,
supportedLanguages: [],
@@ -260,6 +258,3 @@ SiteFooter.defaultProps = {
copyright: 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 { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import FooterLogo from '../../../edx-footer.png';
import SiteFooter, { EVENT_NAMES } from './index';
import FooterLogo from '../config/edx-footer.png';
import SiteFooter, { EVENT_NAMES } from './SiteFooter';
const edXLinks = [

View File

@@ -15,8 +15,6 @@ exports[`<SiteFooter /> renders correctly does not render language selector 1`]
<a
aria-label="edX Home"
href="https://www.example.com"
onClick={[Function]}
target="_self"
>
<img
alt="edx Logo"
@@ -398,8 +396,6 @@ exports[`<SiteFooter /> renders correctly does not render mobile links 1`] = `
<a
aria-label="edX Home"
href="https://www.example.com"
onClick={[Function]}
target="_self"
>
<img
alt="edx Logo"
@@ -807,8 +803,6 @@ exports[`<SiteFooter /> renders correctly does not render social links 1`] = `
<a
aria-label="edX Home"
href="https://www.example.com"
onClick={[Function]}
target="_self"
>
<img
alt="edx Logo"
@@ -1084,8 +1078,6 @@ exports[`<SiteFooter /> renders correctly renders with social and mobile links 1
<a
aria-label="edX Home"
href="https://www.example.com"
onClick={[Function]}
target="_self"
>
<img
alt="edx Logo"

View File

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

1275
stats.json

File diff suppressed because one or more lines are too long