Client/server data adapter and simpler data model. (#53)
* Client/server data adapter and simpler data passthroughs. * Parse error response and pipe to UI * Add top-of-form error display for social links * Remove save failed state from save button * Remove object deconstruction in catch * Fixing a few small bugs. * When opening and closing forms, remove drafts. * Tweak where we send account_privacy back to * Passing course cert visibility through. * Fixin’ up the tests. * Documenting weird social links behavior. * More comments.
This commit is contained in:
@@ -27,7 +27,7 @@ class Bio extends React.Component {
|
||||
|
||||
handleChange(e) {
|
||||
const { name, value } = e.target;
|
||||
this.props.changeHandler(this.props.formId, name, value);
|
||||
this.props.changeHandler(name, value);
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
@@ -45,7 +45,7 @@ class Bio extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, value, visibility, editMode, saveState, error, intl,
|
||||
formId, bio, visibilityBio, editMode, saveState, error, intl,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -61,16 +61,16 @@ class Bio extends React.Component {
|
||||
type="textarea"
|
||||
id={formId}
|
||||
name={formId}
|
||||
value={value}
|
||||
value={bio || ''}
|
||||
invalid={error != null}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormFeedback>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
<FormControls
|
||||
formId={formId}
|
||||
visibilityId="visibilityBio"
|
||||
saveState={saveState}
|
||||
visibility={visibility}
|
||||
visibility={visibilityBio}
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
@@ -82,10 +82,10 @@ class Bio extends React.Component {
|
||||
content={intl.formatMessage(messages['profile.bio.about.me'])}
|
||||
showEditButton
|
||||
onClickEdit={this.handleOpen}
|
||||
showVisibility={visibility !== null}
|
||||
visibility={visibility}
|
||||
showVisibility={visibilityBio !== null}
|
||||
visibility={visibilityBio}
|
||||
/>
|
||||
<p className="lead">{value}</p>
|
||||
<p className="lead">{bio}</p>
|
||||
</React.Fragment>
|
||||
),
|
||||
empty: (
|
||||
@@ -100,7 +100,7 @@ class Bio extends React.Component {
|
||||
static: (
|
||||
<React.Fragment>
|
||||
<EditableItemHeader content={intl.formatMessage(messages['profile.bio.about.me'])} />
|
||||
<p className="lead">{value}</p>
|
||||
<p className="lead">{bio}</p>
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
@@ -117,8 +117,8 @@ Bio.propTypes = {
|
||||
formId: PropTypes.string.isRequired,
|
||||
|
||||
// From Selector
|
||||
value: PropTypes.string,
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
bio: PropTypes.string,
|
||||
visibilityBio: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
@@ -136,8 +136,8 @@ Bio.propTypes = {
|
||||
Bio.defaultProps = {
|
||||
editMode: 'static',
|
||||
saveState: null,
|
||||
value: null,
|
||||
visibility: 'private',
|
||||
bio: null,
|
||||
visibilityBio: 'private',
|
||||
error: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class Certificates extends React.Component {
|
||||
|
||||
handleChange(e) {
|
||||
const { name, value } = e.target;
|
||||
this.props.changeHandler(this.props.formId, name, value);
|
||||
this.props.changeHandler(name, value);
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
@@ -87,7 +87,7 @@ class Certificates extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, visibility, editMode, saveState, intl,
|
||||
visibilityCourseCertificates, editMode, saveState, intl,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -100,9 +100,9 @@ class Certificates extends React.Component {
|
||||
<EditableItemHeader content={intl.formatMessage(messages['profile.certificates.my.certificates'])} />
|
||||
{this.renderCertificates()}
|
||||
<FormControls
|
||||
formId={formId}
|
||||
visibilityId="visibilityCourseCertificates"
|
||||
saveState={saveState}
|
||||
visibility={visibility}
|
||||
visibility={visibilityCourseCertificates}
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
@@ -114,8 +114,8 @@ class Certificates extends React.Component {
|
||||
content={intl.formatMessage(messages['profile.certificates.my.certificates'])}
|
||||
showEditButton
|
||||
onClickEdit={this.handleOpen}
|
||||
showVisibility={visibility !== null}
|
||||
visibility={visibility}
|
||||
showVisibility={visibilityCourseCertificates !== null}
|
||||
visibility={visibilityCourseCertificates}
|
||||
/>
|
||||
{this.renderCertificates()}
|
||||
</React.Fragment>
|
||||
@@ -152,7 +152,7 @@ Certificates.propTypes = {
|
||||
certificates: PropTypes.arrayOf(PropTypes.shape({
|
||||
title: PropTypes.string,
|
||||
})),
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
visibilityCourseCertificates: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
|
||||
@@ -169,7 +169,7 @@ Certificates.propTypes = {
|
||||
Certificates.defaultProps = {
|
||||
editMode: 'static',
|
||||
saveState: null,
|
||||
visibility: 'private',
|
||||
visibilityCourseCertificates: 'private',
|
||||
certificates: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class Country extends React.Component {
|
||||
name,
|
||||
value,
|
||||
} = e.target;
|
||||
this.props.changeHandler(this.props.formId, name, value);
|
||||
this.props.changeHandler(name, value);
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
@@ -49,7 +49,7 @@ class Country extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, value, visibility, editMode, saveState, error,
|
||||
formId, country, visibilityCountry, editMode, saveState, error,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -65,7 +65,7 @@ class Country extends React.Component {
|
||||
type="select"
|
||||
name={formId}
|
||||
className="w-100"
|
||||
value={value}
|
||||
value={country}
|
||||
invalid={error != null}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
@@ -76,9 +76,9 @@ class Country extends React.Component {
|
||||
<FormFeedback>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
<FormControls
|
||||
formId={formId}
|
||||
visibilityId="visibilityCountry"
|
||||
saveState={saveState}
|
||||
visibility={visibility}
|
||||
visibility={visibilityCountry}
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
@@ -90,10 +90,10 @@ class Country extends React.Component {
|
||||
content="Location"
|
||||
showEditButton
|
||||
onClickEdit={this.handleOpen}
|
||||
showVisibility={visibility !== null}
|
||||
visibility={visibility}
|
||||
showVisibility={visibilityCountry !== null}
|
||||
visibility={visibilityCountry}
|
||||
/>
|
||||
<h5>{ALL_COUNTRIES[value]}</h5>
|
||||
<h5>{ALL_COUNTRIES[country]}</h5>
|
||||
</React.Fragment>
|
||||
),
|
||||
empty: (
|
||||
@@ -108,7 +108,7 @@ class Country extends React.Component {
|
||||
static: (
|
||||
<React.Fragment>
|
||||
<EditableItemHeader content="Location" />
|
||||
<h5>{ALL_COUNTRIES[value]}</h5>
|
||||
<h5>{ALL_COUNTRIES[country]}</h5>
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
@@ -125,8 +125,8 @@ Country.propTypes = {
|
||||
formId: PropTypes.string.isRequired,
|
||||
|
||||
// From Selector
|
||||
value: PropTypes.string,
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
country: PropTypes.string,
|
||||
visibilityCountry: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
@@ -141,8 +141,8 @@ Country.propTypes = {
|
||||
Country.defaultProps = {
|
||||
editMode: 'static',
|
||||
saveState: null,
|
||||
value: null,
|
||||
visibility: 'private',
|
||||
country: null,
|
||||
visibilityCountry: 'private',
|
||||
error: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class Education extends React.Component {
|
||||
name,
|
||||
value,
|
||||
} = e.target;
|
||||
this.props.changeHandler(this.props.formId, name, value);
|
||||
this.props.changeHandler(name, value);
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
@@ -51,7 +51,7 @@ class Education extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, value, visibility, editMode, saveState, error, intl,
|
||||
formId, education, visibilityEducation, editMode, saveState, error, intl,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -69,7 +69,7 @@ class Education extends React.Component {
|
||||
type="select"
|
||||
name={formId}
|
||||
className="w-100"
|
||||
value={value}
|
||||
value={education}
|
||||
invalid={error != null}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
@@ -80,9 +80,9 @@ class Education extends React.Component {
|
||||
<FormFeedback>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
<FormControls
|
||||
formId={formId}
|
||||
visibilityId="visibilityEducation"
|
||||
saveState={saveState}
|
||||
visibility={visibility}
|
||||
visibility={visibilityEducation}
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
@@ -94,10 +94,10 @@ class Education extends React.Component {
|
||||
content={intl.formatMessage(messages['profile.education.education'])}
|
||||
showEditButton
|
||||
onClickEdit={this.handleOpen}
|
||||
showVisibility={visibility !== null}
|
||||
visibility={visibility}
|
||||
showVisibility={visibilityEducation !== null}
|
||||
visibility={visibilityEducation}
|
||||
/>
|
||||
<h5>{EDUCATION[value]}</h5>
|
||||
<h5>{EDUCATION[education]}</h5>
|
||||
</React.Fragment>
|
||||
),
|
||||
empty: (
|
||||
@@ -112,7 +112,7 @@ class Education extends React.Component {
|
||||
static: (
|
||||
<React.Fragment>
|
||||
<EditableItemHeader content={intl.formatMessage(messages['profile.education.education'])} />
|
||||
<h5>{EDUCATION[value]}</h5>
|
||||
<h5>{EDUCATION[education]}</h5>
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
@@ -129,8 +129,8 @@ Education.propTypes = {
|
||||
formId: PropTypes.string.isRequired,
|
||||
|
||||
// From Selector
|
||||
value: PropTypes.string,
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
education: PropTypes.string,
|
||||
visibilityEducation: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
@@ -148,8 +148,8 @@ Education.propTypes = {
|
||||
Education.defaultProps = {
|
||||
editMode: 'static',
|
||||
saveState: null,
|
||||
value: null,
|
||||
visibility: 'private',
|
||||
education: null,
|
||||
visibilityEducation: 'private',
|
||||
error: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class Name extends React.Component {
|
||||
name,
|
||||
value,
|
||||
} = e.target;
|
||||
this.props.changeHandler(this.props.formId, name, value);
|
||||
this.props.changeHandler(name, value);
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
@@ -48,7 +48,7 @@ class Name extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, value, visibility, editMode, saveState, error, intl,
|
||||
formId, name, visibilityName, editMode, saveState, error, intl,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -60,7 +60,7 @@ class Name extends React.Component {
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for="name">Full Name</Label>
|
||||
<Input type="text" name={formId} value={value} invalid={error != null} onChange={this.handleChange} />
|
||||
<Input type="text" name={formId} value={name} invalid={error != null} onChange={this.handleChange} />
|
||||
<FormText>
|
||||
<FormattedMessage
|
||||
id="profile.name.details"
|
||||
@@ -71,9 +71,9 @@ class Name extends React.Component {
|
||||
<FormFeedback>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
<FormControls
|
||||
formId={formId}
|
||||
visibilityId="visibilityName"
|
||||
saveState={saveState}
|
||||
visibility={visibility}
|
||||
visibility={visibilityName}
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
@@ -85,10 +85,10 @@ class Name extends React.Component {
|
||||
content={intl.formatMessage(messages['profile.name.full.name'])}
|
||||
showEditButton
|
||||
onClickEdit={this.handleOpen}
|
||||
showVisibility={visibility !== null}
|
||||
visibility={visibility}
|
||||
showVisibility={visibilityName !== null}
|
||||
visibility={visibilityName}
|
||||
/>
|
||||
<h5>{value}</h5>
|
||||
<h5>{name}</h5>
|
||||
</React.Fragment>
|
||||
),
|
||||
empty: (
|
||||
@@ -103,7 +103,7 @@ class Name extends React.Component {
|
||||
static: (
|
||||
<React.Fragment>
|
||||
<EditableItemHeader content={intl.formatMessage(messages['profile.name.full.name'])} />
|
||||
<h5>{value}</h5>
|
||||
<h5>{name}</h5>
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
@@ -120,8 +120,8 @@ Name.propTypes = {
|
||||
formId: PropTypes.string.isRequired,
|
||||
|
||||
// From Selector
|
||||
value: PropTypes.string,
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
name: PropTypes.string,
|
||||
visibilityName: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
@@ -139,8 +139,8 @@ Name.propTypes = {
|
||||
Name.defaultProps = {
|
||||
editMode: 'static',
|
||||
saveState: null,
|
||||
value: null,
|
||||
visibility: 'private',
|
||||
name: null,
|
||||
visibilityName: 'private',
|
||||
error: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class PreferredLanguage extends React.Component {
|
||||
value = [{ code: rawValue }];
|
||||
}
|
||||
|
||||
this.props.changeHandler(this.props.formId, name, value);
|
||||
this.props.changeHandler(name, value);
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
@@ -57,10 +57,10 @@ class PreferredLanguage extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, value: valueArr, visibility, editMode, saveState, error,
|
||||
formId, languageProficiencies, visibilityLanguageProficiencies, editMode, saveState, error,
|
||||
} = this.props;
|
||||
|
||||
const value = valueArr.length ? valueArr[0].code : '';
|
||||
const value = languageProficiencies.length ? languageProficiencies[0].code : '';
|
||||
|
||||
return (
|
||||
<SwitchContent
|
||||
@@ -92,9 +92,9 @@ class PreferredLanguage extends React.Component {
|
||||
<FormFeedback>{error}</FormFeedback>
|
||||
</FormGroup>
|
||||
<FormControls
|
||||
formId={formId}
|
||||
visibilityId="visibilityLanguageProficiencies"
|
||||
saveState={saveState}
|
||||
visibility={visibility}
|
||||
visibility={visibilityLanguageProficiencies}
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
@@ -112,8 +112,8 @@ class PreferredLanguage extends React.Component {
|
||||
)}
|
||||
showEditButton
|
||||
onClickEdit={this.handleOpen}
|
||||
showVisibility={visibility !== null}
|
||||
visibility={visibility}
|
||||
showVisibility={visibilityLanguageProficiencies !== null}
|
||||
visibility={visibilityLanguageProficiencies}
|
||||
/>
|
||||
<h5>{ALL_LANGUAGES[value]}</h5>
|
||||
</React.Fragment>
|
||||
@@ -155,13 +155,13 @@ PreferredLanguage.propTypes = {
|
||||
formId: PropTypes.string.isRequired,
|
||||
|
||||
// From Selector
|
||||
value: PropTypes.oneOfType([
|
||||
languageProficiencies: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.shape({ code: PropTypes.string })),
|
||||
// TODO: ProfilePageSelector should supply null values
|
||||
// instead of empty strings when no value exists
|
||||
PropTypes.oneOf(['']),
|
||||
]),
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
visibilityLanguageProficiencies: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
@@ -176,8 +176,8 @@ PreferredLanguage.propTypes = {
|
||||
PreferredLanguage.defaultProps = {
|
||||
editMode: 'static',
|
||||
saveState: null,
|
||||
value: null,
|
||||
visibility: 'private',
|
||||
languageProficiencies: [],
|
||||
visibilityLanguageProficiencies: 'private',
|
||||
error: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form, Input, FormFeedback } from 'reactstrap';
|
||||
import { Form, Input, FormFeedback, Alert } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
|
||||
@@ -43,24 +43,40 @@ class SocialLinks extends React.Component {
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
const {
|
||||
name,
|
||||
value,
|
||||
} = e.target;
|
||||
const { name, value } = e.target;
|
||||
|
||||
if (name !== 'visibility') {
|
||||
const updatedList = this.props.committedValue.map((socialLink) => {
|
||||
if (socialLink.platform === name) {
|
||||
return { platform: name, social_link: value };
|
||||
}
|
||||
return socialLink;
|
||||
});
|
||||
this.props.changeHandler(this.props.formId, 'socialLinks', updatedList);
|
||||
// The social links are a bit special. If we're updating them, we need to merge them
|
||||
// with any existing social link drafts, essentially sending a fresh copy of the whole
|
||||
// data structure back to the reducer. This helps the reducer stay simple and keeps
|
||||
// special cases out of it, concentrating them here, where they began.
|
||||
if (name !== 'visibilitySocialLinks') {
|
||||
this.props.changeHandler(
|
||||
'socialLinks',
|
||||
this.mergeWithDrafts({
|
||||
platform: name,
|
||||
// If it's an empty string, send it as null.
|
||||
// The empty string is just for the input. We want nulls.
|
||||
socialLink: value,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
this.props.changeHandler(this.props.formId, name, value);
|
||||
this.props.changeHandler(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
mergeWithDrafts(newSocialLink) {
|
||||
const knownPlatforms = ['twitter', 'facebook', 'linkedin'];
|
||||
const updated = [];
|
||||
knownPlatforms.forEach((platform) => {
|
||||
if (newSocialLink.platform === platform) {
|
||||
updated.push(newSocialLink);
|
||||
} else if (this.props.draftSocialLinksByPlatform[platform] !== undefined) {
|
||||
updated.push(this.props.draftSocialLinksByPlatform[platform]);
|
||||
}
|
||||
});
|
||||
return updated;
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.props.submitHandler(this.props.formId);
|
||||
@@ -76,7 +92,7 @@ class SocialLinks extends React.Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
formId, value: values, visibility, editMode, saveState, error, intl,
|
||||
socialLinks, visibilitySocialLinks, editMode, saveState, error, intl,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -86,7 +102,7 @@ class SocialLinks extends React.Component {
|
||||
cases={{
|
||||
empty: (
|
||||
<ul className="list-unstyled">
|
||||
{values.map(({ platform }) => (
|
||||
{socialLinks.map(({ platform }) => (
|
||||
<EmptyListItem
|
||||
key={platform}
|
||||
onClick={this.handleOpen}
|
||||
@@ -97,9 +113,11 @@ class SocialLinks extends React.Component {
|
||||
),
|
||||
static: (
|
||||
<React.Fragment>
|
||||
<EditableItemHeader content={intl.formatMessage(messages['profile.sociallinks.social.links'])} />
|
||||
<EditableItemHeader
|
||||
content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
|
||||
/>
|
||||
<ul className="list-unstyled">
|
||||
{values.map(({ platform, social_link: socialLink }) => (
|
||||
{socialLinks.map(({ platform, socialLink }) => (
|
||||
<StaticListItem
|
||||
key={platform}
|
||||
name={platformDisplayInfo[platform].name}
|
||||
@@ -116,11 +134,11 @@ class SocialLinks extends React.Component {
|
||||
content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
|
||||
showEditButton
|
||||
onClickEdit={this.handleOpen}
|
||||
showVisibility={visibility !== null}
|
||||
visibility={visibility}
|
||||
showVisibility={visibilitySocialLinks !== null}
|
||||
visibility={visibilitySocialLinks}
|
||||
/>
|
||||
<ul className="list-unstyled">
|
||||
{values.map(({ platform, social_link: socialLink }) => (
|
||||
{socialLinks.map(({ platform, socialLink }) => (
|
||||
<EditableListItem
|
||||
key={platform}
|
||||
platform={platform}
|
||||
@@ -134,23 +152,27 @@ class SocialLinks extends React.Component {
|
||||
),
|
||||
editing: (
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<EditableItemHeader content={intl.formatMessage(messages['profile.sociallinks.social.links'])} />
|
||||
<EditableItemHeader
|
||||
content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
|
||||
/>
|
||||
{/* TODO: Replace this alert with per-field errors. Needs API update. */}
|
||||
{error !== null ? <Alert color="danger">{error}</Alert> : null}
|
||||
<ul className="list-unstyled">
|
||||
{values.map(({ platform, social_link: socialLink }) => (
|
||||
{socialLinks.map(({ platform, socialLink }) => (
|
||||
<EditingListItem
|
||||
key={platform}
|
||||
name={platformDisplayInfo[platform].name}
|
||||
platform={platform}
|
||||
value={socialLink}
|
||||
error={error !== null ? error[platform] : null}
|
||||
/* TODO: Per-field errors: error={error !== null ? error[platform] : null} */
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
<FormControls
|
||||
formId={formId}
|
||||
visibilityId="visibilitySocialLinks"
|
||||
saveState={saveState}
|
||||
visibility={visibility}
|
||||
visibility={visibilitySocialLinks}
|
||||
cancelHandler={this.handleClose}
|
||||
changeHandler={this.handleChange}
|
||||
/>
|
||||
@@ -170,15 +192,15 @@ SocialLinks.propTypes = {
|
||||
formId: PropTypes.string.isRequired,
|
||||
|
||||
// From Selector
|
||||
value: PropTypes.arrayOf(PropTypes.shape({
|
||||
socialLinks: PropTypes.arrayOf(PropTypes.shape({
|
||||
platform: PropTypes.string,
|
||||
socialLink: PropTypes.string,
|
||||
})).isRequired,
|
||||
draftSocialLinksByPlatform: PropTypes.objectOf(PropTypes.shape({
|
||||
platform: PropTypes.string,
|
||||
socialLink: PropTypes.string,
|
||||
})),
|
||||
committedValue: PropTypes.arrayOf(PropTypes.shape({
|
||||
platform: PropTypes.string,
|
||||
socialLink: PropTypes.string,
|
||||
})),
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
visibilitySocialLinks: PropTypes.oneOf(['private', 'all_users']),
|
||||
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
||||
saveState: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
@@ -196,9 +218,8 @@ SocialLinks.propTypes = {
|
||||
SocialLinks.defaultProps = {
|
||||
editMode: 'static',
|
||||
saveState: null,
|
||||
value: [],
|
||||
committedValue: [],
|
||||
visibility: 'private',
|
||||
draftSocialLinksByPlatform: {},
|
||||
visibilitySocialLinks: 'private',
|
||||
error: null,
|
||||
};
|
||||
|
||||
@@ -222,16 +243,14 @@ SocialLink.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
|
||||
function EditableListItem({
|
||||
url,
|
||||
platform,
|
||||
onClickEmptyContent,
|
||||
name,
|
||||
url, platform, onClickEmptyContent, name,
|
||||
}) {
|
||||
const linkDisplay = url ?
|
||||
<SocialLink name={name} url={url} platform={platform} /> :
|
||||
<EmptyContent onClick={onClickEmptyContent}>Add {name}</EmptyContent>;
|
||||
const linkDisplay = url ? (
|
||||
<SocialLink name={name} url={url} platform={platform} />
|
||||
) : (
|
||||
<EmptyContent onClick={onClickEmptyContent}>Add {name}</EmptyContent>
|
||||
);
|
||||
|
||||
return <li className="form-group">{linkDisplay}</li>;
|
||||
}
|
||||
@@ -247,13 +266,8 @@ EditableListItem.defaultProps = {
|
||||
onClickEmptyContent: null,
|
||||
};
|
||||
|
||||
|
||||
function EditingListItem({
|
||||
platform,
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
error,
|
||||
platform, name, value, onChange, error,
|
||||
}) {
|
||||
return (
|
||||
<li className="form-group">
|
||||
@@ -261,7 +275,7 @@ function EditingListItem({
|
||||
<Input
|
||||
type="text"
|
||||
name={platform}
|
||||
value={value}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
invalid={error != null}
|
||||
/>
|
||||
@@ -283,7 +297,6 @@ EditingListItem.defaultProps = {
|
||||
error: null,
|
||||
};
|
||||
|
||||
|
||||
function EmptyListItem({ onClick, name }) {
|
||||
return (
|
||||
<li className="mb-4">
|
||||
@@ -292,7 +305,7 @@ function EmptyListItem({ onClick, name }) {
|
||||
id="profile.sociallinks.add"
|
||||
defaultMessage="Add {network}"
|
||||
values={{
|
||||
network: { name },
|
||||
network: name,
|
||||
}}
|
||||
description="{network} is the name of a social network such as Facebook or Twitter"
|
||||
/>
|
||||
@@ -306,7 +319,6 @@ EmptyListItem.propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
function StaticListItem({ name, url, platform }) {
|
||||
return (
|
||||
<li className="mb-2">
|
||||
@@ -317,6 +329,10 @@ function StaticListItem({ name, url, platform }) {
|
||||
|
||||
StaticListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
url: PropTypes.string,
|
||||
platform: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
StaticListItem.defaultProps = {
|
||||
url: null,
|
||||
};
|
||||
|
||||
@@ -9,9 +9,11 @@ import AsyncActionButton from './AsyncActionButton';
|
||||
import { VisibilitySelect } from './Visibility';
|
||||
|
||||
function FormControls({
|
||||
formId, cancelHandler, changeHandler, visibility, saveState, intl,
|
||||
cancelHandler, changeHandler, visibility, visibilityId, saveState, intl,
|
||||
}) {
|
||||
const visibilityId = `${formId}-visibility`;
|
||||
// Eliminate error/failed state for save button
|
||||
const buttonState = saveState === 'error' ? null : saveState;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FormGroup className="mb-4">
|
||||
@@ -22,7 +24,7 @@ function FormControls({
|
||||
id={visibilityId}
|
||||
className="w-auto"
|
||||
type="select"
|
||||
name="visibility"
|
||||
name={visibilityId}
|
||||
value={visibility}
|
||||
onChange={changeHandler}
|
||||
/>
|
||||
@@ -30,12 +32,11 @@ function FormControls({
|
||||
<FormGroup>
|
||||
<AsyncActionButton
|
||||
type="submit"
|
||||
variant={saveState}
|
||||
variant={buttonState}
|
||||
labels={{
|
||||
default: intl.formatMessage(messages['profile.formcontrols.button.save']),
|
||||
pending: intl.formatMessage(messages['profile.formcontrols.button.saving']),
|
||||
complete: intl.formatMessage(messages['profile.formcontrols.button.saved']),
|
||||
error: intl.formatMessage(messages['profile.formcontrols.button.save.failed']),
|
||||
}}
|
||||
/>
|
||||
<Button color="link" onClick={cancelHandler}>
|
||||
@@ -49,9 +50,9 @@ function FormControls({
|
||||
export default injectIntl(FormControls);
|
||||
|
||||
FormControls.propTypes = {
|
||||
formId: PropTypes.string.isRequired,
|
||||
saveState: PropTypes.oneOf([null, 'pending', 'complete', 'error']),
|
||||
visibility: PropTypes.oneOf(['private', 'all_users']),
|
||||
visibilityId: PropTypes.string.isRequired,
|
||||
cancelHandler: PropTypes.func.isRequired,
|
||||
changeHandler: PropTypes.func.isRequired,
|
||||
|
||||
|
||||
@@ -26,11 +26,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Saved',
|
||||
description: 'A button label',
|
||||
},
|
||||
'profile.formcontrols.button.save.failed': {
|
||||
id: 'profile.formcontrols.button.save.failed',
|
||||
defaultMessage: 'Save Failed',
|
||||
description: 'A button label',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
Reference in New Issue
Block a user