docbrown/app/javascript/onboarding/components/ProfileForm.jsx
Rafi 940c7ee37d
Adding aria labels and role to onboarding modal (#12363)
1. Adding role dialog
2. Adding aria-labelledby and aria-describedby
2021-01-21 13:58:55 -05:00

244 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import { userData, getContentOfToken, updateOnboarding } from '../utilities';
import Navigation from './Navigation';
import ColorPicker from './ProfileForm/ColorPicker';
import TextArea from './ProfileForm/TextArea';
import TextInput from './ProfileForm/TextInput';
import CheckBox from './ProfileForm/CheckBox';
import { request } from '@utilities/http';
/* eslint-disable camelcase */
class ProfileForm extends Component {
constructor(props) {
super(props);
this.handleFieldChange = this.handleFieldChange.bind(this);
this.handleColorPickerChange = this.handleColorPickerChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.user = userData();
this.state = {
groups: [],
formValues: {},
canSkip: true,
last_onboarding_page: 'v2: personal info form',
};
}
componentDidMount() {
this.getProfielFieldGroups();
updateOnboarding('v2: personal info form');
}
async getProfielFieldGroups() {
try {
const response = await request(`/profile_field_groups?onboarding=true`);
if (response.ok) {
const data = await response.json();
this.setState({ groups: data.profile_field_groups });
} else {
throw new Error(response.statusText);
}
} catch (error) {
this.setState({ error: true, errorMessage: error.toString() });
}
}
onSubmit() {
const csrfToken = getContentOfToken('csrf-token');
const { formValues, last_onboarding_page } = this.state;
fetch('/onboarding_update', {
method: 'PATCH',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({
user: { last_onboarding_page },
profile: { ...formValues },
}),
credentials: 'same-origin',
}).then((response) => {
if (response.ok) {
const { next } = this.props;
next();
}
});
}
handleFieldChange(e) {
const { formValues } = { ...this.state };
const currentFormState = formValues;
const { name, value } = e.target;
currentFormState[name] = value;
this.setState({
formValues: currentFormState,
canSkip: this.formIsEmpty(currentFormState),
});
}
handleColorPickerChange(e) {
const { formValues } = { ...this.state };
const currentFormState = formValues;
const field = e.target;
const { name, value } = field;
let sibling = field.nextElementSibling
? field.nextElementSibling
: field.previousElementSibling;
sibling.value = value;
currentFormState[name] = value;
this.setState({
formValues: currentFormState,
canSkip: this.formIsEmpty(currentFormState),
});
}
formIsEmpty(currentFormState) {
// Once we've derived the new form values, check if the form is empty
// and use that value to set the `canSkip` property on the state.
Object.values(currentFormState).filter((v) => v.length > 0).length === 0;
}
renderAppropriateFieldType(field) {
switch (field.input_type) {
case 'check_box':
return (
<CheckBox
key={field.id}
field={field}
onFieldChange={this.handleFieldChange}
/>
);
case 'color_field':
return (
<ColorPicker
key={field.id}
field={field}
onColorChange={this.handleColorPickerChange}
/>
);
case 'text_area':
return (
<TextArea
key={field.id}
field={field}
onFieldChange={this.handleFieldChange}
/>
);
default:
return (
<TextInput
key={field.id}
field={field}
onFieldChange={this.handleFieldChange}
/>
);
}
}
render() {
const {
prev,
slidesCount,
currentSlideIndex,
communityConfig,
} = this.props;
const { profile_image_90, username, name } = this.user;
const { canSkip, groups = [], error, errorMessage } = this.state;
if (error) {
return (
<div role="alert" class="crayons-notice crayons-notice--danger">
An error occurred: {errorMessage}
</div>
);
}
const sections = groups.map((group) => {
return (
<div key={group.id} class="onboarding-profile-sub-section">
<h2>{group.name}</h2>
{group.description && (
<div class="color-base-60">{group.description})</div>
)}
<div>
{group.profile_fields.map((field) => {
return this.renderAppropriateFieldType(field);
})}
</div>
</div>
);
});
return (
<div
data-testid="onboarding-profile-form"
className="onboarding-main crayons-modal"
>
<div
className="crayons-modal__box"
role="dialog"
aria-labelledby="title"
aria-describedby="subtitle"
>
<Navigation
prev={prev}
next={this.onSubmit}
canSkip={canSkip}
slidesCount={slidesCount}
currentSlideIndex={currentSlideIndex}
/>
<div className="onboarding-content about">
<header className="onboarding-content-header">
<h1 id="title" className="title">
Build your profile
</h1>
<h2
id="subtitle"
data-testid="onboarding-profile-subtitle"
className="subtitle"
>
Tell us a little bit about yourself this is how others will
see you on {communityConfig.communityName}. Youll always be
able to edit this later in your Settings.
</h2>
</header>
<div className="current-user-info">
<figure className="current-user-avatar-container">
<img
className="current-user-avatar"
alt="profile"
src={profile_image_90}
/>
</figure>
<h3>{name}</h3>
<p>{username}</p>
</div>
<div>{sections}</div>
</div>
</div>
</div>
);
}
}
ProfileForm.propTypes = {
prev: PropTypes.func.isRequired,
next: PropTypes.func.isRequired,
slidesCount: PropTypes.number.isRequired,
currentSlideIndex: PropTypes.func.isRequired,
communityConfig: PropTypes.shape({
communityName: PropTypes.string.isRequired,
}),
};
export default ProfileForm;
/* eslint-enable camelcase */