Merge branch 'develop' of https://github.com/LLK/scratch-www into issue/gh-715

* 'develop' of https://github.com/LLK/scratch-www:
  Pluralize example headlines on educators page
  Educator page language adjustments as per feedback. Resolves GH-734
  fix typo
  Update image format for API change
  Fix lint errors, l10n issues
  revert irrelevant change
  Remove IE weird input additions, fallback to mobile error style on IE, fix some mobile error issues
  Update description of demographics step
  Add student registration flow
  Scope message strings more reusably
  Make registration styles reusable
  Parse with babel-eslint
  Fix whitespace

# Conflicts:
#	src/l10n.json
This commit is contained in:
Matthew Taylor 2016-07-21 10:27:10 -04:00
commit 102de9a087
19 changed files with 851 additions and 547 deletions

View file

@ -1,4 +1,5 @@
{ {
"parser": "babel-eslint",
"rules": { "rules": {
"curly": [2, "multi-line"], "curly": [2, "multi-line"],
"eol-last": [2], "eol-last": [2],
@ -14,16 +15,12 @@
}, },
"env": { "env": {
"browser": true, "browser": true,
"es6": true,
"node": true "node": true
}, },
"globals": { "globals": {
"formatMessage": true "formatMessage": true
}, },
"ecmaFeatures": {
"arrowFunctions": true,
"blockBindings": true,
"jsx": true
},
"plugins": [ "plugins": [
"react", "react",
"json" "json"

View file

@ -34,6 +34,7 @@
"async": "1.5.2", "async": "1.5.2",
"autoprefixer": "6.3.6", "autoprefixer": "6.3.6",
"babel-core": "6.10.4", "babel-core": "6.10.4",
"babel-eslint": "5.0.4",
"babel-loader": "6.2.4", "babel-loader": "6.2.4",
"babel-preset-es2015": "6.9.0", "babel-preset-es2015": "6.9.0",
"babel-preset-react": "6.11.1", "babel-preset-react": "6.11.1",

View file

@ -5,4 +5,111 @@
border-radius: 8px / $em; border-radius: 8px / $em;
box-shadow: 0 0 0 .125rem $active-gray; box-shadow: 0 0 0 .125rem $active-gray;
background-color: $ui-white; background-color: $ui-white;
.card-button {
display: block;
border-radius: .5rem;
border-top-left-radius: 0;
border-top-right-radius: 0;
box-shadow: none;
background-color: $ui-aqua;
width: 23.75rem;
height: 4rem;
&:hover {
box-shadow: none;
}
}
.form {
padding: 3rem 4rem;
.card-button {
margin: 0 0 -3rem -4rem;
}
.form-group {
margin-bottom: 1.2rem;
&.has-error {
.input {
border: 1px solid $ui-orange;
}
}
}
}
.help-block {
$arrow-border-width: 1rem;
display: block;
position: absolute;
margin-left: $arrow-border-width;
border: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
padding: 1rem;
max-width: 18.75rem;
min-height: 1rem;
max-height: 3rem;
overflow: visible;
color: $type-white;
&:before {
display: block;
position: absolute;
top: 1rem;
left: -$arrow-border-width / 2;
transform: rotate(45deg);
border-bottom: 1px solid $active-gray;
border-left: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
width: $arrow-border-width;
height: $arrow-border-width;
content: "";
}
}
}
@media only screen and (max-width: $mobile - 1) {
.card {
width: 22.5rem;
.form {
text-align: left;
.button {
width: 22.5rem;
}
}
}
}
@media only screen and (max-width: $tablet - 1) {
.card {
.input {
width: 90%;
}
}
}
@media only screen and (max-width: $desktop - 1) {
.card {
.help-block {
position: relative;
transform: none;
margin: inherit;
width: 100%;
height: inherit;
&:before {
display: none;
}
}
}
} }

View file

@ -10,7 +10,7 @@ var Deck = React.createClass({
<div className={classNames(['deck', this.props.className])}> <div className={classNames(['deck', this.props.className])}>
<div className="inner"> <div className="inner">
<a href="/" aria-label="Scratch"> <a href="/" aria-label="Scratch">
<img src="/images/logo_sm.png" /> <img className="logo" src="/images/logo_sm.png" />
</a> </a>
{this.props.children} {this.props.children}
</div> </div>

View file

@ -6,146 +6,22 @@
.deck { .deck {
min-height: 100vh; min-height: 100vh;
img { .logo {
margin-left: 2px; margin-left: 2px;
padding: 12px 0; padding: 12px 0;
width: 76px; width: 76px;
} }
.step-navigation {
margin-top: 2rem;
text-align: center;
}
.slide { .slide {
max-width: 28.75rem; max-width: 28.75rem;
h2,
.description {
text-align: center;
color: $type-white;
}
.description {
margin-top: 0;
margin-bottom: 2rem;
}
} }
.card { .card {
margin: 0 auto; width: 23.75rem;
width: 23.75rem;
}
.form {
padding: 3rem 4rem;
.form-group {
margin-bottom: 1.2rem;
&.has-error {
.input {
border: 1px solid $ui-orange;
}
}
}
.button {
margin: 0 0 -3rem -4rem;
border-radius: .5rem;
box-shadow: none;
width: 23.75rem;
height: 4rem;
&.card-button {
display: block;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-color: $ui-aqua;
}
&:hover {
box-shadow: none;
}
}
} }
.input { .input {
width: $cols5; width: $cols5;
} }
.help-block {
$arrow-border-width: 1rem;
display: block;
position: absolute;
margin-left: $arrow-border-width;
border: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
padding: 1rem;
max-width: 18.75rem;
min-height: 1rem;
max-height: 3rem;
overflow: visible;
color: $type-white;
&:before {
display: block;
position: absolute;
top: 1rem;
left: -$arrow-border-width / 2;
transform: rotate(45deg);
border-bottom: 1px solid $active-gray;
border-left: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
width: $arrow-border-width;
height: $arrow-border-width;
content: "";
}
}
}
@media only screen and (max-width: $mobile - 1) {
.deck {
.card {
width: 22.5rem;
}
.form {
text-align: left;
.button {
width: 22.5rem;
}
}
}
}
@media only screen and (max-width: $tablet - 1) {
.deck {
.input {
width: 90%;
}
}
}
@media only screen and (max-width: $desktop - 1) {
.deck {
.help-block {
position: relative;
transform: none;
margin: inherit;
width: 100%;
height: inherit;
&:before {
display: none;
}
}
}
} }

View file

@ -35,4 +35,9 @@ $pass-bg: lighten($ui-aqua, 35%);
border: 1px solid $active-dark-gray; border: 1px solid $active-dark-gray;
background-color: $pass-bg; background-color: $pass-bg;
} }
/* IE10/11-specific style resets */
&::-ms-reveal, &::-ms-clear {
display: none;
}
} }

