Organize the repo and export profile module properly (#262)
* Moving files to their new homes. Subsequent commit will wire everything up again. * Relink files and split stylesheet. Requires adding resolve-url-loader to fix sass import relativity. * Remove browserslist warning. * Remove the need for ProfileMain * Fix test issues - needed thunk.
This commit is contained in:
164
package-lock.json
generated
164
package-lock.json
generated
@@ -4248,6 +4248,27 @@
|
||||
"integrity": "sha512-4diPfzWbLEIElVG4AnqP+00SULlPzNuyJFNnmMrLgyaxG6tZXJ1sn7mjBu4fHrJE+Yp/jgylOweJn2xsLMFggQ==",
|
||||
"dev": true
|
||||
},
|
||||
"adjust-sourcemap-loader": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz",
|
||||
"integrity": "sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert": "1.4.1",
|
||||
"camelcase": "5.0.0",
|
||||
"loader-utils": "1.2.3",
|
||||
"object-path": "0.11.4",
|
||||
"regex-parser": "2.2.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
|
||||
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"agent-base": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
|
||||
@@ -4495,6 +4516,12 @@
|
||||
"commander": "^2.11.0"
|
||||
}
|
||||
},
|
||||
"arity-n": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz",
|
||||
"integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=",
|
||||
"dev": true
|
||||
},
|
||||
"arr-diff": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||
@@ -6631,6 +6658,15 @@
|
||||
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
|
||||
"dev": true
|
||||
},
|
||||
"compose-function": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz",
|
||||
"integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arity-n": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"compressible": {
|
||||
"version": "2.0.15",
|
||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz",
|
||||
@@ -7131,6 +7167,18 @@
|
||||
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
|
||||
"dev": true
|
||||
},
|
||||
"css": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
|
||||
"integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"source-map": "^0.6.1",
|
||||
"source-map-resolve": "^0.5.2",
|
||||
"urix": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"css-color-names": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||
@@ -7462,6 +7510,16 @@
|
||||
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
|
||||
"dev": true
|
||||
},
|
||||
"d": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es5-ext": "^0.10.50",
|
||||
"type": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"damerau-levenshtein": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
|
||||
@@ -8344,6 +8402,28 @@
|
||||
"is-symbol": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.51",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz",
|
||||
"integrity": "sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es6-iterator": "~2.0.3",
|
||||
"es6-symbol": "~3.1.1",
|
||||
"next-tick": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz",
|
||||
@@ -8357,6 +8437,16 @@
|
||||
"es6-promise": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"es6-symbol": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.2.tgz",
|
||||
"integrity": "sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "^1.0.1",
|
||||
"es5-ext": "^0.10.51"
|
||||
}
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@@ -14901,6 +14991,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
||||
"dev": true
|
||||
},
|
||||
"nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
@@ -15334,6 +15430,12 @@
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
|
||||
"integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
|
||||
},
|
||||
"object-path": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz",
|
||||
"integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=",
|
||||
"dev": true
|
||||
},
|
||||
"object-visit": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
|
||||
@@ -18373,6 +18475,12 @@
|
||||
"safe-regex": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"regex-parser": {
|
||||
"version": "2.2.10",
|
||||
"resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.10.tgz",
|
||||
"integrity": "sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA==",
|
||||
"dev": true
|
||||
},
|
||||
"regexp-tree": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.13.tgz",
|
||||
@@ -18595,6 +18703,32 @@
|
||||
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-url-loader": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.0.tgz",
|
||||
"integrity": "sha512-2QcrA+2QgVqsMJ1Hn5NnJXIGCX1clQ1F6QJTqOeiaDw9ACo1G2k+8/shq3mtqne03HOFyskAClqfxKyFBriXZg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"adjust-sourcemap-loader": "2.0.0",
|
||||
"camelcase": "5.0.0",
|
||||
"compose-function": "3.0.3",
|
||||
"convert-source-map": "1.6.0",
|
||||
"es6-iterator": "2.0.3",
|
||||
"loader-utils": "1.2.3",
|
||||
"postcss": "7.0.14",
|
||||
"rework": "1.0.1",
|
||||
"rework-visit": "1.0.0",
|
||||
"source-map": "0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
|
||||
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"responselike": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
|
||||
@@ -18626,6 +18760,30 @@
|
||||
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
|
||||
"dev": true
|
||||
},
|
||||
"rework": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz",
|
||||
"integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"convert-source-map": "^0.3.3",
|
||||
"css": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"convert-source-map": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz",
|
||||
"integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"rework-visit": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz",
|
||||
"integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=",
|
||||
"dev": true
|
||||
},
|
||||
"rgb-regex": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
|
||||
@@ -21406,6 +21564,12 @@
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"dev": true
|
||||
},
|
||||
"type": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
|
||||
"dev": true
|
||||
},
|
||||
"type-check": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/edx/frontend-app-profile.git"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"ie 11"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production BABEL_ENV=production webpack --config=webpack/webpack.prod.config.js",
|
||||
"i18n_extract": "BABEL_ENV=i18n babel src --quiet > /dev/null",
|
||||
@@ -113,6 +117,7 @@
|
||||
"react-test-renderer": "16.9.0",
|
||||
"reactifex": "1.1.1",
|
||||
"redux-mock-store": "1.5.3",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass-loader": "6.0.7",
|
||||
"source-map-loader": "0.2.4",
|
||||
"style-loader": "0.20.3",
|
||||
|
||||
@@ -4,20 +4,27 @@ import { App, AppProvider, APP_ERROR, APP_READY, ErrorPage } from '@edx/frontend
|
||||
import { NewRelicLoggingService } from '@edx/frontend-logging';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import Header, { messages as headerMessages } from '@edx/frontend-component-header';
|
||||
import Footer from '../footer/Footer';
|
||||
|
||||
import appMessages from './i18n';
|
||||
import './index.scss';
|
||||
import ProfileMain from './profile/components/ProfileMain';
|
||||
import { ProfilePage, NotFoundPage } from './profile';
|
||||
import configureStore from './store';
|
||||
|
||||
App.subscribe(APP_READY, () => {
|
||||
ReactDOM.render(
|
||||
<AppProvider store={configureStore()}>
|
||||
<Header />
|
||||
<ProfileMain />
|
||||
<main>
|
||||
<Switch>
|
||||
<Route path="/u/:username" component={ProfilePage} />
|
||||
<Route path="/notfound" component={NotFoundPage} />
|
||||
<Route path="*" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</main>
|
||||
<Footer />
|
||||
</AppProvider>,
|
||||
document.getElementById('root'),
|
||||
@@ -29,4 +36,3 @@ App.subscribe(APP_ERROR, (error) => {
|
||||
});
|
||||
|
||||
App.initialize({ messages: [appMessages, headerMessages], loggingService: NewRelicLoggingService });
|
||||
|
||||
|
||||
140
src/index.scss
140
src/index.scss
@@ -1,145 +1,7 @@
|
||||
@import '~@edx/paragon/scss/edx/theme.scss';
|
||||
@import '~@edx/paragon/scss/edx/fonts.scss'; // Roboto
|
||||
|
||||
$fa-font-path: "~font-awesome/fonts";
|
||||
@import "~font-awesome/scss/font-awesome";
|
||||
@import './profile/index.scss';
|
||||
|
||||
@import "~@edx/frontend-component-header/src/index";
|
||||
@import "~@edx/frontend-component-footer/src/lib/scss/site-footer";
|
||||
|
||||
.word-break-all {
|
||||
word-break: break-all !important;
|
||||
}
|
||||
|
||||
// TODO: Update edx-bootstrap theme to incorporate these edits.
|
||||
.btn, a.btn {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.btn-link {
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-page-bg-banner {
|
||||
height: 12rem;
|
||||
background-image: url('./assets/dot-pattern-light.png');
|
||||
background-repeat: repeat-x;
|
||||
background-size: auto 85%;
|
||||
}
|
||||
|
||||
.profile-page {
|
||||
.edit-section-header {
|
||||
@extend .h6;
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
letter-spacing: 0;
|
||||
margin: 0;
|
||||
}
|
||||
label.edit-section-header {
|
||||
margin-bottom: $spacer * .5;
|
||||
}
|
||||
.profile-avatar-wrap {
|
||||
@include media-breakpoint-up(md) {
|
||||
max-width: 12rem;
|
||||
margin-right: 0;
|
||||
margin-top: -8rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-avatar-menu-container {
|
||||
background: rgba(0,0,0,.65);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
@include media-breakpoint-up(md) {
|
||||
background: linear-gradient(to top, rgba(0,0,0,.65) 4rem, rgba(0,0,0,0) 4rem);
|
||||
align-items: flex-end;
|
||||
}
|
||||
.btn {
|
||||
text-decoration: none;
|
||||
@include media-breakpoint-up(md) {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
@include media-breakpoint-up(md) {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.btn {
|
||||
color: $white;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
position: relative;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
width: 12rem;
|
||||
height: 12rem;
|
||||
}
|
||||
|
||||
.profile-avatar-edit-button {
|
||||
border: none;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: .1rem;
|
||||
font-weight: 600;
|
||||
background: rgba(0,0,0,.5);
|
||||
border-radius:0;
|
||||
transition: opacity 200ms ease;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
&:focus, &:hover, &:active, &.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.certificate {
|
||||
position: relative;
|
||||
.certificate-title {
|
||||
font-family: $font-family-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
.certificate-type-illustration {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
bottom: 0;
|
||||
width: 12rem;
|
||||
opacity: .06;
|
||||
background-size: 90%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right top;
|
||||
}
|
||||
.card-body {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { sendTrackingLogEvent } from '@edx/frontend-analytics';
|
||||
import { App, AppContext } from '@edx/frontend-base';
|
||||
import { App, AppContext, fetchUserAccount } from '@edx/frontend-base';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-i18n';
|
||||
import { StatusAlert, Hyperlink } from '@edx/paragon';
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
openForm,
|
||||
closeForm,
|
||||
updateDraft,
|
||||
} from '../actions';
|
||||
} from './data/actions';
|
||||
|
||||
// Components
|
||||
import ProfileAvatar from './forms/ProfileAvatar';
|
||||
@@ -33,14 +33,14 @@ import PageLoading from './PageLoading';
|
||||
import Banner from './Banner';
|
||||
|
||||
// Selectors
|
||||
import { profilePageSelector } from '../selectors';
|
||||
import { profilePageSelector } from './data/selectors';
|
||||
|
||||
// i18n
|
||||
import messages from './ProfilePage.messages';
|
||||
|
||||
App.requireConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
|
||||
|
||||
export class ProfilePage extends React.Component {
|
||||
class ProfilePage extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
@@ -58,6 +58,7 @@ export class ProfilePage extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchUserAccount(this.context.authenticatedUser.username);
|
||||
this.props.fetchProfile(this.props.match.params.username);
|
||||
sendTrackingLogEvent('edx.profile.viewed', {
|
||||
username: this.props.match.params.username,
|
||||
@@ -331,6 +332,7 @@ ProfilePage.propTypes = {
|
||||
photoUploadError: PropTypes.objectOf(PropTypes.string),
|
||||
|
||||
// Actions
|
||||
fetchUserAccount: PropTypes.func.isRequired,
|
||||
fetchProfile: PropTypes.func.isRequired,
|
||||
saveProfile: PropTypes.func.isRequired,
|
||||
saveProfilePhoto: PropTypes.func.isRequired,
|
||||
@@ -370,6 +372,7 @@ ProfilePage.defaultProps = {
|
||||
export default connect(
|
||||
profilePageSelector,
|
||||
{
|
||||
fetchUserAccount,
|
||||
fetchProfile,
|
||||
saveProfilePhoto,
|
||||
deleteProfilePhoto,
|
||||
@@ -7,11 +7,12 @@ import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import renderer from 'react-test-renderer';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import messages from '../../i18n';
|
||||
import ConnectedProfilePage from './ProfilePage';
|
||||
import messages from '../i18n';
|
||||
import ProfilePage from './ProfilePage';
|
||||
|
||||
const mockStore = configureMockStore();
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
const storeMocks = {
|
||||
loadingApp: require('./__mocks__/loadingApp.mockStore.js'),
|
||||
viewOwnProfile: require('./__mocks__/viewOwnProfile.mockStore.js'),
|
||||
@@ -19,6 +20,7 @@ const storeMocks = {
|
||||
savingEditedBio: require('./__mocks__/savingEditedBio.mockStore.js'),
|
||||
};
|
||||
const requiredProfilePageProps = {
|
||||
fetchUserAccount: () => {},
|
||||
fetchProfile: () => {},
|
||||
saveProfile: () => {},
|
||||
saveProfilePhoto: () => {},
|
||||
@@ -33,6 +35,7 @@ Object.defineProperty(global.document, 'cookie', {
|
||||
writable: true,
|
||||
value: `${App.config.LANGUAGE_PREFERENCE_COOKIE_NAME}=en`,
|
||||
});
|
||||
App.apiClient = jest.fn();
|
||||
|
||||
configureI18n(App.config, messages);
|
||||
|
||||
@@ -49,7 +52,7 @@ describe('<ProfilePage />', () => {
|
||||
>
|
||||
<IntlProvider locale="en">
|
||||
<Provider store={mockStore(storeMocks.loadingApp)}>
|
||||
<ConnectedProfilePage {...requiredProfilePageProps} />
|
||||
<ProfilePage {...requiredProfilePageProps} />
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
</AppContext.Provider>
|
||||
@@ -69,7 +72,7 @@ describe('<ProfilePage />', () => {
|
||||
>
|
||||
<IntlProvider locale="en">
|
||||
<Provider store={mockStore(storeMocks.viewOwnProfile)}>
|
||||
<ConnectedProfilePage {...requiredProfilePageProps} />
|
||||
<ProfilePage {...requiredProfilePageProps} />
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
</AppContext.Provider>
|
||||
@@ -89,7 +92,7 @@ describe('<ProfilePage />', () => {
|
||||
>
|
||||
<IntlProvider locale="en">
|
||||
<Provider store={mockStore(storeMocks.viewOtherProfile)}>
|
||||
<ConnectedProfilePage
|
||||
<ProfilePage
|
||||
{...requiredProfilePageProps}
|
||||
match={{ params: { username: 'verified' } }} // Override default match
|
||||
/>
|
||||
@@ -112,7 +115,7 @@ describe('<ProfilePage />', () => {
|
||||
>
|
||||
<IntlProvider locale="en">
|
||||
<Provider store={mockStore(storeMocks.savingEditedBio)}>
|
||||
<ConnectedProfilePage {...requiredProfilePageProps} />
|
||||
<ProfilePage {...requiredProfilePageProps} />
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
</AppContext.Provider>
|
||||
@@ -134,7 +137,7 @@ describe('<ProfilePage />', () => {
|
||||
>
|
||||
<IntlProvider locale="en">
|
||||
<Provider store={mockStore(storeMocks.loadingApp)}>
|
||||
<ConnectedProfilePage
|
||||
<ProfilePage
|
||||
{...requiredProfilePageProps}
|
||||
match={{ params: { username: 'test-username' } }}
|
||||
/>
|
||||
141
src/profile/_index.scss
Normal file
141
src/profile/_index.scss
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
|
||||
$fa-font-path: "~font-awesome/fonts";
|
||||
@import "~font-awesome/scss/font-awesome";
|
||||
|
||||
.word-break-all {
|
||||
word-break: break-all !important;
|
||||
}
|
||||
|
||||
// TODO: Update edx-bootstrap theme to incorporate these edits.
|
||||
.btn, a.btn {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.btn-link {
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-page-bg-banner {
|
||||
height: 12rem;
|
||||
background-image: url('./assets/dot-pattern-light.png');
|
||||
background-repeat: repeat-x;
|
||||
background-size: auto 85%;
|
||||
}
|
||||
|
||||
.profile-page {
|
||||
.edit-section-header {
|
||||
@extend .h6;
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
letter-spacing: 0;
|
||||
margin: 0;
|
||||
}
|
||||
label.edit-section-header {
|
||||
margin-bottom: $spacer * .5;
|
||||
}
|
||||
.profile-avatar-wrap {
|
||||
@include media-breakpoint-up(md) {
|
||||
max-width: 12rem;
|
||||
margin-right: 0;
|
||||
margin-top: -8rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-avatar-menu-container {
|
||||
background: rgba(0,0,0,.65);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
@include media-breakpoint-up(md) {
|
||||
background: linear-gradient(to top, rgba(0,0,0,.65) 4rem, rgba(0,0,0,0) 4rem);
|
||||
align-items: flex-end;
|
||||
}
|
||||
.btn {
|
||||
text-decoration: none;
|
||||
@include media-breakpoint-up(md) {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
@include media-breakpoint-up(md) {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.btn {
|
||||
color: $white;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
position: relative;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
width: 12rem;
|
||||
height: 12rem;
|
||||
}
|
||||
|
||||
.profile-avatar-edit-button {
|
||||
border: none;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: .1rem;
|
||||
font-weight: 600;
|
||||
background: rgba(0,0,0,.5);
|
||||
border-radius:0;
|
||||
transition: opacity 200ms ease;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
&:focus, &:hover, &:active, &.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.certificate {
|
||||
position: relative;
|
||||
.certificate-title {
|
||||
font-family: $font-family-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
.certificate-type-illustration {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
bottom: 0;
|
||||
width: 12rem;
|
||||
opacity: .06;
|
||||
background-size: 90%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right top;
|
||||
}
|
||||
.card-body {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@@ -1,37 +0,0 @@
|
||||
import { AppContext, fetchUserAccount } from '@edx/frontend-base';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import NotFoundPage from './NotFoundPage';
|
||||
import ConnectedProfilePage from './ProfilePage';
|
||||
|
||||
function ProfileMain(props) {
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
props.fetchUserAccount(authenticatedUser.username);
|
||||
}, [authenticatedUser.username]);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<Switch>
|
||||
<Route path="/u/:username" component={ConnectedProfilePage} />
|
||||
<Route path="/notfound" component={NotFoundPage} />
|
||||
<Route path="*" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
ProfileMain.propTypes = {
|
||||
fetchUserAccount: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
{
|
||||
fetchUserAccount,
|
||||
},
|
||||
)(ProfileMain);
|
||||
@@ -13,7 +13,7 @@ import EmptyContent from './elements/EmptyContent';
|
||||
import SwitchContent from './elements/SwitchContent';
|
||||
|
||||
// Selectors
|
||||
import { editableFormSelector } from '../../selectors';
|
||||
import { editableFormSelector } from '../data/selectors';
|
||||
|
||||
class Bio extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -13,11 +13,11 @@ import EditableItemHeader from './elements/EditableItemHeader';
|
||||
import SwitchContent from './elements/SwitchContent';
|
||||
|
||||
// Assets
|
||||
import professionalCertificateSVG from '../../assets/professional-certificate.svg';
|
||||
import verifiedCertificateSVG from '../../assets/verified-certificate.svg';
|
||||
import professionalCertificateSVG from '../assets/professional-certificate.svg';
|
||||
import verifiedCertificateSVG from '../assets/verified-certificate.svg';
|
||||
|
||||
// Selectors
|
||||
import { certificatesSelector } from '../../selectors';
|
||||
import { certificatesSelector } from '../data/selectors';
|
||||
|
||||
class Certificates extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -13,7 +13,7 @@ import EmptyContent from './elements/EmptyContent';
|
||||
import SwitchContent from './elements/SwitchContent';
|
||||
|
||||
// Selectors
|
||||
import { countrySelector } from '../../selectors';
|
||||
import { countrySelector } from '../data/selectors';
|
||||
|
||||
class Country extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -14,10 +14,10 @@ import EmptyContent from './elements/EmptyContent';
|
||||
import SwitchContent from './elements/SwitchContent';
|
||||
|
||||
// Constants
|
||||
import { EDUCATION_LEVELS } from '../../constants';
|
||||
import { EDUCATION_LEVELS } from '../data/constants';
|
||||
|
||||
// Selectors
|
||||
import { editableFormSelector } from '../../selectors';
|
||||
import { editableFormSelector } from '../data/selectors';
|
||||
|
||||
class Education extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -12,7 +12,7 @@ import EmptyContent from './elements/EmptyContent';
|
||||
import SwitchContent from './elements/SwitchContent';
|
||||
|
||||
// Selectors
|
||||
import { editableFormSelector } from '../../selectors';
|
||||
import { editableFormSelector } from '../data/selectors';
|
||||
|
||||
class Name extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -13,7 +13,7 @@ import EmptyContent from './elements/EmptyContent';
|
||||
import SwitchContent from './elements/SwitchContent';
|
||||
|
||||
// Selectors
|
||||
import { preferredLanguageSelector } from '../../selectors';
|
||||
import { preferredLanguageSelector } from '../data/selectors';
|
||||
|
||||
class PreferredLanguage extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { Button, Dropdown } from '@edx/paragon';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-i18n';
|
||||
|
||||
import { ReactComponent as DefaultAvatar } from '../../assets/avatar.svg';
|
||||
import { ReactComponent as DefaultAvatar } from '../assets/avatar.svg';
|
||||
|
||||
import messages from './ProfileAvatar.messages';
|
||||
|
||||
@@ -16,7 +16,7 @@ import EmptyContent from './elements/EmptyContent';
|
||||
import SwitchContent from './elements/SwitchContent';
|
||||
|
||||
// Selectors
|
||||
import { editableFormSelector } from '../../selectors';
|
||||
import { editableFormSelector } from '../data/selectors';
|
||||
|
||||
const platformDisplayInfo = {
|
||||
facebook: {
|
||||
@@ -1,9 +1,5 @@
|
||||
import profileReducer from './reducers';
|
||||
import profileSaga from './sagas';
|
||||
import ConnectedProfilePage from './components/ProfilePage';
|
||||
|
||||
export {
|
||||
ConnectedProfilePage,
|
||||
profileReducer,
|
||||
profileSaga,
|
||||
};
|
||||
export { default as reducer } from './data/reducers';
|
||||
export { default as saga } from './data/sagas';
|
||||
export { default as ProfilePage } from './ProfilePage';
|
||||
export { default as NotFoundPage } from './NotFoundPage';
|
||||
export { default as messages } from './ProfilePage.messages';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import { userAccount } from '@edx/frontend-auth';
|
||||
|
||||
import { profileReducer } from './profile';
|
||||
import { reducer as profileReducer } from './profile';
|
||||
|
||||
const createRootReducer = () =>
|
||||
combineReducers({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { all } from 'redux-saga/effects';
|
||||
|
||||
import { profileSaga } from './profile';
|
||||
import { saga as profileSaga } from './profile';
|
||||
|
||||
export default function* rootSaga() {
|
||||
yield all([
|
||||
|
||||
@@ -55,6 +55,9 @@ module.exports = Merge.smart(commonConfig, {
|
||||
plugins: () => [PostCssRtlPlugin()],
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'resolve-url-loader',
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader', // compiles Sass to CSS
|
||||
options: {
|
||||
|
||||
@@ -67,11 +67,14 @@ module.exports = Merge.smart(commonConfig, {
|
||||
options: {
|
||||
plugins: () => [
|
||||
PostCssRtlPlugin(),
|
||||
PostCssAutoprefixerPlugin({ grid: true, browsers: ['>1%'] }),
|
||||
PostCssAutoprefixerPlugin({ grid: true }),
|
||||
CssNano(),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'resolve-url-loader',
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader', // compiles Sass to CSS
|
||||
options: {
|
||||
|
||||
Reference in New Issue
Block a user