Split up content of EmailListTermsConditionsForm within onboarding (#7294) [deploy]

* Move Terms & Conditions, CoC into Intro slide of onboarding

Closes https://github.com/thepracticaldev/dev.to/issues/6545.

* Rename ClosingSlide in Onboarding tests

* Rename EmailListTermsConditionsForm to EmailPreferencesForm

* Proper lint and prepend `v2:` to some onboarding components

* Move email preferences to last slide

Closes https://github.com/thepracticaldev/dev.to/issues/6550.

* Use crayons variables for padding

* Remove backToSlide function, rename buttonIsDisabled function
This commit is contained in:
Vaidehi Joshi 2020-04-15 15:16:53 -07:00 committed by GitHub
parent d27471aac5
commit bbe0bf970f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 414 additions and 368 deletions

View file

@ -109,11 +109,19 @@
width: 100%;
border-bottom: 2px solid var(--box-darker);
&.intro-slide {
border-bottom: none;
}
.navigation-content {
display: flex;
justify-content: space-between;
padding: $su-4;
&.intro-slide {
padding: 0;
}
button {
@extend .crayons-btn;
}
@ -337,6 +345,10 @@
padding: $su-7;
justify-content: center;
&.intro-slide {
padding: 0;
}
button {
@extend .crayons-btn;
}
@ -586,6 +598,14 @@ $onboarding-user-selected-hover: rgba(71, 85, 235, 0.2);
}
}
.checkbox-form-wrapper {
padding: $su-7;
.checkbox-form {
padding-bottom: $su-6;
}
}
// Checkboxes
.checkbox-item {
label {
@ -625,5 +645,5 @@ $onboarding-user-selected-hover: rgba(71, 85, 235, 0.2);
.onboarding-form-input--last {
margin-bottom: 25px;
}
}
}
}

View file