View file

@ -6,6 +6,7 @@ var intl = require('../../lib/intl.jsx');
var log = require('../../lib/log'); var log = require('../../lib/log');
var smartyStreets = require('../../lib/smarty-streets'); var smartyStreets = require('../../lib/smarty-streets');
var Avatar = require('../../components/avatar/avatar.jsx');
var Button = require('../../components/forms/button.jsx'); var Button = require('../../components/forms/button.jsx');
var Card = require('../../components/card/card.jsx'); var Card = require('../../components/card/card.jsx');
var CharCount = require('../../components/forms/charcount.jsx'); var CharCount = require('../../components/forms/charcount.jsx');
@ -23,6 +24,8 @@ var StepNavigation = require('../../components/stepnavigation/stepnavigation.jsx
var TextArea = require('../../components/forms/textarea.jsx'); var TextArea = require('../../components/forms/textarea.jsx');
var Tooltip = require('../../components/tooltip/tooltip.jsx'); var Tooltip = require('../../components/tooltip/tooltip.jsx');
require('./steps.scss');
var DEFAULT_COUNTRY = 'us'; var DEFAULT_COUNTRY = 'us';
var NextStepButton = React.createClass({ var NextStepButton = React.createClass({
@ -34,7 +37,7 @@ var NextStepButton = React.createClass({
}, },
render: function () { render: function () {
return ( return (
<Button type="submit" disabled={this.props.waiting} className="card-button"> <Button type="submit" disabled={this.props.waiting} className="card-button" {... this.props}>
{this.props.waiting ? {this.props.waiting ?
<Spinner /> : <Spinner /> :
this.props.text this.props.text
@ -79,16 +82,16 @@ module.exports = {
return this.props.onNextStep(formData); return this.props.onNextStep(formData);
case 'username exists': case 'username exists':
return invalidate({ return invalidate({
'user.username': formatMessage({id: 'general.validationUsernameExists'}) 'user.username': formatMessage({id: 'registration.validationUsernameExists'})
}); });
case 'bad username': case 'bad username':
return invalidate({ return invalidate({
'user.username': formatMessage({id: 'general.validationUsernameVulgar'}) 'user.username': formatMessage({id: 'registration.validationUsernameVulgar'})
}); });
case 'invalid username': case 'invalid username':
default: default:
return invalidate({ return invalidate({
'user.username': formatMessage({id: 'general.validationUsernameInvalid'}) 'user.username': formatMessage({id: 'registration.validationUsernameInvalid'})
}); });
} }
}.bind(this)); }.bind(this));
@ -96,14 +99,14 @@ module.exports = {
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
return ( return (
<Slide className="username-step"> <Slide className="registration-step username-step">
<h2><intl.FormattedMessage id="teacherRegistration.usernameStepTitle" /></h2> <h2><intl.FormattedMessage id="registration.usernameStepTitle" /></h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.usernameStepDescription" /> <intl.FormattedMessage id="registration.usernameStepDescription" />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
<Input label={formatMessage({id: 'general.createUsername'})} <Input label={formatMessage({id: 'registration.createUsername'})}
className={this.state.validUsername} className={this.state.validUsername}
type="text" type="text"
name="user.username" name="user.username"
@ -114,13 +117,13 @@ module.exports = {
}} }}
validationErrors={{ validationErrors={{
matchRegexp: formatMessage({ matchRegexp: formatMessage({
id: 'teacherRegistration.validationUsernameRegexp' id: 'registration.validationUsernameRegexp'
}), }),
minLength: formatMessage({ minLength: formatMessage({
id: 'teacherRegistration.validationUsernameMinLength' id: 'registration.validationUsernameMinLength'
}), }),
maxLength: formatMessage({ maxLength: formatMessage({
id: 'teacherRegistration.validationUsernameMaxLength' id: 'registration.validationUsernameMaxLength'
}) })
}} }}
required /> required />
@ -134,24 +137,24 @@ module.exports = {
}} }}
validationErrors={{ validationErrors={{
minLength: formatMessage({ minLength: formatMessage({
id: 'teacherRegistration.validationPasswordLength' id: 'registration.validationPasswordLength'
}), }),
notEquals: formatMessage({ notEquals: formatMessage({
id: 'teacherRegistration.validationPasswordNotEquals' id: 'registration.validationPasswordNotEquals'
}), }),
notEqualsField: formatMessage({ notEqualsField: formatMessage({
id: 'teacherRegistration.validationPasswordNotUsername' id: 'registration.validationPasswordNotUsername'
}) })
}} }}
required /> required />
<Checkbox label={formatMessage({id: 'teacherRegistration.showPassword'})} <Checkbox label={formatMessage({id: 'registration.showPassword'})}
value={this.state.showPassword} value={this.state.showPassword}
onChange={this.onChangeShowPassword} onChange={this.onChangeShowPassword}
help={null} help={null}
name="showPassword" /> name="showPassword" />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton waiting={this.props.waiting || this.state.waiting} <NextStepButton waiting={this.props.waiting || this.state.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -163,7 +166,8 @@ module.exports = {
getDefaultProps: function () { getDefaultProps: function () {
return { return {
defaultCountry: DEFAULT_COUNTRY, defaultCountry: DEFAULT_COUNTRY,
waiting: false waiting: false,
description: null
}; };
}, },
getInitialState: function () { getInitialState: function () {
@ -191,14 +195,18 @@ module.exports = {
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
return ( return (
<Slide className="demographics-step"> <Slide className="registration-step demographics-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.personalStepTitle" /> <intl.FormattedMessage id="registration.personalStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.personalStepDescription" /> {this.props.description ?
this.props.description
:
<intl.FormattedMessage id="registration.personalStepDescription" />
}
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.props.onNextStep}> <Form onValidSubmit={this.props.onNextStep}>
@ -234,7 +242,7 @@ module.exports = {
label="I'm a robot!" label="I'm a robot!"
name="user.isRobot" /> name="user.isRobot" />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -251,14 +259,14 @@ module.exports = {
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
return ( return (
<Slide className="name-step"> <Slide className="registration-step name-step">
<h2> <h2>
<intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" /> <intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.nameStepDescription" /> <intl.FormattedMessage id="teacherRegistration.nameStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.props.onNextStep}> <Form onValidSubmit={this.props.onNextStep}>
@ -271,7 +279,7 @@ module.exports = {
name="user.name.last" name="user.name.last"
required /> required />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -297,14 +305,14 @@ module.exports = {
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
return ( return (
<Slide className="phone-step"> <Slide className="registration-step phone-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.phoneStepTitle" /> <intl.FormattedMessage id="teacherRegistration.phoneStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.phoneStepDescription" /> <intl.FormattedMessage id="teacherRegistration.phoneStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
@ -319,7 +327,7 @@ module.exports = {
isFalse: formatMessage({id: 'teacherRegistration.validationPhoneConsent'}) isFalse: formatMessage({id: 'teacherRegistration.validationPhoneConsent'})
}} /> }} />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -373,14 +381,14 @@ module.exports = {
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
return ( return (
<Slide className="organization-step"> <Slide className="registration-step organization-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.orgStepTitle" /> <intl.FormattedMessage id="teacherRegistration.orgStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.orgStepDescription" /> <intl.FormattedMessage id="teacherRegistration.orgStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
@ -417,7 +425,7 @@ module.exports = {
placeholder={'http://'} /> placeholder={'http://'} />
</div> </div>
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -487,14 +495,14 @@ module.exports = {
return 0; return 0;
}.bind(this)); }.bind(this));
return ( return (
<Slide className="address-step"> <Slide className="registration-step address-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.addressStepTitle" /> <intl.FormattedMessage id="teacherRegistration.addressStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.addressStepDescription" /> <intl.FormattedMessage id="teacherRegistration.addressStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
@ -528,7 +536,7 @@ module.exports = {
required /> required />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton waiting={this.props.waiting || this.state.waiting} <NextStepButton waiting={this.props.waiting || this.state.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -558,14 +566,14 @@ module.exports = {
var textAreaClass = (this.state.characterCount > this.props.maxCharacters) ? 'fail' : ''; var textAreaClass = (this.state.characterCount > this.props.maxCharacters) ? 'fail' : '';
return ( return (
<Slide className="usescratch-step"> <Slide className="registration-step usescratch-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" /> <intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" /> <intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.props.onNextStep}> <Form onValidSubmit={this.props.onNextStep}>
@ -585,7 +593,7 @@ module.exports = {
<CharCount maxCharacters={this.props.maxCharacters} <CharCount maxCharacters={this.props.maxCharacters}
currentCharacters={this.state.characterCount} /> currentCharacters={this.state.characterCount} />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -625,14 +633,14 @@ module.exports = {
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
return ( return (
<Slide className="email-step"> <Slide className="registration-step email-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.emailStepTitle" /> <intl.FormattedMessage id="teacherRegistration.emailStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.emailStepDescription" /> <intl.FormattedMessage id="teacherRegistration.emailStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
@ -652,7 +660,7 @@ module.exports = {
required /> required />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
@ -669,7 +677,7 @@ module.exports = {
}, },
render: function () { render: function () {
return ( return (
<Slide className="last-step"> <Slide className="registration-step last-step">
<h2> <h2>
<intl.FormattedMessage id="registration.lastStepTitle" /> <intl.FormattedMessage id="registration.lastStepTitle" />
</h2> </h2>
@ -707,10 +715,99 @@ module.exports = {
); );
} }
})), })),
ClassInviteStep: React.createClass({
getDefaultProps: function () {
return {
classroom: {
title: '',
thumbnail: '',
educator: {
username: '',
profile: {
images: ''
}
}
},
messages: {
'general.getStarted': 'Get Started',
'registration.classroomInviteStepDescription': 'has invited you to join the class:'
},
waiting: false
};
},
onNextStep: function () {
this.props.onNextStep();
},
render: function () {
return (
<Slide className="registration-step class-invite-step">
<Avatar className="invite-avatar" src={this.props.classroom.educator.profile.images['50x50']} />
<h2>{this.props.classroom.educator.username}</h2>
<p className="description">
{this.props.messages['registration.classroomInviteStepDescription']}
</p>
<Card>
<div className="contents">
<h3>{this.props.classroom.title}</h3>
<img className="class-image" src={this.props.classroom.images['250x150']} />
</div>
<NextStepButton onClick={this.onNextStep}
waiting={this.props.waiting}
text={this.props.messages['general.getStarted']} />
</Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
</Slide>
);
}
}),
ClassWelcomeStep: React.createClass({
getDefaultProps: function () {
return {
classroom: {
title: '',
thumbnail: '',
educator: {
username: '',
profile: {
images: ''
}
}
},
messages: {
'registration.goToClass': 'Go to Class',
'registration.welcomeStepDescription': 'You have successfully set up a Scratch account! ' +
'You are now a member of the class:',
'registration.welcomeStepPrompt': 'To get started, click on the button below.',
'registration.welcomeStepTitle': 'Hurray! Welcome to Scratch!'
}
};
},
onNextStep: function () {
this.props.onNextStep();
},
render: function () {
return (
<Slide className="registration-step class-welcome-step">
<h2>{this.props.messages['registration.welcomeStepTitle']}</h2>
<p className="description">{this.props.messages['registration.welcomeStepDescription']}</p>
<Card>
<div className="contents">
<h3>{this.props.classroom.title}</h3>
<img className="class-image" src={this.props.classroom.images['250x150']} />
<p>{this.props.messages['registration.welcomeStepPrompt']}</p>
</div>
<NextStepButton onClick={this.onNextStep}
waiting={this.props.waiting}
text={this.props.messages['registration.goToClass']} />
</Card>
</Slide>
);
}
}),
RegistrationError: intl.injectIntl(React.createClass({ RegistrationError: intl.injectIntl(React.createClass({
render: function () { render: function () {
return ( return (
<Slide className="error-step"> <Slide className="registration-step error-step">
<h2>Something went wrong</h2> <h2>Something went wrong</h2>
<Card> <Card>
<h4>There was an error while processing your registration</h4> <h4>There was an error while processing your registration</h4>

View file

@ -0,0 +1,254 @@
@import "../../colors";
@import "../../frameless";
.registration-step {
.demographics-checkbox-is-robot {
display: none;
}
.invite-avatar {
display: block;
margin: 0 auto 1rem auto;
border: 2px solid $ui-white;
border-radius: 8px;
}
.gender-input,
.other-input {
float: right;
width: 90%;
.row {
margin-left: .5rem;
}
}
&.class-invite-step,
&.class-welcome-step {
.card {
text-align: center;
.contents {
padding: 2rem 0;
}
}
}
&.username-step,
&.name-step,
&.address-step,
&.email-step {
.help-block {
transform: translate(15.75rem, -4rem);
}
}
&.demographics-step {
.gender-input {
margin-top: -5.5rem;
}
.help-block {
transform: translate(13rem, -2rem);
}
.radio {
margin-right: 2.5rem;
line-height: 3rem;
input {
margin-right: 1rem;
}
}
}
&.phone-step {
.form-group {
margin-bottom: 2rem;
}
input {
&[type=checkbox] {
margin-bottom: 1.25rem;
}
}
.help-block {
margin-top: .5rem;
}
.checkbox-row {
.help-block {
margin-top: 0;
}
}
}
&.organization-step {
.help-block {
transform: translate(16rem, -4rem);
}
.checkbox-group {
.help-block {
transform: translate(16rem, -16rem);
}
}
.organization-type,
.url-input {
p {
margin: .25rem 0;
text-align: left;
color: $ui-dark-gray;
}
}
input {
&[value="8"] {
margin: 1rem 0;
}
}
.other-input {
margin-top: -5.75rem;
}
}
&.address-step {
.select {
.help-block {
transform: translate(0, .5rem);
}
}
}
&.usescratch-step {
.form {
.form-group {
margin-bottom: 0;
&.has-error {
.textarea {
border: 1px solid $ui-orange;
}
}
}
}
.help-block {
margin-top: .75rem;
}
p {
&.char-count {
margin-top: 0;
margin-bottom: 1rem;
text-align: right;
}
}
}
&.last-step,
&.error-step {
&.slide {
max-width: 38.75rem;
}
.card {
margin: 1rem auto;
padding: 1.5rem;
width: initial;
h4,
p {
text-align: left;
}
p {
margin: 0;
}
}
}
}
@media only screen and (max-width: $mobile - 1) {
.registration-step {
&.demographics-step {
.radio {
width: 100%;
text-align: left;
}
}
&.last-step,
&.error-step {
.card {
margin: 0 auto;
width: 18.75rem;
}
}
}
}
@media only screen and (max-width: $desktop - 1) {
.registration-step {
.form {
text-align: left;
}
&.username-step,
&.demographics-step,
&.name-step,
&.address-step,
&.email-step {
.help-block {
transform: none;
}
}
&.phone-step {
.checkbox,
.help-block {
text-align: left;
}
.checkbox {
margin-bottom: 1rem;
}
}
&.organization-step {
.checkbox-group {
text-align: left;
}
}
}
}
/* IE10 and IE11 fallback */
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.registration-step {
&.username-step,
&.demographics-step,
&.name-step,
&.phone-step,
&.organization-step,
&.address-step,
&.email-step {
.help-block {
position: relative;
transform: none;
margin: inherit;
width: 100%;
height: inherit;
&:before {
display: none;
}
}
}
}
}

View file

@ -1,7 +1,28 @@
@import "../../frameless"; @import "../../frameless";
@import "../../colors";
.slide { .slide {
padding: 10px; padding: 10px;
> h2,
> .description {
text-align: center;
color: $type-white;
}
> .description {
margin-top: 0;
margin-bottom: 2rem;
}
.card {
margin: 0 auto;
}
.step-navigation {
margin-top: 2rem;
text-align: center;
}
} }
@media only screen and (max-width: $tablet - 1) { @media only screen and (max-width: $tablet - 1) {

View file

@ -12,7 +12,6 @@
"general.copyright": "Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab", "general.copyright": "Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab",
"general.country": "Country", "general.country": "Country",
"general.create": "Create", "general.create": "Create",
"general.createUsername": "Create a Username",
"general.credits": "Credits", "general.credits": "Credits",
"general.discuss": "Discuss", "general.discuss": "Discuss",
"general.dmca": "DMCA", "general.dmca": "DMCA",
@ -77,9 +76,6 @@
"general.username": "Username", "general.username": "Username",
"general.validationEmail": "Please enter a valid email address", "general.validationEmail": "Please enter a valid email address",
"general.validationEmailMatch": "The emails do not match", "general.validationEmailMatch": "The emails do not match",
"general.validationUsernameExists": "Sorry, that username already exists",
"general.validationUsernameVulgar": "Hmm, that looks inappropriate",
"general.validationUsernameInvalid": "Invalid username",
"general.viewAll": "View All", "general.viewAll": "View All",
"general.website": "Website", "general.website": "Website",
"general.whatsHappening": "What's Happening?", "general.whatsHappening": "What's Happening?",
@ -109,13 +105,34 @@
"parents.FaqResourcesQ": "What resources are available for learning Scratch?", "parents.FaqResourcesQ": "What resources are available for learning Scratch?",
"parents.introDescription": "Scratch is a programming language and an online community where children can program and share interactive media such as stories, games, and animation with people from all over the world. As children create with Scratch, they learn to think creatively, work collaboratively, and reason systematically. Scratch is designed and maintained by the Lifelong Kindergarten group at the MIT Media Lab.", "parents.introDescription": "Scratch is a programming language and an online community where children can program and share interactive media such as stories, games, and animation with people from all over the world. As children create with Scratch, they learn to think creatively, work collaboratively, and reason systematically. Scratch is designed and maintained by the Lifelong Kindergarten group at the MIT Media Lab.",
"registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account", "registration.checkOutResources": "Get Started with Resources",
"registration.lastStepDescription": "We are currently processing your application. ", "registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>.",
"registration.confirmYourEmail": "Confirm Your Email", "registration.confirmYourEmail": "Confirm Your Email",
"registration.confirmYourEmailDescription": "If you haven't already, please click the link in the confirmation email sent to:", "registration.confirmYourEmailDescription": "If you haven't already, please click the link in the confirmation email sent to:",
"registration.createUsername": "Create a Username",
"registration.goToClass": "Go to Class",
"registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account",
"registration.lastStepDescription": "We are currently processing your application. ",
"registration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.",
"registration.nextStep": "Next Step",
"registration.notOnWebsite": "This information will not appear on the Scratch website.",
"registration.personalStepTitle": "Personal Information",
"registration.personalStepDescription": "Your individual responses will not be displayed publicly, and will be kept confidential and secure",
"registration.showPassword": "Show password",
"registration.usernameStepDescription": "Fill in the following forms to request an account. The approval process may take up to 24 hours.",
"registration.usernameStepTitle": "Request a Teacher Account",
"registration.validationPasswordLength": "Passwords must be at least six characters",
"registration.validationPasswordNotEquals": "Your password may not be \"password\"",
"registration.validationPasswordNotUsername": "Your password may not be your username",
"registration.validationUsernameRegexp": "Your username may only contain letters, numbers, \"-\", and \"_\"",
"registration.validationUsernameMinLength": "Usernames must be at least 3 characters",
"registration.validationUsernameMaxLength": "Usernames must be at most 20 characters",
"registration.validationUsernameExists": "Sorry, that username already exists",
"registration.validationUsernameVulgar": "Hmm, that looks inappropriate",
"registration.validationUsernameInvalid": "Invalid username",
"registration.waitForApproval": "Wait for Approval", "registration.waitForApproval": "Wait for Approval",
"registration.waitForApprovalDescription": "You can log into your Scratch Account now, but the features specific to Teachers are not yet available. Your information is being reviewed. Please be patient, the approval process can take up to 24 hours. You will receive an email indicating your account has been upgraded once your account has been approved.", "registration.waitForApprovalDescription": "You can log into your Scratch Account now, but the features specific to Teachers are not yet available. Your information is being reviewed. Please be patient, the approval process can take up to 24 hours. You will receive an email indicating your account has been upgraded once your account has been approved.",
"registration.checkOutResources": "Get Started with Resources", "registration.welcomeStepDescription": "You have successfully set up a Scratch account! You are now a member of the class:",
"registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>." "registration.welcomeStepPrompt": "To get started, click on the button below.",
"registration.welcomeStepTitle": "Hurray! Welcome to Scratch!"
} }

View file

@ -12,109 +12,16 @@
"title": "About" "title": "About"
}, },
{ {
"name": "developers", "name": "guidelines",
"pattern": "^/developers/?$", "pattern": "^/community_guidelines/?$",
"view": "developers/developers", "view": "guidelines/guidelines",
"title": "Developers" "title": "Scratch Community Guidelines"
}, },
{ {
"name": "hoc", "name": "student-registration",
"pattern": "^/hoc/?$", "pattern": "^/classes/:id/register/:token",
"view": "hoc/hoc", "view": "studentregistration/studentregistration",
"title": "Hour of Code" "title": "Class Registration"
},
{
"name": "explore",
"pattern": "^/explore/:projects/:all/?$",
"routeAlias": "^/explore(?!/ajax)",
"view": "explore/explore",
"title": "Explore"
},
{
"name": "explore-redirect",
"pattern": "^/explore/?$",
"routeAlias": "^/explore(?!/ajax)",
"redirect": "/explore/projects/all"
},
{
"name": "explore-projects-redirect",
"pattern": "^/explore/projects/?$",
"routeAlias": "^/explore(?!/ajax)",
"redirect": "/explore/projects/all"
},
{
"name": "explore-studios-redirect",
"pattern": "^/explore/studios/?$",
"routeAlias": "^/explore(?!/ajax)",
"redirect": "/explore/studios/all"
},
{
"name": "search",
"pattern": "^/search/:projects?$/?$",
"routeAlias": "^/search",
"view": "search/search",
"title": "Search"
},
{
"name": "credits",
"pattern": "^/info/credits/?$",
"view": "credits/credits",
"title": "Credits"
},
{
"name": "faq",
"pattern": "^/info/faq/?$",
"view": "faq/faq",
"title": "FAQ"
},
{
"name": "educator-landing",
"pattern": "^/educators/?$",
"view": "teachers/landing/landing",
"title": "Educators"
},
{
"name": "teacher-faq",
"pattern": "^/educators/faq/?$",
"view": "teachers/faq/faq",
"title": "Teacher Accounts FAQ"
},
{
"name": "cards",
"pattern": "^/info/cards/?$",
"view": "cards/cards",
"title": "Cards"
},
{
"name": "communityblocks-interviews",
"pattern": "^/info/communityblocks-interviews/?$",
"view": "communityblocks-interviews/communityblocks-interviews",
"title": "Community Blocks Beta Tester Interviews"
},
{
"name": "jobs",
"pattern": "^/jobs/?$",
"view": "jobs/jobs",
"title": "Jobs"
},
{
"name": "teacherregistration",
"pattern": "^/educators/register$",
"view": "teacherregistration/teacherregistration",
"title": "Teacher Registration",
"viewportWidth": "device-width"
},
{
"name": "teacherwaitingroom",
"pattern": "^/educators/waiting",
"view": "teacherwaitingroom/teacherwaitingroom",
"title": "Thank you for requesting a Scratch Teacher Account"
},
{
"name": "wedo2",
"pattern": "^/wedo/?$",
"view": "wedo2/wedo2",
"title": "LEGO WeDo 2.0"
}, },
{ {
"name": "conference-index", "name": "conference-index",
@ -157,9 +64,10 @@
"viewportWidth": "device-width" "viewportWidth": "device-width"
}, },
{ {
"name": "donate", "name": "developers",
"pattern": "^/info/donate/?", "pattern": "^/developers/?$",
"redirect": "https://secure.donationpay.org/scratchfoundation/" "view": "developers/developers",
"title": "Developers"
}, },
{ {
"name": "dmca", "name": "dmca",
@ -168,10 +76,72 @@
"title": "DMCA" "title": "DMCA"
}, },
{ {
"name": "guidelines", "name": "educator-landing",
"pattern": "^/community_guidelines/?$", "pattern": "^/educators/?$",
"view": "guidelines/guidelines", "view": "teachers/landing/landing",
"title": "Scratch Community Guidelines" "title": "Educators"
},
{
"name": "teacher-faq",
"pattern": "^/educators/faq/?$",
"view": "teachers/faq/faq",
"title": "Teacher Accounts FAQ"
},
{
"name": "teacherregistration",
"pattern": "^/educators/register$",
"view": "teacherregistration/teacherregistration",
"title": "Teacher Registration",
"viewportWidth": "device-width"
},
{
"name": "teacherwaitingroom",
"pattern": "^/educators/waiting",
"view": "teacherwaitingroom/teacherwaitingroom",
"title": "Thank you for requesting a Scratch Teacher Account"
},
{
"name": "explore",
"pattern": "^/explore/:projects/:all/?$",
"routeAlias": "^/explore(?!/ajax)",
"view": "explore/explore",
"title": "Explore"
},
{
"name": "hoc",
"pattern": "^/hoc/?$",
"view": "hoc/hoc",
"title": "Hour of Code"
},
{
"name": "cards",
"pattern": "^/info/cards/?$",
"view": "cards/cards",
"title": "Cards"
},
{
"name": "communityblocks-interviews",
"pattern": "^/info/communityblocks-interviews/?$",
"view": "communityblocks-interviews/communityblocks-interviews",
"title": "Community Blocks Beta Tester Interviews"
},
{
"name": "credits",
"pattern": "^/info/credits/?$",
"view": "credits/credits",
"title": "Credits"
},
{
"name": "faq",
"pattern": "^/info/faq/?$",
"view": "faq/faq",
"title": "FAQ"
},
{
"name": "jobs",
"pattern": "^/jobs/?$",
"view": "jobs/jobs",
"title": "Jobs"
}, },
{ {
"name": "privacypolicy", "name": "privacypolicy",
@ -179,10 +149,46 @@
"view": "privacypolicy/privacypolicy", "view": "privacypolicy/privacypolicy",
"title": "Privacy Policy" "title": "Privacy Policy"
}, },
{
"name": "search",
"pattern": "^/search/:projects?$/?$",
"routeAlias": "^/search",
"view": "search/search",
"title": "Search"
},
{ {
"name": "terms", "name": "terms",
"pattern": "^/terms_of_use/?$", "pattern": "^/terms_of_use/?$",
"view": "terms/terms", "view": "terms/terms",
"title": "Scratch Terms of Use" "title": "Scratch Terms of Use"
},
{
"name": "wedo2",
"pattern": "^/wedo/?$",
"view": "wedo2/wedo2",
"title": "LEGO WeDo 2.0"
},
{
"name": "donate",
"pattern": "^/info/donate/?",
"redirect": "https://secure.donationpay.org/scratchfoundation/"
},
{
"name": "explore-redirect",
"pattern": "^/explore/?$",
"routeAlias": "^/explore(?!/ajax)",
"redirect": "/explore/projects/all"
},
{
"name": "explore-projects-redirect",
"pattern": "^/explore/projects/?$",
"routeAlias": "^/explore(?!/ajax)",
"redirect": "/explore/projects/all"
},
{
"name": "explore-studios-redirect",
"pattern": "^/explore/studios/?$",
"routeAlias": "^/explore(?!/ajax)",
"redirect": "/explore/studios/all"
} }
] ]

View file

@ -0,0 +1,3 @@
{
"studentRegistration.classroomApiGeneralError": "Sorry, we could not find the registration information for this class"
}

View file

@ -0,0 +1,124 @@
var defaults = require('lodash.defaultsdeep');
var React = require('react');
var render = require('../../lib/render.jsx');
var api = require('../../lib/api');
var intl = require('../../lib/intl.jsx');
var Deck = require('../../components/deck/deck.jsx');
var Progression = require('../../components/progression/progression.jsx');
var Steps = require('../../components/registration/steps.jsx');
require('./studentregistration.scss');
var StudentRegistration = intl.injectIntl(React.createClass({
type: 'StudentRegistration',
getDefaultProps: function () {
return {
classroomId: null,
classroomToken: null
};
},
getInitialState: function () {
return {
formData: {},
registrationError: null,
step: 0,
waiting: false
};
},
advanceStep: function (formData) {
formData = formData || {};
this.setState({
step: this.state.step + 1,
formData: defaults({}, formData, this.state.formData)
});
},
componentDidMount: function () {
api({
uri: '/classrooms/' + this.props.classroomId + '/' + this.props.classroomToken
}, function (err, body, res) {
if (err) {
return this.setState({
registrationError: this.props.intl.formatMessage({
id: 'studentRegistration.classroomApiGeneralError'
})
});
}
if (res.statusCode === 404) {
// TODO: Use react-router for this
return window.location = '/404';
}
this.setState({classroom: body});
}.bind(this));
},
register: function (formData) {
this.setState({waiting: true});
formData = defaults({}, formData || {}, this.state.formData);
api({
host: '',
uri: '/classes/register_new_student/',
method: 'post',
useCsrf: true,
formData: {
username: formData.user.username,
password: formData.user.password,
birth_month: formData.user.birth.month,
birth_year: formData.user.birth.year,
gender: (
formData.user.gender === 'other' ?
formData.user.genderOther :
formData.user.gender
),
country: formData.user.country,
is_robot: formData.user.isRobot,
classroom_id: this.props.classroomId,
classroom_token: this.props.classroomToken
}
}, function (err, res) {
this.setState({waiting: false});
if (err) return this.setState({registrationError: err});
if (res[0].success) return this.advanceStep(formData);
this.setState({registrationError: res[0].msg});
}.bind(this));
},
goToClass: function () {
window.location = '/classes/' + this.props.classroomId + '/';
},
render: function () {
var demographicsDescription = this.props.intl.formatMessage({id: 'registration.notOnWebsite'});
return (
<Deck className="student-registration">
{this.state.registrationError ?
<Steps.RegistrationError {... this.state} />
:
<Progression {... this.state}>
<Steps.ClassInviteStep classroom={this.state.classroom}
messages={this.props.messages}
onNextStep={this.advanceStep}
waiting={this.state.waiting} />
<Steps.UsernameStep onNextStep={this.advanceStep}
waiting={this.state.waiting} />
<Steps.DemographicsStep description={demographicsDescription}
onNextStep={this.register}
waiting={this.state.waiting} />
<Steps.ClassWelcomeStep classroom={this.state.classroom}
messages={this.props.messages}
onNextStep={this.goToClass}
waiting={this.state.waiting} />
</Progression>
}
</Deck>
);
}
}));
var [classroomId, _, classroomToken] = document.location.pathname.split('/')
.filter(function (p) {
if (p) return p;
})
.slice(-3);
var props = {classroomId, classroomToken};
render(<StudentRegistration {... props} />, document.getElementById('app'));

View file

@ -0,0 +1,13 @@
@import "../../colors";
@import "../../frameless";
@include responsive-layout (".student-registration", ".slide");
html,
body {
background-color: darken($ui-purple, 8%);
}
.teacher-registration {
background-color: $ui-purple;
}

View file

@ -1,19 +1,6 @@
{ {
"teacherRegistration.usernameStepDescription": "Fill in the following forms to request an account. The approval process may take up to 24 hours.",
"teacherRegistration.usernameStepTitle": "Request a Teacher Account",
"teacherRegistration.validationUsernameRegexp": "Your username may only contain letters, numbers, \"-\", and \"_\"",
"teacherRegistration.validationUsernameMinLength": "Usernames must be at least 3 characters",
"teacherRegistration.validationUsernameMaxLength": "Usernames must be at most 20 characters",
"teacherRegistration.validationPasswordLength": "Passwords must be at least six characters",
"teacherRegistration.validationPasswordNotEquals": "Your password may not be \"password\"",
"teacherRegistration.validationPasswordNotUsername": "Your password may not be your username",
"teacherRegistration.showPassword": "Show password",
"teacherRegistration.nextStep": "Next Step",
"teacherRegistration.personalStepTitle": "Personal Information",
"teacherRegistration.personalStepDescription": "Your individual responses will not be displayed publicly, and will be kept confidential and secure",
"teacherRegistration.nameStepTitle": "First &amp; Last Name", "teacherRegistration.nameStepTitle": "First &amp; Last Name",
"teacherRegistration.nameStepDescription": "Your name will not be displayed publicly, and will be kept confidential and secure.", "teacherRegistration.nameStepDescription": "Your name will not be displayed publicly, and will be kept confidential and secure.",
"teacherRegistration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.",
"teacherRegistration.firstName": "First Name", "teacherRegistration.firstName": "First Name",
"teacherRegistration.lastName": "Last Name", "teacherRegistration.lastName": "Last Name",
"teacherRegistration.phoneStepTitle": "Phone Number", "teacherRegistration.phoneStepTitle": "Phone Number",

View file

@ -79,28 +79,28 @@ var TeacherRegistration = React.createClass({
<Steps.RegistrationError {... this.state} /> <Steps.RegistrationError {... this.state} />
: :
<Progression {... this.state}> <Progression {... this.state}>
<Steps.UsernameStep onNextStep={this.advanceStep} <Steps.UsernameStep onNextStep={this.advanceStep}
waiting={this.state.waiting} /> waiting={this.state.waiting} />
<Steps.DemographicsStep onNextStep={this.advanceStep} <Steps.DemographicsStep onNextStep={this.advanceStep}
waiting={this.state.waiting} /> waiting={this.state.waiting} />
<Steps.NameStep onNextStep={this.advanceStep} <Steps.NameStep onNextStep={this.advanceStep}
waiting={this.state.waiting} /> waiting={this.state.waiting} />
<Steps.PhoneNumberStep onNextStep={this.advanceStep} <Steps.PhoneNumberStep onNextStep={this.advanceStep}
waiting={this.state.waiting} waiting={this.state.waiting}
defaultCountry={ defaultCountry={
this.state.formData.user && this.state.formData.user.country this.state.formData.user && this.state.formData.user.country
} /> } />
<Steps.OrganizationStep onNextStep={this.advanceStep} <Steps.OrganizationStep onNextStep={this.advanceStep}
waiting={this.state.waiting} /> waiting={this.state.waiting} />
<Steps.AddressStep onNextStep={this.advanceStep} <Steps.AddressStep onNextStep={this.advanceStep}
waiting={this.state.waiting} waiting={this.state.waiting}
defaultCountry={ defaultCountry={
this.state.formData.user && this.state.formData.user.country this.state.formData.user && this.state.formData.user.country
} /> } />
<Steps.UseScratchStep onNextStep={this.advanceStep} <Steps.UseScratchStep onNextStep={this.advanceStep}
waiting={this.state.waiting} /> waiting={this.state.waiting} />
<Steps.EmailStep onNextStep={this.register} <Steps.EmailStep onNextStep={this.register}
waiting={this.state.waiting} /> waiting={this.state.waiting} />
<Steps.TeacherApprovalStep email={this.state.formData.user && this.state.formData.user.email} /> <Steps.TeacherApprovalStep email={this.state.formData.user && this.state.formData.user.email} />
</Progression> </Progression>
} }

View file

@ -10,209 +10,4 @@ body {
.teacher-registration { .teacher-registration {
background-color: $ui-purple; background-color: $ui-purple;
.demographics-checkbox-is-robot {
display: none;
}
.gender-input,
.other-input {
float: right;
width: 90%;
.row {
margin-left: .5rem;
}
}
.username-step,
.name-step,
.address-step,
.email-step {
.help-block {
transform: translate(15.75rem, -4rem);
}
}
.demographics-step {
.gender-input {
margin-top: -5.5rem;
}
.help-block {
transform: translate(13rem, -2rem);
}
.radio {
margin-right: 2.5rem;
line-height: 3rem;
input {
margin-right: 1rem;
}
}
}
.phone-step {
.form-group {
margin-bottom: 2rem;
}
input {
&[type=checkbox] {
margin-bottom: 1.25rem;
}
}
.help-block {
margin-top: .5rem;
}
.checkbox-row {
.help-block {
margin-top: 0;
}
}
}
.organization-step {
.help-block {
transform: translate(16rem, -4rem);
}
.checkbox-group {
.help-block {
transform: translate(16rem, -16rem);
}
}
.organization-type,
.url-input {
p {
margin: .25rem 0;
text-align: left;
color: $ui-dark-gray;
}
}
input {
&[value="8"] {
margin: 1rem 0;
}
}
.other-input {
margin-top: -5.75rem;
}
}
.address-step {
.select {
.help-block {
transform: translate(0, .5rem);
}
}
}
.usescratch-step {
.form {
.form-group {
margin-bottom: 0;
&.has-error {
.textarea {
border: 1px solid $ui-orange;
}
}
}
}
.help-block {
margin-top: .75rem;
}
p {
&.char-count {
margin-top: 0;
margin-bottom: 1rem;
text-align: right;
}
}
}
.last-step,
.error-step {
&.slide {
max-width: 38.75rem;
}
.card {
margin: 1rem auto;
padding: 1.5rem;
width: initial;
h4,
p {
text-align: left;
}
p {
margin: 0;
}
}
}
}
@media only screen and (max-width: $mobile - 1) {
.teacher-registration {
.demographics-step {
.radio {
width: 100%;
text-align: left;
}
}
.last-step,
.error-step {
.card {
margin: 0 auto;
width: 18.75rem;
}
}
}
}
@media only screen and (max-width: $desktop - 1) {
.teacher-registration {
.form {
text-align: left;
}
.username-step,
.demographics-step,
.name-step {
.help-block {
transform: none;
}
}
.phone-step {
.checkbox,
.help-block {
text-align: left;
}
.checkbox {
margin-bottom: 1rem;
}
}
.organization-step {
.checkbox-group {
text-align: left;
}
}
}
} }

View file

@ -1,16 +1,17 @@
{ {
"teacherlanding.title": "Scratch for Educators", "teacherlanding.title": "Scratch for Educators",
"teacherlanding.intro": "Your students can use Scratch to code their own interactive stories, animations, and games. In the process, they learn to think creatively, reason systematically, and work collaboratively — essential skills for everyone in todays society.", "teacherlanding.intro": "Your students can use Scratch to code their own interactive stories, animations, and games. In the process, they learn to think creatively, reason systematically, and work collaboratively — essential skills for everyone in todays society.",
"teacherlanding.inPracticeAnchor": "In Practice", "teacherlanding.inPracticeAnchor": "Who Uses Scratch?",
"teacherlanding.resourcesAnchor": "Resources", "teacherlanding.resourcesAnchor": "Resources",
"teacherlanding.inPracticeTitle": "Who Uses Scratch?", "teacherlanding.inPracticeTitle": "Who Uses Scratch?",
"teacherlanding.inPracticeIntro": "Educators are using Scratch in a wide variety of: ", "teacherlanding.inPracticeIntro": "Educators are using Scratch in a wide variety of: ",
"teacherlanding.generalUsageSettings": "<b>Settings:</b> schools, museums, libraries, community centers", "teacherlanding.generalUsageSettings": "<b>Settings:</b> schools, museums, libraries, community centers",
"teacherlanding.generalUsageGradeLevels": "<b>Grade Levels:</b> elementary, middle, and high school (and some colleges too!)", "teacherlanding.generalUsageGradeLevels": "<b>Grade Levels:</b> elementary, middle, and high school (and some colleges too!)",
"teacherlanding.generalUsageSubjectAreas": "<b>Subject Areas:</b> language arts, science, social studies, math, computer science, foreign languages, and the arts", "teacherlanding.generalUsageSubjectAreas": "<b>Subject Areas:</b> language arts, science, social studies, math, computer science, foreign languages, and the arts",
"teacherlanding.ingridTitle": "Instructional Technology Specialist", "teacherlanding.elementaryTitle": "Elementary Schools",
"teacherlanding.dylanTitle": "Educational Technologist", "teacherlanding.middleTitle": "Middle Schools",
"teacherlanding.afterSchoolTitle": "After-School Program", "teacherlanding.communityTitle": "Community Programs",
"teacherlanding.afterSchoolTitle": "After-School Programs",
"teacherlanding.resourcesTitle": "Educator Resources", "teacherlanding.resourcesTitle": "Educator Resources",
"teacherlanding.scratchEdTitle": "A Community for Educators", "teacherlanding.scratchEdTitle": "A Community for Educators",
"teacherlanding.scratchEdDescription": "<a href=\"http://scratched.gse.harvard.edu/\">ScratchEd</a> is an online community where Scratch educators <a href=\"http://scratched.gse.harvard.edu/stories\">share stories</a>, exchange resources, ask questions, and find people. ScratchEd is developed and supported by the Harvard Graduate School of Education.", "teacherlanding.scratchEdDescription": "<a href=\"http://scratched.gse.harvard.edu/\">ScratchEd</a> is an online community where Scratch educators <a href=\"http://scratched.gse.harvard.edu/stories\">share stories</a>, exchange resources, ask questions, and find people. ScratchEd is developed and supported by the Harvard Graduate School of Education.",

View file

@ -62,34 +62,34 @@ var Landing = injectIntl(React.createClass({
<p><FormattedHTMLMessage id="teacherlanding.generalUsageSubjectAreas"/></p> <p><FormattedHTMLMessage id="teacherlanding.generalUsageSubjectAreas"/></p>
</FlexRow> </FlexRow>
<FlexRow className="stories"> <FlexRow className="stories">
<a href="//bit.ly/28SBsa9" className="story">
<img src="/images/teachers/stories/ingrid.jpg" alt="ingrid's story" />
<div className="story-info">
<p className="name">Ingrid Gustafson</p>
<p><FormattedMessage id="teacherlanding.ingridTitle" /></p>
</div>
</a>
<a href="//bit.ly/28Q5l6P" className="story"> <a href="//bit.ly/28Q5l6P" className="story">
<img src="/images/teachers/stories/dylan.jpg" alt="dylan's story" /> <img src="/images/teachers/stories/dylan.jpg" alt="Elementary Schools" />
<div className="story-info"> <div className="story-info">
<p className="name">Dylan Ryder</p> <p className="name"><FormattedMessage id="teacherlanding.elementaryTitle" /></p>
<p><FormattedMessage id="teacherlanding.dylanTitle" /></p> <p>The School at Columbia University</p>
</div>
</a>
<a href="//bit.ly/28SBsa9" className="story">
<img src="/images/teachers/stories/ingrid.jpg" alt="Middle Schools" />
<div className="story-info">
<p className="name"><FormattedMessage id="teacherlanding.middleTitle" /></p>
<p>Cambridge Public Schools</p>
</div> </div>
</a> </a>
<a href="//bit.ly/28SC1AY" className="story"> <a href="//bit.ly/28SC1AY" className="story">
<img src="/images/teachers/stories/plug-in-studio.jpg" <img src="/images/teachers/stories/plug-in-studio.jpg"
alt="plug in studio's story" /> alt="Community Programs" />
<div className="story-info"> <div className="story-info">
<p className="name">Plug-In Studios</p> <p className="name"><FormattedMessage id="teacherlanding.communityTitle" /></p>
<p><FormattedMessage id="teacherlanding.afterSchoolTitle" /></p> <p>Plug-In Studio</p>
</div> </div>
</a> </a>
<a href="//bit.ly/28UzapJ" className="story"> <a href="//bit.ly/28UzapJ" className="story">
<img src="/images/teachers/stories/ghana-code-club.jpg" <img src="/images/teachers/stories/ghana-code-club.jpg"
alt="ghana code club's story" /> alt="After-School Programs" />
<div className="story-info"> <div className="story-info">
<p className="name">Ghana Code Club</p> <p className="name"><FormattedMessage id="teacherlanding.afterSchoolTitle" /></p>
<p><FormattedMessage id="teacherlanding.afterSchoolTitle" /></p> <p>Ghana Code Club</p>
</div> </div>
</a> </a>
</FlexRow> </FlexRow>