refactor: use raw html form elements (#123)
* refactor: use raw html for form elements * test: update snapshot to reflect minor changes in html output * refactor: replace all reactstrap with paragon or html * test: update snapshot * fix: add a noop to the button in async button if needed This is a flaw in the Button component in Paragon. It calls props.onClick even if it doesn't exist. * refactor: use classnames for toggling class names * fix: remove unneeded ids. fix some form markup * fix: update snapshot to reflect removed labels
This commit is contained in:
@@ -16,6 +16,13 @@
|
||||
"jsx-a11y/anchor-is-valid": [ "error", {
|
||||
"components": [ "Link" ],
|
||||
"specialLink": [ "to" ]
|
||||
}],
|
||||
"jsx-a11y/label-has-for": [ 2, {
|
||||
"components": [ "label" ],
|
||||
"required": {
|
||||
"some": [ "nesting", "id" ]
|
||||
},
|
||||
"allowChildren": false
|
||||
}]
|
||||
},
|
||||
"env": {
|
||||
|
||||
40
package-lock.json
generated
40
package-lock.json
generated
@@ -13933,16 +13933,6 @@
|
||||
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isfunction": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
|
||||
"integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
|
||||
},
|
||||
"lodash.isobject": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
|
||||
"integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0="
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
@@ -14009,11 +13999,6 @@
|
||||
"integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.tonumber": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.tonumber/-/lodash.tonumber-4.0.3.tgz",
|
||||
"integrity": "sha1-C5azGzVnJ5Prf1pj7nkfG56QJdk="
|
||||
},
|
||||
"lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
@@ -18792,15 +18777,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-popper": {
|
||||
"version": "0.10.4",
|
||||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.10.4.tgz",
|
||||
"integrity": "sha1-rypBXqIike3VBGeNev2opu4ylao=",
|
||||
"requires": {
|
||||
"popper.js": "^1.14.1",
|
||||
"prop-types": "^15.6.1"
|
||||
}
|
||||
},
|
||||
"react-proptype-conditional-require": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz",
|
||||
@@ -18912,22 +18888,6 @@
|
||||
"integrity": "sha512-HH2N/b5tRxh7ypIgCRsiBl/CTxRkTEPf9DhIstaM6hne4WiwM5/bBbWuvVlRZc/i3FdqZED3pZ//6n4mtxma4w==",
|
||||
"dev": true
|
||||
},
|
||||
"reactstrap": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-7.1.0.tgz",
|
||||
"integrity": "sha512-wtc4RkgnGn1TsZ0AxOZ2OqT+b8YmCWZj/tErPujWLepxzlEEhveZGC+uDerdaHVSAzJUP2DTk605iper7hutQQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"classnames": "^2.2.3",
|
||||
"lodash.isfunction": "^3.0.9",
|
||||
"lodash.isobject": "^3.0.2",
|
||||
"lodash.tonumber": "^4.0.3",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-lifecycles-compat": "^3.0.4",
|
||||
"react-popper": "^0.10.4",
|
||||
"react-transition-group": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.1.4",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"bootstrap": "^4.2.1",
|
||||
"classnames": "^2.2.5",
|
||||
"classnames": "^2.2.6",
|
||||
"connected-react-router": "^5.0.1",
|
||||
"email-prop-type": "^1.1.5",
|
||||
"font-awesome": "^4.7.0",
|
||||
@@ -59,7 +59,6 @@
|
||||
"react-router": "^4.2.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-transition-group": "^2.5.3",
|
||||
"reactstrap": "^7.1.0",
|
||||
"redux": "^4.0.1",
|
||||
"redux-devtools-extension": "^2.13.2",
|
||||
"redux-logger": "^3.0.6",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Button, Col, Container, Row } from 'reactstrap';
|
||||
import { Button } from '@edx/paragon';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
@@ -12,12 +12,9 @@ export default class ErrorPage extends Component {
|
||||
const { username } = apiClient.getAuthenticationState().authentication;
|
||||
|
||||
return (
|
||||
<Container
|
||||
fluid
|
||||
className="py-5 justify-content-center align-items-start text-center"
|
||||
>
|
||||
<Row>
|
||||
<Col>
|
||||
<div className="container-fluid py-5 justify-content-center align-items-start text-center">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<p className="my-0 py-5 text-muted">
|
||||
<FormattedMessage
|
||||
id="profile.error.message.text"
|
||||
@@ -25,22 +22,25 @@ export default class ErrorPage extends Component {
|
||||
description="error message when an unexpected error occurs"
|
||||
/>
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Link to={`/u/${username}`}>
|
||||
<Button color="primary">
|
||||
<FormattedMessage
|
||||
id="profile.error.button.text"
|
||||
defaultMessage="Return to Your Profile"
|
||||
description="text for button that navigates back to your profile page after an error has occured"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
buttonType="primary"
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="profile.error.button.text"
|
||||
defaultMessage="Return to Your Profile"
|
||||
description="text for button that navigates back to your profile page after an error has occured"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Container } from 'reactstrap';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export default class NotFoundPage extends Component {
|
||||
@@ -7,10 +6,7 @@ export default class NotFoundPage extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container
|
||||
fluid
|
||||
className="d-flex py-5 justify-content-center align-items-start text-center"
|
||||
>
|
||||
<div className="container-fluid d-flex py-5 justify-content-center align-items-start text-center">
|
||||
<p
|
||||
className="my-0 py-5 text-muted"
|
||||
style={{ maxWidth: '32em' }}
|
||||
@@ -21,7 +17,7 @@ export default class NotFoundPage extends Component {
|
||||
description="error message when a page does not exist"
|
||||
/>
|
||||
</p>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Container, Row, Col, Alert, Button } from 'reactstrap';
|
||||
import { StatusAlert, Hyperlink } from '@edx/paragon';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
|
||||
@@ -89,9 +89,12 @@ export class ProfilePage extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<Button color="primary" href={configuration.VIEW_MY_RECORDS_URL} target="blank">
|
||||
{this.props.intl.formatMessage(messages['profile.viewMyRecords'])}
|
||||
</Button>
|
||||
<Hyperlink
|
||||
className="btn btn-primary"
|
||||
destination={configuration.VIEW_MY_RECORDS_URL}
|
||||
target="_blank"
|
||||
content={this.props.intl.formatMessage(messages['profile.viewMyRecords'])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,11 +119,11 @@ export class ProfilePage extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col md={4} lg={3}>
|
||||
<Alert color="danger">{photoUploadError.userMessage}</Alert>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="row">
|
||||
<div className="col-md-4 col-lg-3">
|
||||
<StatusAlert alertType="danger" dialog={photoUploadError.userMessage} dismissible={false} open />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,9 +163,9 @@ export class ProfilePage extends React.Component {
|
||||
return (
|
||||
<div className="profile-page">
|
||||
<Banner />
|
||||
<Container fluid>
|
||||
<Row className="align-items-center pt-4 mb-4 pt-md-0 mb-md-0">
|
||||
<Col xs="auto" md={4} lg={3}>
|
||||
<div className="container-fluid">
|
||||
<div className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0">
|
||||
<div className="col-auto col-md-4 col-lg-3">
|
||||
<div className="d-flex align-items-center d-md-block">
|
||||
<ProfileAvatar
|
||||
className="mb-md-3"
|
||||
@@ -174,19 +177,19 @@ export class ProfilePage extends React.Component {
|
||||
isEditable={this.props.isCurrentUserProfile && !requiresParentalConsent}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="pl-0">
|
||||
</div>
|
||||
<div className="col pl-0">
|
||||
<div className="d-md-none">
|
||||
{this.renderHeadingLockup()}
|
||||
</div>
|
||||
<div className="d-none d-md-block float-right">
|
||||
{this.renderViewMyRecordsButton()}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
{this.renderPhotoUploadErrorMessage()}
|
||||
<Row>
|
||||
<Col md={4} lg={4}>
|
||||
<div className="row">
|
||||
<div className="col-md-4 col-lg-4">
|
||||
<div className="d-none d-md-block mb-4">
|
||||
{this.renderHeadingLockup()}
|
||||
</div>
|
||||
@@ -224,8 +227,8 @@ export class ProfilePage extends React.Component {
|
||||
formId="socialLinks"
|
||||
{...commonFormProps}
|
||||
/>
|
||||
</Col>
|
||||
<Col md={8} lg={{ size: 7, offset: 1 }} className="pt-md-3">
|
||||
</div>
|
||||
<div className="pt-md-3 col-md-8 col-lg-7 offset-lg-1">
|
||||
{shouldShowAgeMessage ? <AgeMessage accountURL="#account" /> : null}
|
||||
<Bio
|
||||
bio={bio}
|
||||
@@ -238,9 +241,9 @@ export class ProfilePage extends React.Component {
|
||||
formId="certificates"
|
||||
{...commonFormProps}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert } from 'reactstrap';
|
||||
import { StatusAlert } from '@edx/paragon';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { configuration } from '../../config/environment';
|
||||
@@ -8,27 +8,34 @@ const { ACCOUNT_SETTINGS_URL } = configuration;
|
||||
|
||||
function AgeMessage() {
|
||||
return (
|
||||
<Alert color="info">
|
||||
<FormattedMessage
|
||||
id="profile.age.headline"
|
||||
defaultMessage="Your profile cannot be shared."
|
||||
description="error message"
|
||||
tagName="h6"
|
||||
/>
|
||||
<FormattedMessage
|
||||
id="profile.age.details"
|
||||
defaultMessage="To share your profile with other edX learners, you must confirm that you are over the age of 13."
|
||||
description="error message"
|
||||
tagName="p"
|
||||
/>
|
||||
<a href={ACCOUNT_SETTINGS_URL}>
|
||||
<FormattedMessage
|
||||
id="profile.age.set.date"
|
||||
defaultMessage="Set your date of birth"
|
||||
description="label on a link to set birthday"
|
||||
/>
|
||||
</a>
|
||||
</Alert>
|
||||
<StatusAlert
|
||||
alertType="info"
|
||||
dialog={
|
||||
<React.Fragment>
|
||||
<FormattedMessage
|
||||
id="profile.age.headline"
|
||||
defaultMessage="Your profile cannot be shared."
|
||||
description="error message"
|
||||
tagName="h6"
|
||||
/>
|
||||
<FormattedMessage
|
||||
id="profile.age.details"
|
||||
defaultMessage="To share your profile with other edX learners, you must confirm that you are over the age of 13."
|
||||
description="error message"
|
||||
tagName="p"
|
||||
/>
|
||||
<a href={ACCOUNT_SETTINGS_URL}>
|
||||
<FormattedMessage
|
||||
id="profile.age.set.date"
|
||||
defaultMessage="Set your date of birth"
|
||||
description="label on a link to set birthday"
|
||||
/>
|
||||
</a>
|
||||
</React.Fragment>
|
||||
}
|
||||
dismissible={false}
|
||||
open
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form, FormFeedback, FormGroup, Input, Label } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import messages from './Bio.messages';
|
||||
|
||||
@@ -55,22 +55,21 @@ class Bio extends React.Component {
|
||||
cases={{
|
||||
editing: (
|
||||
<div role="dialog" aria-labelledby={`${formId}-label`}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for={formId} id={`${formId}-label`}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label htmlFor={formId}>
|
||||
{intl.formatMessage(messages['profile.bio.about.me'])}
|
||||
</Label>
|
||||
<Input
|
||||
type="textarea"
|
||||
</label>
|
||||
<textarea
|
||||
className={classNames('form-control', { 'is-invalid': Boolean(error) })}
|
||||
id={formId}
|
||||
name={formId}
|
||||
value={bio || ''}
|
||||
invalid={error != null}
|
||||
value={bio}
|
||||
onChange={this.handleChange}
|
||||
aria-describedby={`${formId}-error-feedback`}
|
||||
/>
|
||||
<FormFeedback id={`${formId}-error-feedback`}>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
<p className="invalid-feedback" id={`${formId}-error-feedback`}>{error}</p>
|
||||
</div>
|
||||
<FormControls
|
||||
visibilityId="visibilityBio"
|
||||
saveState={saveState}
|
||||
@@ -78,7 +77,7 @@ class Bio extends React.Component {
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
),
|
||||
editable: (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, intlShape, FormattedDate, FormattedMessage } from 'react-intl';
|
||||
import { Row, Col, Card, CardBody, CardTitle, Button, Form } from 'reactstrap';
|
||||
import { Hyperlink } from '@edx/paragon';
|
||||
import { connect } from 'react-redux';
|
||||
import get from 'lodash.get';
|
||||
|
||||
@@ -66,14 +66,14 @@ class Certificates extends React.Component {
|
||||
})(certificateType);
|
||||
|
||||
return (
|
||||
<Col key={downloadUrl} sm={6} className="d-flex align-items-stretch">
|
||||
<Card className="mb-4 certificate flex-grow-1">
|
||||
<div key={downloadUrl} className="col col-sm-6 d-flex align-items-stretch">
|
||||
<div className="card mb-4 certificate flex-grow-1">
|
||||
<div
|
||||
className="certificate-type-illustration"
|
||||
style={{ backgroundImage: `url(${certificateIllustration})` }}
|
||||
/>
|
||||
<CardBody className="d-flex flex-column">
|
||||
<CardTitle>
|
||||
<div className="card-body d-flex flex-column">
|
||||
<div className="card-title">
|
||||
<p className="small mb-0">
|
||||
{intl.formatMessage(get(
|
||||
messages,
|
||||
@@ -82,7 +82,7 @@ class Certificates extends React.Component {
|
||||
))}
|
||||
</p>
|
||||
<h4 className="certificate-title">{courseDisplayName}</h4>
|
||||
</CardTitle>
|
||||
</div>
|
||||
<p className="small mb-0">
|
||||
<FormattedMessage
|
||||
id="profile.certificate.organization.label"
|
||||
@@ -101,13 +101,16 @@ class Certificates extends React.Component {
|
||||
/>
|
||||
</p>
|
||||
<div>
|
||||
<Button outline color="primary" href={downloadUrl} target="blank">
|
||||
{intl.formatMessage(messages['profile.certificates.view.certificate'])}
|
||||
</Button>
|
||||
<Hyperlink
|
||||
className="btn btn-outline-primary"
|
||||
destination={downloadUrl}
|
||||
target="_blank"
|
||||
content={intl.formatMessage(messages['profile.certificates.view.certificate'])}
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,7 +124,7 @@ class Certificates extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<Row className="align-items-stretch">{this.props.certificates.map(certificate => this.renderCertificate(certificate))}</Row>
|
||||
<div className="row align-items-stretch">{this.props.certificates.map(certificate => this.renderCertificate(certificate))}</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -137,7 +140,7 @@ class Certificates extends React.Component {
|
||||
cases={{
|
||||
editing: (
|
||||
<div role="dialog" aria-labelledby="course-certificates-label">
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<EditableItemHeader
|
||||
headingId="course-certificates-label"
|
||||
content={intl.formatMessage(messages['profile.certificates.my.certificates'])}
|
||||
@@ -150,7 +153,7 @@ class Certificates extends React.Component {
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
),
|
||||
editable: (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form, FormFeedback, FormGroup, Input, Label } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import messages from './Country.messages';
|
||||
|
||||
@@ -66,27 +66,26 @@ class Country extends React.Component {
|
||||
cases={{
|
||||
editing: (
|
||||
<div role="dialog" aria-labelledby={`${formId}-label`}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for="country" id={`${formId}-label`}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label htmlFor="country">
|
||||
{intl.formatMessage(messages['profile.country.label'])}
|
||||
</Label>
|
||||
<Input
|
||||
</label>
|
||||
<select
|
||||
className={classNames('form-control', 'w-100', { 'is-invalid': Boolean(error) })}
|
||||
type="select"
|
||||
id={formId}
|
||||
name={formId}
|
||||
className="w-100"
|
||||
value={country}
|
||||
invalid={error != null}
|
||||
onChange={this.handleChange}
|
||||
aria-describedby={`${formId}-error-feedback`}
|
||||
>
|
||||
{sortedCountries.map(({ code, name }) => (
|
||||
<option key={code} value={code}>{name}</option>
|
||||
))}
|
||||
</Input>
|
||||
<FormFeedback id={`${formId}-error-feedback`}>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
</select>
|
||||
<p className="invalid-feedback" id={`${formId}-error-feedback`}>{error}</p>
|
||||
</div>
|
||||
<FormControls
|
||||
visibilityId="visibilityCountry"
|
||||
saveState={saveState}
|
||||
@@ -94,7 +93,7 @@ class Country extends React.Component {
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
),
|
||||
editable: (
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form, FormFeedback, FormGroup, Input, Label } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import get from 'lodash.get';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import messages from './Education.messages';
|
||||
|
||||
@@ -62,18 +62,16 @@ class Education extends React.Component {
|
||||
cases={{
|
||||
editing: (
|
||||
<div role="dialog" aria-labelledby={`${formId}-label`}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for="education" id={`${formId}-label`}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label htmlFor="education">
|
||||
{intl.formatMessage(messages['profile.education.education'])}
|
||||
</Label>
|
||||
<Input
|
||||
type="select"
|
||||
</label>
|
||||
<select
|
||||
className={classNames('form-control', 'w-100', { 'is-invalid': Boolean(error) })}
|
||||
id={formId}
|
||||
name={formId}
|
||||
className="w-100"
|
||||
value={education}
|
||||
invalid={error != null}
|
||||
onChange={this.handleChange}
|
||||
aria-describedby={`${formId}-error-feedback`}
|
||||
>
|
||||
@@ -86,9 +84,9 @@ class Education extends React.Component {
|
||||
))}
|
||||
</option>
|
||||
))}
|
||||
</Input>
|
||||
<FormFeedback id={`${formId}-error-feedback`}>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
</select>
|
||||
<p className="invalid-feedback" id={`${formId}-error-feedback`}>{error}</p>
|
||||
</div>
|
||||
<FormControls
|
||||
visibilityId="visibilityEducation"
|
||||
saveState={saveState}
|
||||
@@ -96,7 +94,7 @@ class Education extends React.Component {
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
),
|
||||
editable: (
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form, FormFeedback, FormGroup, FormText, Label } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
|
||||
@@ -48,7 +47,7 @@ class Name extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, name, visibilityName, editMode, saveState, error, intl,
|
||||
formId, name, visibilityName, editMode, saveState, intl,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -58,11 +57,9 @@ class Name extends React.Component {
|
||||
cases={{
|
||||
editing: (
|
||||
<div role="dialog" aria-labelledby={`${formId}-label`}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for="name" id={`${formId}-label`}>
|
||||
{intl.formatMessage(messages['profile.name.full.name'])}
|
||||
</Label>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group">
|
||||
<EditableItemHeader content={intl.formatMessage(messages['profile.name.full.name'])} />
|
||||
{/*
|
||||
This isn't a mistake - the name field should not be editable. But if it were,
|
||||
you'd find the original code got deleted in the commit which added this comment.
|
||||
@@ -72,11 +69,10 @@ class Name extends React.Component {
|
||||
such to fully get rid of it.
|
||||
*/}
|
||||
<p className="h5">{name}</p>
|
||||
<FormText id={`${formId}-help-text`}>
|
||||
<small className="form-text text-muted" id={`${formId}-help-text`}>
|
||||
{intl.formatMessage(messages['profile.name.details'])}
|
||||
</FormText>
|
||||
<FormFeedback id={`${formId}-error-feedback`}>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
</small>
|
||||
</div>
|
||||
<FormControls
|
||||
visibilityId="visibilityName"
|
||||
saveState={saveState}
|
||||
@@ -84,7 +80,7 @@ class Name extends React.Component {
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
),
|
||||
editable: (
|
||||
@@ -97,9 +93,9 @@ class Name extends React.Component {
|
||||
visibility={visibilityName}
|
||||
/>
|
||||
<p className="h5">{name}</p>
|
||||
<FormText>
|
||||
<small className="form-text text-muted">
|
||||
{intl.formatMessage(messages['profile.name.details'])}
|
||||
</FormText>
|
||||
</small>
|
||||
</React.Fragment>
|
||||
),
|
||||
empty: (
|
||||
@@ -108,9 +104,9 @@ class Name extends React.Component {
|
||||
<EmptyContent onClick={this.handleOpen}>
|
||||
{intl.formatMessage(messages['profile.name.empty'])}
|
||||
</EmptyContent>
|
||||
<FormText>
|
||||
<small className="form-text text-muted">
|
||||
{intl.formatMessage(messages['profile.name.details'])}
|
||||
</FormText>
|
||||
</small>
|
||||
</React.Fragment>
|
||||
),
|
||||
static: (
|
||||
@@ -137,7 +133,6 @@ Name.propTypes = {
|
||||
visibilityName: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
|
||||
// Actions
|
||||
changeHandler: PropTypes.func.isRequired,
|
||||
@@ -154,7 +149,6 @@ Name.defaultProps = {
|
||||
saveState: null,
|
||||
name: null,
|
||||
visibilityName: 'private',
|
||||
error: null,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { Spinner } from 'reactstrap';
|
||||
|
||||
import Banner from './elements/Banner';
|
||||
|
||||
@@ -13,7 +12,7 @@ function PageLoading() {
|
||||
height: '50vh',
|
||||
}}
|
||||
>
|
||||
<Spinner color="primary" />
|
||||
<div className="spinner-border text-primary" role="status" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form, FormFeedback, FormGroup, Input, Label } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import messages from './PreferredLanguage.messages';
|
||||
|
||||
@@ -76,27 +76,25 @@ class PreferredLanguage extends React.Component {
|
||||
cases={{
|
||||
editing: (
|
||||
<div role="dialog" aria-labelledby={`${formId}-label`}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for={formId} id={`${formId}-label`}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label htmlFor={formId}>
|
||||
{intl.formatMessage(messages['profile.preferredlanguage.label'])}
|
||||
</Label>
|
||||
<Input
|
||||
type="select"
|
||||
</label>
|
||||
<select
|
||||
id={formId}
|
||||
name={formId}
|
||||
className="w-100"
|
||||
className={classNames('form-control', 'w-100', { 'is-invalid': Boolean(error) })}
|
||||
value={value}
|
||||
invalid={error != null}
|
||||
onChange={this.handleChange}
|
||||
aria-describedby={`${formId}-error-feedback`}
|
||||
>
|
||||
{sortedLanguages.map(({ code, name }) => (
|
||||
<option key={code} value={code}>{name}</option>
|
||||
))}
|
||||
</Input>
|
||||
<FormFeedback id={`${formId}-error-feedback`}>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
</select>
|
||||
<p className="invalid-feedback" id={`${formId}-error-feedback`}>{error}</p>
|
||||
</div>
|
||||
<FormControls
|
||||
visibilityId="visibilityLanguageProficiencies"
|
||||
saveState={saveState}
|
||||
@@ -104,7 +102,7 @@ class PreferredLanguage extends React.Component {
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
),
|
||||
editable: (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Input, Spinner, Dropdown, DropdownToggle, DropdownMenu, DropdownItem, Button } from 'reactstrap';
|
||||
import { Button, Dropdown } from '@edx/paragon';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
|
||||
import { ReactComponent as DefaultAvatar } from '../../assets/avatar.svg';
|
||||
@@ -11,10 +11,6 @@ class ProfileAvatar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
dropdownOpen: false,
|
||||
};
|
||||
|
||||
this.fileInput = React.createRef();
|
||||
this.form = React.createRef();
|
||||
|
||||
@@ -22,7 +18,6 @@ class ProfileAvatar extends React.Component {
|
||||
this.onClickDelete = this.onClickDelete.bind(this);
|
||||
this.onChangeInput = this.onChangeInput.bind(this);
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.toggleDropdown = this.toggleDropdown.bind(this);
|
||||
}
|
||||
|
||||
onClickUpload() {
|
||||
@@ -43,19 +38,13 @@ class ProfileAvatar extends React.Component {
|
||||
this.form.current.reset();
|
||||
}
|
||||
|
||||
toggleDropdown() {
|
||||
this.setState({
|
||||
dropdownOpen: !this.state.dropdownOpen,
|
||||
});
|
||||
}
|
||||
|
||||
renderPending() {
|
||||
return (
|
||||
<div
|
||||
className="position-absolute w-100 h-100 d-flex justify-content-center align-items-center rounded-circle"
|
||||
style={{ backgroundColor: 'rgba(0,0,0,.65)' }}
|
||||
>
|
||||
<Spinner color="primary" />
|
||||
<div className="spinner-border text-primary" role="status" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -64,49 +53,51 @@ class ProfileAvatar extends React.Component {
|
||||
if (this.props.isDefault) {
|
||||
return (
|
||||
<Button
|
||||
className="text-white btn-block"
|
||||
color="link"
|
||||
size="sm"
|
||||
className={'text-white btn-block btn-sm'.split(' ')}
|
||||
buttonType="link"
|
||||
onClick={this.onClickUpload}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.upload-button"
|
||||
defaultMessage="Upload Photo"
|
||||
description="Upload photo button"
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
isOpen={this.state.dropdownOpen}
|
||||
toggle={this.toggleDropdown}
|
||||
>
|
||||
<DropdownToggle className="text-white btn-block" color="link" size="sm">
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.change-button"
|
||||
defaultMessage="Change"
|
||||
description="Change photo button"
|
||||
/>
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
<DropdownItem onClick={this.onClickUpload}>
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.upload-button"
|
||||
defaultMessage="Upload Photo"
|
||||
description="Upload photo button"
|
||||
/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this.onClickDelete}>
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.remove.button"
|
||||
defaultMessage="Remove"
|
||||
description="Remove photo button"
|
||||
/>
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
buttonType="primary"
|
||||
title={(
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.change-button"
|
||||
defaultMessage="Change"
|
||||
description="Change photo button"
|
||||
/>
|
||||
)}
|
||||
menuItems={[
|
||||
(
|
||||
<button className="dropdown-item" onClick={this.onClickUpload}>
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.upload-button"
|
||||
defaultMessage="Upload Photo"
|
||||
description="Upload photo button"
|
||||
/>
|
||||
</button>
|
||||
),
|
||||
(
|
||||
<button className="dropdown-item" onClick={this.onClickDelete}>
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.remove.button"
|
||||
defaultMessage="Remove"
|
||||
description="Remove photo button"
|
||||
/>
|
||||
</button>
|
||||
),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -148,9 +139,9 @@ class ProfileAvatar extends React.Component {
|
||||
encType="multipart/form-data"
|
||||
>
|
||||
{/* The name of this input must be 'file' */}
|
||||
<Input
|
||||
className="d-none"
|
||||
innerRef={this.fileInput}
|
||||
<input
|
||||
className="d-none form-control-file"
|
||||
ref={this.fileInput}
|
||||
type="file"
|
||||
name="file"
|
||||
id="photo-file"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form, Input, Label, Alert } from 'reactstrap';
|
||||
import { StatusAlert } from '@edx/paragon';
|
||||
import { connect } from 'react-redux';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
|
||||
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import messages from './SocialLinks.messages';
|
||||
|
||||
@@ -158,14 +159,14 @@ class SocialLinks extends React.Component {
|
||||
),
|
||||
editing: (
|
||||
<div role="dialog" aria-labelledby="social-links-label">
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<EditableItemHeader
|
||||
headingId="social-links-label"
|
||||
content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
|
||||
/>
|
||||
{/* TODO: Replace this alert with per-field errors. Needs API update. */}
|
||||
<div id="social-error-feedback">
|
||||
{error !== null ? <Alert color="danger">{error}</Alert> : null}
|
||||
{error !== null ? <StatusAlert alertType="danger" dialog={error} dismissible={false} open /> : null}
|
||||
</div>
|
||||
<ul className="list-unstyled">
|
||||
{socialLinks.map(({ platform, socialLink }) => (
|
||||
@@ -186,7 +187,7 @@ class SocialLinks extends React.Component {
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
@@ -282,14 +283,14 @@ function EditingListItem({
|
||||
}) {
|
||||
return (
|
||||
<li className="form-group">
|
||||
<Label for={`social-${platform}`}>{name}</Label>
|
||||
<Input
|
||||
<label htmlFor={`social-${platform}`}>{name}</label>
|
||||
<input
|
||||
className={classNames('form-control', { 'is-invalid': Boolean(error) })}
|
||||
type="text"
|
||||
id={`social-${platform}`}
|
||||
name={platform}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
invalid={error != null}
|
||||
aria-describedby="social-error-feedback"
|
||||
/>
|
||||
</li>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Icon } from '@edx/paragon';
|
||||
import { Button, Spinner } from 'reactstrap';
|
||||
import { Icon, Button } from '@edx/paragon';
|
||||
|
||||
function AsyncActionButton({
|
||||
onClick,
|
||||
@@ -16,7 +15,11 @@ function AsyncActionButton({
|
||||
const renderIcon = () => {
|
||||
if (variant === 'error') return <Icon className="icon fa fa-times-circle" />;
|
||||
if (variant === 'complete') return <Icon className="icon fa fa-check-circle" />;
|
||||
if (variant === 'pending') return <Spinner size="sm" color="white" />;
|
||||
if (variant === 'pending') {
|
||||
return (
|
||||
<div className="spinner-border-sm spinner-border text-white" role="status" />
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -29,12 +32,14 @@ function AsyncActionButton({
|
||||
return labels.default;
|
||||
};
|
||||
|
||||
const isDisabled = variant === 'pending' || variant === 'complete' || variant === 'error';
|
||||
|
||||
return (
|
||||
<Button
|
||||
type={type}
|
||||
aria-live="assertive"
|
||||
onClick={onClick}
|
||||
disabled={variant === 'pending' || variant === 'complete' || variant === 'error'}
|
||||
onClick={onClick || (() => {})}
|
||||
disabled={isDisabled}
|
||||
className={classNames(
|
||||
'btn-async-action',
|
||||
'd-inline-flex align-items-center justify-content-center',
|
||||
@@ -43,16 +48,20 @@ function AsyncActionButton({
|
||||
'btn-state-pending': variant === 'pending',
|
||||
'btn-state-complete': variant === 'complete',
|
||||
'btn-state-error': variant === 'error',
|
||||
[`btn-${color}`]: color != null,
|
||||
disabled: isDisabled,
|
||||
},
|
||||
)}
|
||||
color={color}
|
||||
).split(' ')}
|
||||
style={style}
|
||||
>
|
||||
<span aria-hidden className="icon-state d-inline-flex justify-content-start">
|
||||
{renderIcon()}
|
||||
</span>
|
||||
{renderLabel()}
|
||||
</Button>
|
||||
label={(
|
||||
<React.Fragment>
|
||||
<span aria-hidden className="icon-state d-inline-flex justify-content-start">
|
||||
{renderIcon()}
|
||||
</span>
|
||||
{renderLabel()}
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Label, FormGroup } from 'reactstrap';
|
||||
import { Button } from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
|
||||
import messages from './FormControls.messages';
|
||||
@@ -16,10 +16,10 @@ function FormControls({
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FormGroup className="mb-4">
|
||||
<Label className="mb-1" size="sm" for={visibilityId}>
|
||||
<div className="mb-4 form-group">
|
||||
<label className="mb-1 col-form-label-sm" htmlFor={visibilityId}>
|
||||
{intl.formatMessage(messages['profile.formcontrols.who.can.see'])}
|
||||
</Label>
|
||||
</label>
|
||||
<VisibilitySelect
|
||||
id={visibilityId}
|
||||
className="w-auto"
|
||||
@@ -28,8 +28,8 @@ function FormControls({
|
||||
value={visibility}
|
||||
onChange={changeHandler}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<AsyncActionButton
|
||||
type="submit"
|
||||
variant={buttonState}
|
||||
@@ -39,10 +39,12 @@ function FormControls({
|
||||
complete: intl.formatMessage(messages['profile.formcontrols.button.saved']),
|
||||
}}
|
||||
/>
|
||||
<Button color="link" onClick={cancelHandler}>
|
||||
{intl.formatMessage(messages['profile.formcontrols.button.cancel'])}
|
||||
</Button>
|
||||
</FormGroup>
|
||||
<Button
|
||||
buttonType="link"
|
||||
onClick={cancelHandler}
|
||||
label={intl.formatMessage(messages['profile.formcontrols.button.cancel'])}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Input } from 'reactstrap';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faEyeSlash, faEye } from '@fortawesome/free-regular-svg-icons';
|
||||
@@ -41,14 +40,14 @@ function VisibilitySelect({ intl, className, ...props }) {
|
||||
<span className="d-inline-block ml-1 mr-2" style={{ width: '1.5rem' }}>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
</span>
|
||||
<Input className="d-inline-block w-auto" {...props} type="select">
|
||||
<select className="d-inline-block w-auto form-control" {...props}>
|
||||
<option key="private" value="private">
|
||||
{intl.formatMessage(messages['profile.visibility.who.just.me'])}
|
||||
</option>
|
||||
<option key="all_users" value="all_users">
|
||||
{intl.formatMessage(messages['profile.visibility.who.everyone'])}
|
||||
</option>
|
||||
</Input>
|
||||
</select>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,7 @@ exports[`<ProfilePage /> Renders correctly in various states app loading 1`] = `
|
||||
<div
|
||||
className="spinner-border text-primary"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -38,7 +32,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
|
||||
className="container-fluid"
|
||||
>
|
||||
<div
|
||||
className="align-items-center pt-4 mb-4 pt-md-0 mb-md-0 row"
|
||||
className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0"
|
||||
>
|
||||
<div
|
||||
className="col-auto col-md-4 col-lg-3"
|
||||
@@ -77,7 +71,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="pl-0 col"
|
||||
className="col pl-0"
|
||||
>
|
||||
<div
|
||||
className="d-md-none"
|
||||
@@ -235,7 +229,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
className="container-fluid"
|
||||
>
|
||||
<div
|
||||
className="align-items-center pt-4 mb-4 pt-md-0 mb-md-0 row"
|
||||
className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0"
|
||||
>
|
||||
<div
|
||||
className="col-auto col-md-4 col-lg-3"
|
||||
@@ -254,14 +248,14 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
>
|
||||
<div
|
||||
className="dropdown"
|
||||
onKeyDown={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-expanded={false}
|
||||
aria-haspopup={true}
|
||||
aria-label={null}
|
||||
className="text-white btn-block btn btn-link btn-sm"
|
||||
aria-haspopup="true"
|
||||
className="btn dropdown-toggle btn-primary"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
@@ -270,17 +264,21 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
</button>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
aria-label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Change"
|
||||
description="Change photo button"
|
||||
id="profile.profileavatar.change-button"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
className="dropdown-menu"
|
||||
role="menu"
|
||||
tabIndex="-1"
|
||||
x-placement={undefined}
|
||||
>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={[Function]}
|
||||
role="menuitem"
|
||||
tabIndex="0"
|
||||
type="button"
|
||||
onKeyDown={[Function]}
|
||||
>
|
||||
<span>
|
||||
Upload Photo
|
||||
@@ -289,9 +287,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={[Function]}
|
||||
role="menuitem"
|
||||
tabIndex="0"
|
||||
type="button"
|
||||
onKeyDown={[Function]}
|
||||
>
|
||||
<span>
|
||||
Remove
|
||||
@@ -328,7 +324,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="pl-0 col"
|
||||
className="col pl-0"
|
||||
>
|
||||
<div
|
||||
className="d-md-none"
|
||||
@@ -356,14 +352,21 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
className="d-none d-md-block float-right"
|
||||
>
|
||||
<a
|
||||
aria-label={null}
|
||||
className="btn btn-primary"
|
||||
href="undefined/records"
|
||||
onClick={[Function]}
|
||||
target="blank"
|
||||
type={undefined}
|
||||
target="_blank"
|
||||
>
|
||||
View My Records
|
||||
<span>
|
||||
|
||||
<span
|
||||
aria-hidden={false}
|
||||
aria-label="Opens in a new window"
|
||||
className="fa fa-external-link"
|
||||
title="Opens in a new window"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -400,14 +403,21 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
className="d-md-none mb-4"
|
||||
>
|
||||
<a
|
||||
aria-label={null}
|
||||
className="btn btn-primary"
|
||||
href="undefined/records"
|
||||
onClick={[Function]}
|
||||
target="blank"
|
||||
type={undefined}
|
||||
target="_blank"
|
||||
>
|
||||
View My Records
|
||||
<span>
|
||||
|
||||
<span
|
||||
aria-hidden={false}
|
||||
aria-label="Opens in a new window"
|
||||
className="fa fa-external-link"
|
||||
title="Opens in a new window"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
@@ -1157,13 +1167,13 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="align-items-stretch row"
|
||||
className="row align-items-stretch"
|
||||
>
|
||||
<div
|
||||
className="d-flex align-items-stretch col-sm-6"
|
||||
className="col col-sm-6 d-flex align-items-stretch"
|
||||
>
|
||||
<div
|
||||
className="mb-4 certificate flex-grow-1 card"
|
||||
className="card mb-4 certificate flex-grow-1"
|
||||
>
|
||||
<div
|
||||
className="certificate-type-illustration"
|
||||
@@ -1174,7 +1184,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="d-flex flex-column card-body"
|
||||
className="card-body d-flex flex-column"
|
||||
>
|
||||
<div
|
||||
className="card-title"
|
||||
@@ -1217,14 +1227,21 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
aria-label={null}
|
||||
className="btn btn-outline-primary"
|
||||
href="http://www.example.com/"
|
||||
onClick={[Function]}
|
||||
target="blank"
|
||||
type={undefined}
|
||||
target="_blank"
|
||||
>
|
||||
View Certificate
|
||||
<span>
|
||||
|
||||
<span
|
||||
aria-hidden={false}
|
||||
aria-label="Opens in a new window"
|
||||
className="fa fa-external-link"
|
||||
title="Opens in a new window"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1250,7 +1267,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
className="container-fluid"
|
||||
>
|
||||
<div
|
||||
className="align-items-center pt-4 mb-4 pt-md-0 mb-md-0 row"
|
||||
className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0"
|
||||
>
|
||||
<div
|
||||
className="col-auto col-md-4 col-lg-3"
|
||||
@@ -1269,14 +1286,14 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
>
|
||||
<div
|
||||
className="dropdown"
|
||||
onKeyDown={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-expanded={false}
|
||||
aria-haspopup={true}
|
||||
aria-label={null}
|
||||
className="text-white btn-block btn btn-link btn-sm"
|
||||
aria-haspopup="true"
|
||||
className="btn dropdown-toggle btn-primary"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
@@ -1285,17 +1302,21 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
</button>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
aria-label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Change"
|
||||
description="Change photo button"
|
||||
id="profile.profileavatar.change-button"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
className="dropdown-menu"
|
||||
role="menu"
|
||||
tabIndex="-1"
|
||||
x-placement={undefined}
|
||||
>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={[Function]}
|
||||
role="menuitem"
|
||||
tabIndex="0"
|
||||
type="button"
|
||||
onKeyDown={[Function]}
|
||||
>
|
||||
<span>
|
||||
Upload Photo
|
||||
@@ -1304,9 +1325,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={[Function]}
|
||||
role="menuitem"
|
||||
tabIndex="0"
|
||||
type="button"
|
||||
onKeyDown={[Function]}
|
||||
>
|
||||
<span>
|
||||
Remove
|
||||
@@ -1343,7 +1362,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="pl-0 col"
|
||||
className="col pl-0"
|
||||
>
|
||||
<div
|
||||
className="d-md-none"
|
||||
@@ -1371,14 +1390,21 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
className="d-none d-md-block float-right"
|
||||
>
|
||||
<a
|
||||
aria-label={null}
|
||||
className="btn btn-primary"
|
||||
href="undefined/records"
|
||||
onClick={[Function]}
|
||||
target="blank"
|
||||
type={undefined}
|
||||
target="_blank"
|
||||
>
|
||||
View My Records
|
||||
<span>
|
||||
|
||||
<span
|
||||
aria-hidden={false}
|
||||
aria-label="Opens in a new window"
|
||||
className="fa fa-external-link"
|
||||
title="Opens in a new window"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1415,14 +1441,21 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
className="d-md-none mb-4"
|
||||
>
|
||||
<a
|
||||
aria-label={null}
|
||||
className="btn btn-primary"
|
||||
href="undefined/records"
|
||||
onClick={[Function]}
|
||||
target="blank"
|
||||
type={undefined}
|
||||
target="_blank"
|
||||
>
|
||||
View My Records
|
||||
<span>
|
||||
|
||||
<span
|
||||
aria-hidden={false}
|
||||
aria-label="Opens in a new window"
|
||||
className="fa fa-external-link"
|
||||
title="Opens in a new window"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
@@ -2016,16 +2049,13 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
role="dialog"
|
||||
>
|
||||
<form
|
||||
className=""
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className=""
|
||||
htmlFor="bio"
|
||||
id="bio-label"
|
||||
>
|
||||
About Me
|
||||
</label>
|
||||
@@ -2037,7 +2067,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
onChange={[Function]}
|
||||
value="This is my bio"
|
||||
/>
|
||||
<div
|
||||
<p
|
||||
className="invalid-feedback"
|
||||
id="bio-error-feedback"
|
||||
/>
|
||||
@@ -2085,6 +2115,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
id="visibilityBio"
|
||||
name="visibilityBio"
|
||||
onChange={[Function]}
|
||||
type="select"
|
||||
value="all_users"
|
||||
>
|
||||
<option
|
||||
@@ -2104,11 +2135,12 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
className="form-group"
|
||||
>
|
||||
<button
|
||||
aria-label={null}
|
||||
aria-live="assertive"
|
||||
className="btn-async-action d-inline-flex align-items-center justify-content-center btn-state-pending btn btn-primary disabled"
|
||||
className="btn btn-async-action d-inline-flex align-items-center justify-content-center btn-state-pending btn-primary disabled"
|
||||
disabled={true}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
style={null}
|
||||
type="submit"
|
||||
>
|
||||
@@ -2119,20 +2151,15 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
<div
|
||||
className="spinner-border-sm spinner-border text-white"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
/>
|
||||
</span>
|
||||
Saving
|
||||
</button>
|
||||
<button
|
||||
aria-label={null}
|
||||
className="btn btn-link"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
@@ -2230,13 +2257,13 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="align-items-stretch row"
|
||||
className="row align-items-stretch"
|
||||
>
|
||||
<div
|
||||
className="d-flex align-items-stretch col-sm-6"
|
||||
className="col col-sm-6 d-flex align-items-stretch"
|
||||
>
|
||||
<div
|
||||
className="mb-4 certificate flex-grow-1 card"
|
||||
className="card mb-4 certificate flex-grow-1"
|
||||
>
|
||||
<div
|
||||
className="certificate-type-illustration"
|
||||
@@ -2247,7 +2274,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="d-flex flex-column card-body"
|
||||
className="card-body d-flex flex-column"
|
||||
>
|
||||
<div
|
||||
className="card-title"
|
||||
@@ -2290,14 +2317,21 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
aria-label={null}
|
||||
className="btn btn-outline-primary"
|
||||
href="http://www.example.com/"
|
||||
onClick={[Function]}
|
||||
target="blank"
|
||||
type={undefined}
|
||||
target="_blank"
|
||||
>
|
||||
View Certificate
|
||||
<span>
|
||||
|
||||
<span
|
||||
aria-hidden={false}
|
||||
aria-label="Opens in a new window"
|
||||
className="fa fa-external-link"
|
||||
title="Opens in a new window"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -73,6 +73,18 @@ $fa-font-path: "~font-awesome/fonts";
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user