@ -2,7 +2,7 @@ import 'preact/devtools';
import { h, Component } from 'preact';
import IntroSlide from './components/IntroSlide';
import EmailListTermsConditionsForm from './components/EmailListTermsConditionsForm';
import EmailPreferencesForm from './components/EmailPreferencesForm';
import ClosingSlide from './components/ClosingSlide';
import FollowTags from './components/FollowTags';
import FollowUsers from './components/FollowUsers';
@ -20,10 +20,10 @@ export default class Onboarding extends Component {
const slides = [
IntroSlide,
EmailListTermsConditionsForm,
ProfileForm,
FollowTags,
FollowUsers,
EmailPreferencesForm,
ClosingSlide,
];

View file

@ -5,7 +5,6 @@ import { axe, toHaveNoViolations } from 'jest-axe';
import Onboarding from '../Onboarding';
import ProfileForm from '../components/ProfileForm';
import EmailTermsConditionsForm from '../components/EmailListTermsConditionsForm';
import FollowTags from '../components/FollowTags';
import FollowUsers from '../components/FollowUsers';
@ -82,29 +81,6 @@ describe('<Onboarding />', () => {
describe('IntroSlide', () => {
let onboardingSlides;
beforeEach(() => {
onboardingSlides = initializeSlides(0, getUserData());
});
test('renders properly', () => {
expect(onboardingSlides).toMatchSnapshot();
});
test('should not have basic a11y violations', async () => {
const results = await axe(onboardingSlides.toString());
expect(results).toHaveNoViolations();
});
test('should advance', () => {
onboardingSlides.find('.next-button').simulate('click');
expect(onboardingSlides.state().currentSlide).toBe(1);
});
});
describe('EmailTermsConditionsForm', () => {
let onboardingSlides;
const codeOfConductCheckEvent = {
target: {
value: 'checked_code_of_conduct',
@ -117,6 +93,7 @@ describe('<Onboarding />', () => {
name: 'checked_terms_and_conditions',
},
};
const updateCodeOfConduct = () => {
onboardingSlides
.find('#checked_code_of_conduct')
@ -129,59 +106,29 @@ describe('<Onboarding />', () => {
};
beforeEach(() => {
onboardingSlides = initializeSlides(1, getUserData());
onboardingSlides = initializeSlides(0, getUserData());
});
test('renders properly', () => {
expect(onboardingSlides).toMatchSnapshot();
});
// Arguably this test is actually just testing the Preact framework
// but for the sake of detecting a regression I am refactoring it instead
// of removing it (@jacobherrington)
test('should track state changes', () => {
const emailTerms = onboardingSlides.find(<EmailTermsConditionsForm />);
expect(emailTerms.state('checked_code_of_conduct')).toBe(false);
expect(emailTerms.state('checked_terms_and_conditions')).toBe(false);
updateCodeOfConduct();
updateTermsAndConditions();
expect(emailTerms.state('checked_code_of_conduct')).toBe(true);
expect(emailTerms.state('checked_terms_and_conditions')).toBe(true);
});
test('should not advance if required boxes are not checked', () => {
// When none of the boxes are checked
onboardingSlides.find('.next-button').simulate('click');
expect(onboardingSlides.state().currentSlide).toBe(1);
// When only the code of conduct is checked
updateCodeOfConduct();
expect(onboardingSlides.state().currentSlide).toBe(1);
// When only the terms and conditions are checked
updateCodeOfConduct();
updateTermsAndConditions();
onboardingSlides.find('.next-button').simulate('click');
expect(onboardingSlides.state().currentSlide).toBe(1);
});
test('should advance if required boxes are checked', async () => {
fetch.once({});
expect(onboardingSlides.state().currentSlide).toBe(0);
updateCodeOfConduct();
updateTermsAndConditions();
onboardingSlides.find('.next-button').simulate('click');
await flushPromises();
expect(onboardingSlides.state().currentSlide).toBe(2);
expect(onboardingSlides.state().currentSlide).toBe(1);
});
it('should step backward', () => {
onboardingSlides.find('.back-button').simulate('click');
expect(onboardingSlides.state().currentSlide).toBe(0);
test('should not have basic a11y violations', async () => {
const results = await axe(onboardingSlides.toString());
expect(results).toHaveNoViolations();
});
});
@ -193,7 +140,7 @@ describe('<Onboarding />', () => {
document.body.appendChild(meta);
beforeEach(() => {
onboardingSlides = initializeSlides(2, getUserData());
onboardingSlides = initializeSlides(1, getUserData());
});
test('renders properly', () => {
@ -231,12 +178,12 @@ describe('<Onboarding />', () => {
profileForm.find('.next-button').simulate('click');
fetch.once(fakeTagsResponse);
await flushPromises();
expect(onboardingSlides.state().currentSlide).toBe(3);
expect(onboardingSlides.state().currentSlide).toBe(2);
});
it('should step backward', () => {
onboardingSlides.find('.back-button').simulate('click');
expect(onboardingSlides.state().currentSlide).toBe(1);
expect(onboardingSlides.state().currentSlide).toBe(0);
});
});
@ -244,7 +191,7 @@ describe('<Onboarding />', () => {
let onboardingSlides;
beforeEach(async () => {
onboardingSlides = initializeSlides(3, getUserData(), fakeTagsResponse);
onboardingSlides = initializeSlides(2, getUserData(), fakeTagsResponse);
await flushPromises();
});
@ -256,7 +203,7 @@ describe('<Onboarding />', () => {
expect(onboardingSlides.find('.onboarding-tags__item').length).toBe(3);
});
test('should allow a user to add a tag', async () => {
test('should allow a user to add a tag and advance', async () => {
fetch.once({});
const followTags = onboardingSlides.find(<FollowTags />);
const firstButton = onboardingSlides
@ -269,12 +216,12 @@ describe('<Onboarding />', () => {
onboardingSlides.find('.next-button').simulate('click');
fetch.once(fakeUsersResponse);
await flushPromises();
expect(onboardingSlides.state().currentSlide).toBe(4);
expect(onboardingSlides.state().currentSlide).toBe(3);
});
it('should step backward', () => {
onboardingSlides.find('.back-button').simulate('click');
expect(onboardingSlides.state().currentSlide).toBe(2);
expect(onboardingSlides.state().currentSlide).toBe(1);
});
});
@ -282,7 +229,7 @@ describe('<Onboarding />', () => {
let onboardingSlides;
beforeEach(async () => {
onboardingSlides = initializeSlides(4, getUserData(), fakeUsersResponse);
onboardingSlides = initializeSlides(3, getUserData(), fakeUsersResponse);
await flushPromises();
});
@ -309,7 +256,7 @@ describe('<Onboarding />', () => {
expect(followUsers.state('selectedUsers').length).toBe(2);
onboardingSlides.find('.next-button').simulate('click');
await flushPromises();
expect(onboardingSlides.state().currentSlide).toBe(5);
expect(onboardingSlides.state().currentSlide).toBe(4);
});
test('should have a functioning select-all toggle', async () => {
@ -330,11 +277,36 @@ describe('<Onboarding />', () => {
fetch.once(fakeTagsResponse);
onboardingSlides.find('.back-button').simulate('click');
await flushPromises();
expect(onboardingSlides.state().currentSlide).toBe(2);
});
});
describe('EmailPreferencesForm', () => {
let onboardingSlides;
beforeEach(() => {
onboardingSlides = initializeSlides(4, getUserData());
});
test('renders properly', () => {
expect(onboardingSlides).toMatchSnapshot();
});
test('should allow user to advance', async () => {
fetch.once({});
onboardingSlides.find('.next-button').simulate('click');
await flushPromises();
expect(onboardingSlides.state().currentSlide).toBe(5);
});
it('should step backward', () => {
onboardingSlides.find('.back-button').simulate('click');
expect(onboardingSlides.state().currentSlide).toBe(3);
});
});
describe('CloseSlide', () => {
describe('ClosingSlide', () => {
let onboardingSlides;
beforeEach(() => {

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Onboarding /> CloseSlide renders properly 1`] = `
exports[`<Onboarding /> ClosingSlide renders properly 1`] = `
preact-render-spy (1 nodes)
-------
<main class="onboarding-body">
@ -77,7 +77,7 @@ preact-render-spy (1 nodes)
`;
exports[`<Onboarding /> EmailTermsConditionsForm renders properly 1`] = `
exports[`<Onboarding /> EmailPreferencesForm renders properly 1`] = `
preact-render-spy (1 nodes)
-------
<main class="onboarding-body">
@ -102,53 +102,12 @@ preact-render-spy (1 nodes)
</nav>
<div class="onboarding-content terms-and-conditions-wrapper">
<header class="onboarding-content-header">
<h1 class="title">Getting started</h1>
<h2 class="subtitle">Let's review a few things first</h2>
<h1 class="title">Almost there!</h1>
<h2 class="subtitle">
Review your email preferences before we continue.
</h2>
</header>
<form>
<fieldset>
<legend>Some things to check off</legend>
<ul>
<li class="checkbox-item">
<label htmlFor="checked_code_of_conduct">
<input
type="checkbox"
id="checked_code_of_conduct"
name="checked_code_of_conduct"
checked={false}
onChange={[Function bound handleChange]}
/>
I agree to uphold the
<a
href="/code-of-conduct"
data-no-instant={true}
onClick={[Function onClick]}
>
Code of Conduct
</a>
</label>
</li>
<li class="checkbox-item">
<label htmlFor="checked_terms_and_conditions">
<input
type="checkbox"
id="checked_terms_and_conditions"
name="checked_terms_and_conditions"
checked={false}
onChange={[Function bound handleChange]}
/>
I agree to our
<a
href="/terms"
data-no-instant={true}
onClick={[Function onClick]}
>
Terms and Conditions
</a>
</label>
</li>
</ul>
</fieldset>
<fieldset>
<legend>Email preferences</legend>
<ul>
@ -173,7 +132,7 @@ preact-render-spy (1 nodes)
checked={true}
onChange={[Function bound handleChange]}
/>
I want to receive a periodic digest with some of the top posts from your tags.
I want to receive a periodic digest of top posts from my tags.
</label>
</li>
</ul>
@ -413,17 +372,66 @@ preact-render-spy (1 nodes)
DEV is where programmers share ideas and help each other grow.
</h2>
</div>
<nav class="onboarding-navigation">
<div class="navigation-content">
<button
onClick={[Function bound onSubmit]}
class="next-button"
type="button"
>
Continue
</button>
</div>
</nav>
<div class="checkbox-form-wrapper">
<form class="checkbox-form">
<fieldset>
<ul>
<li class="checkbox-item">
<label htmlFor="checked_code_of_conduct">
<input
type="checkbox"
id="checked_code_of_conduct"
name="checked_code_of_conduct"
checked={false}
onChange={[Function bound handleChange]}
/>
You agree to uphold our
<a
href="/code-of-conduct"
data-no-instant={true}
onClick={[Function onClick]}
>
Code of Conduct
</a>
.
</label>
</li>
<li class="checkbox-item">
<label htmlFor="checked_terms_and_conditions">
<input
type="checkbox"
id="checked_terms_and_conditions"
name="checked_terms_and_conditions"
checked={false}
onChange={[Function bound handleChange]}
/>
You agree to our
<a
href="/terms"
data-no-instant={true}
onClick={[Function onClick]}
>
Terms and Conditions
</a>
.
</label>
</li>
</ul>
</fieldset>
</form>
<nav class="onboarding-navigation intro-slide">
<div class="navigation-content intro-slide">
<button
disabled={true}
onClick={[Function bound onSubmit]}
class="next-button"
type="button"
>
Continue
</button>
</div>
</nav>
</div>
</div>
</main>

View file

@ -1,223 +0,0 @@
/* eslint camelcase: "off" */
import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import Navigation from './Navigation';
import { getContentOfToken, updateOnboarding } from '../utilities';
/* eslint-disable camelcase */
class EmailTermsConditionsForm extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.checkRequirements = this.checkRequirements.bind(this);
this.state = {
checked_code_of_conduct: false,
checked_terms_and_conditions: false,
email_newsletter: true,
email_digest_periodic: true,
message: '',
textShowing: null,
};
}
componentDidMount() {
updateOnboarding('emails, COC and T&C form');
}
onSubmit() {
if (!this.checkRequirements()) return;
const csrfToken = getContentOfToken('csrf-token');
fetch('/onboarding_checkbox_update', {
method: 'PATCH',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({ user: this.state }),
credentials: 'same-origin',
}).then((response) => {
if (response.ok) {
localStorage.setItem('shouldRedirectToOnboarding', false);
const { next } = this.props;
next();
}
});
}
checkRequirements() {
const {
checked_code_of_conduct,
checked_terms_and_conditions,
} = this.state;
if (!checked_code_of_conduct) {
this.setState({
message: 'You must agree to our Code of Conduct before continuing.',
});
return false;
}
if (!checked_terms_and_conditions) {
this.setState({
message:
'You must agree to our Terms and Conditions before continuing.',
});
return false;
}
return true;
}
handleChange(event) {
const { name } = event.target;
this.setState((currentState) => ({
[name]: !currentState[name],
}));
}
handleShowText(event, id) {
event.preventDefault();
this.setState({ textShowing: document.getElementById(id).innerHTML });
}
backToSlide() {
this.setState({ textShowing: null });
}
render() {
const {
message,
checked_code_of_conduct,
checked_terms_and_conditions,
email_newsletter,
email_digest_periodic,
textShowing,
} = this.state;
const { prev } = this.props;
if (textShowing) {
return (
<div className="onboarding-main">
<div className="onboarding-content terms-and-conditions-wrapper">
<button type="button" onClick={() => this.backToSlide()}>
Back
</button>
<div
className="terms-and-conditions-content"
/* eslint-disable react/no-danger */
dangerouslySetInnerHTML={{ __html: textShowing }}
/* eslint-enable react/no-danger */
/>
</div>
</div>
);
}
return (
<div className="onboarding-main">
<Navigation prev={prev} next={this.onSubmit} />
<div className="onboarding-content terms-and-conditions-wrapper">
<header className="onboarding-content-header">
<h1 className="title">Getting started</h1>
<h2 className="subtitle">Let&apos;s review a few things first</h2>
</header>
{message && (
<span className="crayons-notice crayons-notice--danger">
{message}
</span>
)}
<form>
<fieldset>
<legend>Some things to check off</legend>
<ul>
<li className="checkbox-item">
<label htmlFor="checked_code_of_conduct">
<input
type="checkbox"
id="checked_code_of_conduct"
name="checked_code_of_conduct"
checked={checked_code_of_conduct}
onChange={this.handleChange}
/>
I agree to uphold the
{' '}
<a
href="/code-of-conduct"
data-no-instant
onClick={(e) => this.handleShowText(e, 'coc')}
>
Code of Conduct
</a>
</label>
</li>
<li className="checkbox-item">
<label htmlFor="checked_terms_and_conditions">
<input
type="checkbox"
id="checked_terms_and_conditions"
name="checked_terms_and_conditions"
checked={checked_terms_and_conditions}
onChange={this.handleChange}
/>
I agree to our
{' '}
<a
href="/terms"
data-no-instant
onClick={(e) => this.handleShowText(e, 'terms')}
>
Terms and Conditions
</a>
</label>
</li>
</ul>
</fieldset>
<fieldset>
<legend>Email preferences</legend>
<ul>
<li className="checkbox-item">
<label htmlFor="email_newsletter">
<input
type="checkbox"
id="email_newsletter"
name="email_newsletter"
checked={email_newsletter}
onChange={this.handleChange}
/>
I want to receive weekly newsletter emails.
</label>
</li>
<li className="checkbox-item">
<label htmlFor="email_digest_periodic">
<input
type="checkbox"
id="email_digest_periodic"
name="email_digest_periodic"
checked={email_digest_periodic}
onChange={this.handleChange}
/>
I want to receive a periodic digest with some of the top
posts from your tags.
</label>
</li>
</ul>
</fieldset>
</form>
</div>
</div>
);
}
}
EmailTermsConditionsForm.propTypes = {
prev: PropTypes.func.isRequired,
next: PropTypes.string.isRequired,
};
export default EmailTermsConditionsForm;

View file

@ -0,0 +1,111 @@
import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import Navigation from './Navigation';
import { getContentOfToken, updateOnboarding } from '../utilities';
/* eslint-disable camelcase */
class EmailPreferencesForm extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
email_newsletter: true,
email_digest_periodic: true,
};
}
componentDidMount() {
updateOnboarding('v2: email preferences form');
}
onSubmit() {
const csrfToken = getContentOfToken('csrf-token');
fetch('/onboarding_checkbox_update', {
method: 'PATCH',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({ user: this.state }),
credentials: 'same-origin',
}).then((response) => {
if (response.ok) {
localStorage.setItem('shouldRedirectToOnboarding', false);
const { next } = this.props;
next();
}
});
}
handleChange(event) {
const { name } = event.target;
this.setState((currentState) => ({
[name]: !currentState[name],
}));
}
render() {
const { email_newsletter, email_digest_periodic } = this.state;
const { prev } = this.props;
return (
<div className="onboarding-main">
<Navigation prev={prev} next={this.onSubmit} />
<div className="onboarding-content terms-and-conditions-wrapper">
<header className="onboarding-content-header">
<h1 className="title">Almost there!</h1>
<h2 className="subtitle">
Review your email preferences before we continue.
</h2>
</header>
<form>
<fieldset>
<legend>Email preferences</legend>
<ul>
<li className="checkbox-item">
<label htmlFor="email_newsletter">
<input
type="checkbox"
id="email_newsletter"
name="email_newsletter"
checked={email_newsletter}
onChange={this.handleChange}
/>
I want to receive weekly newsletter emails.
</label>
</li>
<li className="checkbox-item">
<label htmlFor="email_digest_periodic">
<input
type="checkbox"
id="email_digest_periodic"
name="email_digest_periodic"
checked={email_digest_periodic}
onChange={this.handleChange}
/>
I want to receive a periodic digest of top posts from my
tags.
</label>
</li>
</ul>
</fieldset>
</form>
</div>
</div>
);
}
}
EmailPreferencesForm.propTypes = {
prev: PropTypes.func.isRequired,
next: PropTypes.string.isRequired,
};
export default EmailPreferencesForm;
/* eslint-enable camelcase */

View file

@ -32,7 +32,7 @@ class FollowTags extends Component {
'Content-Type': 'application/json',
},
body: JSON.stringify({
user: { last_onboarding_page: 'follow tags page' },
user: { last_onboarding_page: 'v2: follow tags page' },
}),
credentials: 'same-origin',
});

View file

@ -38,7 +38,7 @@ class FollowUsers extends Component {
'Content-Type': 'application/json',
},
body: JSON.stringify({
user: { last_onboarding_page: 'follow users page' },
user: { last_onboarding_page: 'v2: follow users page' },
}),
credentials: 'same-origin',
});

View file

@ -2,27 +2,94 @@ import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import Navigation from './Navigation';
import { userData, updateOnboarding } from '../utilities';
import { getContentOfToken, userData, updateOnboarding } from '../utilities';
/* eslint-disable camelcase */
class IntroSlide extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.user = userData();
this.state = {
checked_code_of_conduct: false,
checked_terms_and_conditions: false,
text: null,
};
}
componentDidMount() {
updateOnboarding('intro slide');
updateOnboarding('v2: intro, code of conduct, terms & conditions');
}
onSubmit() {
const { next } = this.props;
next();
const csrfToken = getContentOfToken('csrf-token');
fetch('/onboarding_checkbox_update', {
method: 'PATCH',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({ user: this.state }),
credentials: 'same-origin',
}).then((response) => {
if (response.ok) {
localStorage.setItem('shouldRedirectToOnboarding', false);
next();
}
});
}
handleChange(event) {
const { name } = event.target;
this.setState((currentState) => ({
[name]: !currentState[name],
}));
}
handleShowText(event, id) {
event.preventDefault();
this.setState({ text: document.getElementById(id).innerHTML });
}
isButtonDisabled() {
const {
checked_code_of_conduct,
checked_terms_and_conditions,
} = this.state;
return !checked_code_of_conduct || !checked_terms_and_conditions;
}
render() {
const { prev } = this.props;
const {
checked_code_of_conduct,
checked_terms_and_conditions,
text,
} = this.state;
if (text) {
return (
<div className="onboarding-main">
<div className="onboarding-content terms-and-conditions-wrapper">
<button type="button" onClick={() => this.setState({ text: null })}>
Back
</button>
<div
className="terms-and-conditions-content"
/* eslint-disable react/no-danger */
dangerouslySetInnerHTML={{ __html: text }}
/* eslint-enable react/no-danger */
/>
</div>
</div>
);
}
return (
<div className="onboarding-main introduction">
@ -43,7 +110,65 @@ class IntroSlide extends Component {
DEV is where programmers share ideas and help each other grow.
</h2>
</div>
<Navigation prev={prev} next={this.onSubmit} hidePrev />
<div className="checkbox-form-wrapper">
<form className="checkbox-form">
<fieldset>
<ul>
<li className="checkbox-item">
<label htmlFor="checked_code_of_conduct">
<input
type="checkbox"
id="checked_code_of_conduct"
name="checked_code_of_conduct"
checked={checked_code_of_conduct}
onChange={this.handleChange}
/>
You agree to uphold our
{' '}
<a
href="/code-of-conduct"
data-no-instant
onClick={(e) => this.handleShowText(e, 'coc')}
>
Code of Conduct
</a>
.
</label>
</li>
<li className="checkbox-item">
<label htmlFor="checked_terms_and_conditions">
<input
type="checkbox"
id="checked_terms_and_conditions"
name="checked_terms_and_conditions"
checked={checked_terms_and_conditions}
onChange={this.handleChange}
/>
You agree to our
{' '}
<a
href="/terms"
data-no-instant
onClick={(e) => this.handleShowText(e, 'terms')}
>
Terms and Conditions
</a>
.
</label>
</li>
</ul>
</fieldset>
</form>
<Navigation
disabled={this.isButtonDisabled()}
className="intro-slide"
prev={prev}
next={this.onSubmit}
hidePrev
/>
</div>
</div>
);
}
@ -55,3 +180,5 @@ IntroSlide.propTypes = {
};
export default IntroSlide;
/* eslint-enable camelcase */

View file

@ -1,16 +1,36 @@
import { h } from 'preact';
import PropTypes from 'prop-types';
const Navigation = ({ next, prev, hideNext, hidePrev }) => (
<nav className="onboarding-navigation">
<div className="navigation-content">
const Navigation = ({
next,
prev,
hideNext,
hidePrev,
disabled,
className,
}) => (
<nav
className={`onboarding-navigation${
className && className.length > 0 ? ` ${className}` : ''
}`}
>
<div
className={`navigation-content${
className && className.length > 0 ? ` ${className}` : ''
}`}
>
{!hidePrev && (
<button onClick={prev} className="back-button" type="button">
Back
</button>
)}
{!hideNext && (
<button onClick={next} className="next-button" type="button">
<button
disabled={disabled}
onClick={next}
className="next-button"
type="button"
>
Continue
</button>
)}
@ -19,6 +39,8 @@ const Navigation = ({ next, prev, hideNext, hidePrev }) => (
);
Navigation.propTypes = {
disabled: PropTypes.bool,
className: PropTypes.string,
prev: PropTypes.func.isRequired,
next: PropTypes.string.isRequired,
hideNext: PropTypes.bool,
@ -26,8 +48,10 @@ Navigation.propTypes = {
};
Navigation.defaultProps = {
disabled: false,
hideNext: false,
hidePrev: false,
className: '',
};
export default Navigation;

View file

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import Navigation from './Navigation';
import { userData, getContentOfToken, updateOnboarding } from '../utilities';
/* eslint-disable camelcase */
class ProfileForm extends Component {
constructor(props) {
super(props);
@ -40,7 +41,7 @@ class ProfileForm extends Component {
const { next } = this.props;
next();
}
})
});
}
handleChange(e) {
@ -68,7 +69,11 @@ class ProfileForm extends Component {
</header>
<div className="current-user-info">
<figure className="current-user-avatar-container">
<img className="current-user-avatar" alt="profile" src={profile_image_90} />
<img
className="current-user-avatar"
alt="profile"
src={profile_image_90}
/>
</figure>
<h3>{name}</h3>
<p>{username}</p>
@ -131,3 +136,5 @@ ProfileForm.propTypes = {
};
export default ProfileForm;
/* eslint-enable camelcase */