* fix: fixing broken linter script and linting The linter script in package.json didn’t specify which files to lint, so it never linted anything. As soon as I fixed the script line, there was suddenly a ton of stuff that needed linting. This fixes the script and cleans up all the things that needed linting. The vast majority were formatting auto-fixes in VSCode for me. * fix: setting NODE_ENV to production in .env Without this change, the NODE_ENV comes through as “null” (a string!) in the app. This causes a number of third party dependencies like React and Redux to potentially go into development mode, slowing them down, or to not realize they’re in production mode, causing them to throw some warnings. * style: some additional linting * feat: upgrading to modern paragon and a brand package This commit updates the app from Paragon 7 to 12 and fixes the breaking changes in between. Mostly small changes to Button and Dropdown, as well as using “container” instead of “container-fluid” to preserve the page’s width as closely as possible. It also adds the brand package, which is why it’s a feature. Using the brand package allows the MFE to be rebranded by using an npm alias to override the source of the brand. * test: fixing test snapshots that failed when updating paragon The test snapshots got a bit out of date when updating paragon. Also removing an unncessary “type” from Dropdown.Toggle which does nothing. - container has been replaced by container-fluid - The Button component is a different implementation, which adds slightly different properties to the rendered button. i.e., onKeyDown and disabled, but doesn’t add the id or onBlur. - The Dropdown doesn’t render its contents until it’s opened, which is why “Upload Photo” and “Remove” are no longer in the snapshot. - * build: bumping paragon to 13 * fix: fixing broken test snapshot btn-outline is definitely not a correct button type. * fix: using the ‘size’ property on Button * fix: updating dependencies We needed to upgrade paragon to 13.1.2 to fix a transpilation issue that was causing ES6 code to be included in the build artifact. All other upgrades here were attempts at fixing that, but they’re all also perfectly valid and good to update, so I left them. babel-polyfill has been replaced by including core-js and regenerator-runtime. Upgrading frontend-build fixed an issue with eslint configuration that emerged during the other upgrades. * fix: switch back to container-fluid We want to leave it as container-fluid and solve the max width problem through paragon. * style: cleanup and formatting of SCSS Also removing an unnecessary variant of primary on a button. * test: fix broken snapshot test
194 lines
6.0 KiB
JavaScript
194 lines
6.0 KiB
JavaScript
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { connect } from 'react-redux';
|
|
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
|
import { ValidationFormGroup } from '@edx/paragon';
|
|
|
|
import messages from './PreferredLanguage.messages';
|
|
|
|
// Components
|
|
import FormControls from './elements/FormControls';
|
|
import EditableItemHeader from './elements/EditableItemHeader';
|
|
import EmptyContent from './elements/EmptyContent';
|
|
import SwitchContent from './elements/SwitchContent';
|
|
|
|
// Selectors
|
|
import { preferredLanguageSelector } from '../data/selectors';
|
|
|
|
class PreferredLanguage extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.handleChange = this.handleChange.bind(this);
|
|
this.handleSubmit = this.handleSubmit.bind(this);
|
|
this.handleClose = this.handleClose.bind(this);
|
|
this.handleOpen = this.handleOpen.bind(this);
|
|
}
|
|
|
|
handleChange(e) {
|
|
const { name, value } = e.target;
|
|
// Restructure the data.
|
|
// We deconstruct our value prop in render() so this
|
|
// changes our data's shape back to match what came in
|
|
if (name === this.props.formId) {
|
|
if (value !== '') {
|
|
this.props.changeHandler(name, [{ code: value }]);
|
|
} else {
|
|
this.props.changeHandler(name, []);
|
|
}
|
|
} else {
|
|
this.props.changeHandler(name, value);
|
|
}
|
|
}
|
|
|
|
handleSubmit(e) {
|
|
e.preventDefault();
|
|
this.props.submitHandler(this.props.formId);
|
|
}
|
|
|
|
handleClose() {
|
|
this.props.closeHandler(this.props.formId);
|
|
}
|
|
|
|
handleOpen() {
|
|
this.props.openHandler(this.props.formId);
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
formId,
|
|
languageProficiencies,
|
|
visibilityLanguageProficiencies,
|
|
editMode,
|
|
saveState,
|
|
error,
|
|
intl,
|
|
sortedLanguages,
|
|
languageMessages,
|
|
} = this.props;
|
|
|
|
const value = languageProficiencies.length ? languageProficiencies[0].code : '';
|
|
|
|
return (
|
|
<SwitchContent
|
|
className="mb-5"
|
|
expression={editMode}
|
|
cases={{
|
|
editing: (
|
|
<div role="dialog" aria-labelledby={`${formId}-label`}>
|
|
<form onSubmit={this.handleSubmit}>
|
|
<ValidationFormGroup
|
|
for={formId}
|
|
invalid={error !== null}
|
|
invalidMessage={error}
|
|
>
|
|
<label className="edit-section-header" htmlFor={formId}>
|
|
{intl.formatMessage(messages['profile.preferredlanguage.label'])}
|
|
</label>
|
|
<select
|
|
data-hj-suppress
|
|
id={formId}
|
|
name={formId}
|
|
className="form-control"
|
|
value={value}
|
|
onChange={this.handleChange}
|
|
>
|
|
<option value=""> </option>
|
|
{sortedLanguages.map(({ code, name }) => (
|
|
<option key={code} value={code}>{name}</option>
|
|
))}
|
|
</select>
|
|
</ValidationFormGroup>
|
|
<FormControls
|
|
visibilityId="visibilityLanguageProficiencies"
|
|
saveState={saveState}
|
|
visibility={visibilityLanguageProficiencies}
|
|
cancelHandler={this.handleClose}
|
|
changeHandler={this.handleChange}
|
|
/>
|
|
</form>
|
|
</div>
|
|
),
|
|
editable: (
|
|
<>
|
|
<EditableItemHeader
|
|
content={intl.formatMessage(messages['profile.preferredlanguage.label'])}
|
|
showEditButton
|
|
onClickEdit={this.handleOpen}
|
|
showVisibility={visibilityLanguageProficiencies !== null}
|
|
visibility={visibilityLanguageProficiencies}
|
|
/>
|
|
<p data-hj-suppress className="h5">{languageMessages[value]}</p>
|
|
</>
|
|
),
|
|
empty: (
|
|
<>
|
|
<EditableItemHeader
|
|
content={intl.formatMessage(messages['profile.preferredlanguage.label'])}
|
|
/>
|
|
<EmptyContent onClick={this.handleOpen}>
|
|
{intl.formatMessage(messages['profile.preferredlanguage.empty'])}
|
|
</EmptyContent>
|
|
</>
|
|
),
|
|
static: (
|
|
<>
|
|
<EditableItemHeader
|
|
content={intl.formatMessage(messages['profile.preferredlanguage.label'])}
|
|
/>
|
|
<p data-hj-suppress className="h5">{languageMessages[value]}</p>
|
|
</>
|
|
),
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
PreferredLanguage.propTypes = {
|
|
// It'd be nice to just set this as a defaultProps...
|
|
// except the class that comes out on the other side of react-redux's
|
|
// connect() method won't have it anymore. Static properties won't survive
|
|
// through the higher order function.
|
|
formId: PropTypes.string.isRequired,
|
|
|
|
// From Selector
|
|
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(['']),
|
|
]),
|
|
visibilityLanguageProficiencies: PropTypes.oneOf(['private', 'all_users']),
|
|
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
|
|
saveState: PropTypes.string,
|
|
error: PropTypes.string,
|
|
sortedLanguages: PropTypes.arrayOf(PropTypes.shape({
|
|
code: PropTypes.string.isRequired,
|
|
name: PropTypes.string.isRequired,
|
|
})).isRequired,
|
|
languageMessages: PropTypes.objectOf(PropTypes.string).isRequired,
|
|
|
|
// Actions
|
|
changeHandler: PropTypes.func.isRequired,
|
|
submitHandler: PropTypes.func.isRequired,
|
|
closeHandler: PropTypes.func.isRequired,
|
|
openHandler: PropTypes.func.isRequired,
|
|
|
|
// i18n
|
|
intl: intlShape.isRequired,
|
|
};
|
|
|
|
PreferredLanguage.defaultProps = {
|
|
editMode: 'static',
|
|
saveState: null,
|
|
languageProficiencies: [],
|
|
visibilityLanguageProficiencies: 'private',
|
|
error: null,
|
|
};
|
|
|
|
export default connect(
|
|
preferredLanguageSelector,
|
|
{},
|
|
)(injectIntl(PreferredLanguage));
|