Merge pull request #3241 from benjiwheeler/join-flow-info-button

Join flow info button
This commit is contained in:
Benjamin Wheeler 2019-08-13 17:49:29 -04:00 committed by GitHub
commit 2e506de6be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 0 deletions

View file

@ -0,0 +1,46 @@
const bindAll = require('lodash.bindall');
const PropTypes = require('prop-types');
const React = require('react');
require('./info-button.scss');
class InfoButton extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleHideMessage',
'handleShowMessage'
]);
this.state = {
visible: false
};
}
handleHideMessage () {
this.setState({visible: false});
}
handleShowMessage () {
this.setState({visible: true});
}
render () {
return (
<div
className="info-button"
onClick={this.handleShowMessage}
onMouseOut={this.handleHideMessage}
onMouseOver={this.handleShowMessage}
>
{this.state.visible && (
<div className="info-button-message">
{this.props.message}
</div>
)}
</div>
);
}
}
InfoButton.propTypes = {
message: PropTypes.string
};
module.exports = InfoButton;

View file

@ -0,0 +1,78 @@
@import "../../colors";
@import "../../frameless";
.info-button {
position: relative;
display: inline-block;
width: 1rem;
height: 1rem;
margin-left: .375rem;
border-radius: 50%;
background-color: $ui-blue;
&:after {
position: absolute;
content: "?";
color: $ui-white;
font-family: verdana;
font-weight: 400;
top: -.125rem;
left: .325rem;
font-size: .75rem;
}
}
.info-button-message {
$arrow-border-width: 1rem;
display: block;
position: absolute;
top: 0;
left: 0;
transform: translate(1rem, -1rem);
width: 16.5rem;
min-height: 1rem;
margin-left: $arrow-border-width;
border: 1px solid $active-gray;
border-radius: 5px;
padding: .75rem;
overflow: visible;
background-color: $ui-blue;
color: $type-white;
line-height: 1.25rem;
text-align: left;
font-size: .875rem;
z-index: 1;
&: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-blue;
width: $arrow-border-width;
height: $arrow-border-width;
content: "";
}
}
@media #{$intermediate-and-smaller} {
.info-button-message {
position: relative;
transform: none;
margin: inherit;
width: 100%;
height: inherit;
&:before {
display: none;
}
}
}

View file

@ -88,6 +88,7 @@ class BirthDateStep extends React.Component {
<JoinFlowStep
description={this.props.intl.formatMessage({id: 'registration.private'})}
headerImgSrc="/images/hoc/getting-started.jpg"
infoMessage={this.props.intl.formatMessage({id: 'registration.birthDateStepInfo'})}
title={this.props.intl.formatMessage({id: 'registration.birthDateStepTitle'})}
waiting={isSubmitting}
onSubmit={handleSubmit}

View file

@ -82,6 +82,7 @@ class GenderStep extends React.Component {
<JoinFlowStep
className="join-flow-gender-step"
description={this.props.intl.formatMessage({id: 'registration.genderStepDescription'})}
infoMessage={this.props.intl.formatMessage({id: 'registration.genderStepInfo'})}
title={this.props.intl.formatMessage({id: 'registration.genderStepTitle'})}
waiting={isSubmitting}
onSubmit={handleSubmit}

View file

@ -5,6 +5,7 @@ const PropTypes = require('prop-types');
const NextStepButton = require('./next-step-button.jsx');
const ModalTitle = require('../modal/base/modal-title.jsx');
const ModalInnerContent = require('../modal/base/modal-inner-content.jsx');
const InfoButton = require('../info-button/info-button.jsx');
require('./join-flow-step.scss');
@ -13,6 +14,7 @@ const JoinFlowStep = ({
className,
description,
headerImgSrc,
infoMessage,
innerContentClassName,
nextButton,
onSubmit,
@ -42,6 +44,9 @@ const JoinFlowStep = ({
{description && (
<div className="join-flow-description">
{description}
{infoMessage && (
<InfoButton message={infoMessage} />
)}
</div>
)}
{children}
@ -59,6 +64,7 @@ JoinFlowStep.propTypes = {
className: PropTypes.string,
description: PropTypes.string,
headerImgSrc: PropTypes.string,
infoMessage: PropTypes.string,
innerContentClassName: PropTypes.string,
nextButton: PropTypes.node,
onSubmit: PropTypes.func,

View file

@ -141,6 +141,7 @@
"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.",
"registration.birthDateStepInfo": "This helps us understand the age range of people who use Scratch. We use this to confirm account ownership if you contact our team. This information will not be made public on your account.",
"registration.birthDateStepTitle": "When were you born?",
"registration.checkOutResources": "Get Started with Resources",
"registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>.",
@ -156,6 +157,7 @@
"registration.createUsername": "Create a username",
"registration.genderStepTitle": "What's your gender?",
"registration.genderStepDescription": "Scratch welcomes people of all genders. We will always keep this information private.",
"registration.genderStepInfo": "This helps us understand who uses Scratch, so that we can broaden participation. This information will not be made public on your account.",
"registration.genderOptionAnother": "Another gender:",
"registration.genderOptionPreferNotToSay": "Prefer not to say",
"registration.emailStepTitle": "What's your email?",

View file

@ -0,0 +1,34 @@
import React from 'react';
import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
import InfoButton from '../../../src/components/info-button/info-button';
describe('InfoButton', () => {
test('Info button defaults to not visible', () => {
const component = mountWithIntl(
<InfoButton
message="Here is some info about something!"
/>
);
expect(component.find('div.info-button-message').exists()).toEqual(false);
});
test('clicking on info button makes info message visible', () => {
const component = mountWithIntl(
<InfoButton
message="Here is some info about something!"
/>
);
component.find('div.info-button').simulate('click');
expect(component.find('div.info-button-message').exists()).toEqual(true);
});
test('after message is visible, mouseOut makes it vanish', () => {
const component = mountWithIntl(
<InfoButton
message="Here is some info about something!"
/>
);
component.find('div.info-button').simulate('click');
expect(component.find('div.info-button-message').exists()).toEqual(true);
component.find('div.info-button').simulate('mouseOut');
expect(component.find('div.info-button-message').exists()).toEqual(false);
});
});