mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-16 16:19:48 -05:00
Merge pull request #3436 from benjiwheeler/join-flow-toggle-info-button
Join flow info button has larger touch target, can be clicked to toggle
This commit is contained in:
commit
099d5a1007
5 changed files with 143 additions and 23 deletions
|
@ -11,19 +11,45 @@ class InfoButton extends React.Component {
|
|||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleHideMessage',
|
||||
'handleShowMessage'
|
||||
'handleClick',
|
||||
'handleMouseLeave',
|
||||
'handleShowMessage',
|
||||
'setButtonRef'
|
||||
]);
|
||||
this.state = {
|
||||
requireClickToClose: false, // default to closing on mouseout
|
||||
visible: false
|
||||
};
|
||||
}
|
||||
handleHideMessage () {
|
||||
this.setState({visible: false});
|
||||
componentWillMount () {
|
||||
window.addEventListener('mousedown', this.handleClick, false);
|
||||
}
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('mousedown', this.handleClick, false);
|
||||
}
|
||||
handleClick (e) {
|
||||
if (this.buttonRef) { // only handle click if we can tell whether it happened in this component
|
||||
let newVisibleState = false; // for most clicks, hide the info message
|
||||
if (this.buttonRef.contains(e.target)) { // if the click was inside the info icon...
|
||||
newVisibleState = !this.state.requireClickToClose; // toggle it
|
||||
}
|
||||
this.setState({
|
||||
requireClickToClose: newVisibleState,
|
||||
visible: newVisibleState
|
||||
});
|
||||
}
|
||||
}
|
||||
handleMouseLeave () {
|
||||
if (this.state.visible && !this.state.requireClickToClose) {
|
||||
this.setState({visible: false});
|
||||
}
|
||||
}
|
||||
handleShowMessage () {
|
||||
this.setState({visible: true});
|
||||
}
|
||||
setButtonRef (element) {
|
||||
this.buttonRef = element;
|
||||
}
|
||||
render () {
|
||||
const messageJsx = this.state.visible && (
|
||||
<div className="info-button-message">
|
||||
|
@ -34,8 +60,8 @@ class InfoButton extends React.Component {
|
|||
<React.Fragment>
|
||||
<div
|
||||
className="info-button"
|
||||
onClick={this.handleShowMessage}
|
||||
onMouseOut={this.handleHideMessage}
|
||||
ref={this.setButtonRef}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
onMouseOver={this.handleShowMessage}
|
||||
>
|
||||
<MediaQuery minWidth={frameless.desktop}>
|
||||
|
|
|
@ -4,23 +4,21 @@
|
|||
.info-button {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-left: .375rem;
|
||||
margin-top: -.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: $type-gray-60percent;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-left: -.125rem;
|
||||
margin-top: -.75rem;
|
||||
background-image: url("/svgs/info-button/info-button.svg");
|
||||
background-size: cover;
|
||||
top: .1875rem;
|
||||
top: .6875rem;
|
||||
}
|
||||
|
||||
.info-button-message {
|
||||
$arrow-border-width: 1rem;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
top: .375rem;
|
||||
left: .5rem;
|
||||
transform: translate(1rem, -1rem);
|
||||
width: 16.5rem;
|
||||
min-height: 1rem;
|
||||
|
@ -66,6 +64,7 @@
|
|||
we need to center this element within its width. */
|
||||
margin: 0 calc((100% - 16.5rem) / 2);;
|
||||
top: .125rem;
|
||||
left: 0;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
|
|
|
@ -127,10 +127,11 @@
|
|||
}
|
||||
|
||||
.join-flow-privacy-message {
|
||||
margin: 1rem auto;
|
||||
margin: .5rem auto 1rem;
|
||||
font-size: .75rem;
|
||||
font-weight: 500;
|
||||
color: $type-gray-60percent;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.join-flow-inner-username-step {
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><g fill-rule="evenodd"><path d="M10 0a10 10 0 1 0 10 10A10 10 0 0 0 10 0" fill="#9a9eac"/><path d="M10 13.39a1.33 1.33 0 1 1-1.33 1.33A1.33 1.33 0 0 1 10 13.39zm2.68-8.77a3 3 0 0 1 1.42 2.31 3.15 3.15 0 0 1-.95 2.56 8.37 8.37 0 0 1-1.59 1.1c-.7.39-.7.41-.7.77 0 .55 0 1-1 1s-1-.45-1-1a2.65 2.65 0 0 1 1.72-2.52A6.61 6.61 0 0 0 11.79 8a1.22 1.22 0 0 0 .3-.91 1 1 0 0 0-.5-.79 2.8 2.8 0 0 0-2.87.2c-.98.64-.81 1.55-1.72 1.3s-.92-1-.53-1.7a3.94 3.94 0 0 1 1.9-1.66 4.67 4.67 0 0 1 4.3.18z" fill="#fff"/></g></svg>
|
||||
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="40" height="40"><g fill-rule="evenodd"><path d="M20 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10" fill="#9a9eac"/><path d="M20 23.39a1.33 1.33 0 1 1-1.33 1.33A1.34 1.34 0 0 1 20 23.39zm2.68-8.77a3 3 0 0 1 1.42 2.31 3.14 3.14 0 0 1-1 2.56 8.2 8.2 0 0 1-1.59 1.1c-.7.39-.7.41-.7.77 0 .55 0 1-1 1s-1-.45-1-1a2.64 2.64 0 0 1 1.72-2.52 6.71 6.71 0 0 0 1.26-.84 1.22 1.22 0 0 0 .3-.91 1 1 0 0 0-.5-.79 2.8 2.8 0 0 0-2.87.2c-1 .64-.81 1.55-1.72 1.3s-.92-1-.53-1.7a3.9 3.9 0 0 1 1.9-1.66 4.67 4.67 0 0 1 4.3.18z" fill="#fff"/></g></svg>
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 589 B |
|
@ -3,6 +3,15 @@ import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
|
|||
import InfoButton from '../../../src/components/info-button/info-button';
|
||||
|
||||
describe('InfoButton', () => {
|
||||
// mock window.addEventListener
|
||||
// for more on this technique, see discussion at https://github.com/airbnb/enzyme/issues/426#issuecomment-253515886
|
||||
const mockedAddEventListener = {};
|
||||
/* eslint-disable no-undef */
|
||||
window.addEventListener = jest.fn((event, cb) => {
|
||||
mockedAddEventListener[event] = cb;
|
||||
});
|
||||
/* eslint-enable no-undef */
|
||||
|
||||
test('Info button defaults to not visible', () => {
|
||||
const component = mountWithIntl(
|
||||
<InfoButton
|
||||
|
@ -11,33 +20,118 @@ describe('InfoButton', () => {
|
|||
);
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(false);
|
||||
});
|
||||
test('mouseOver on info button makes info message visible', () => {
|
||||
|
||||
test('mouseOver on info button makes info message visible', done => {
|
||||
const component = mountWithIntl(
|
||||
<InfoButton
|
||||
message="Here is some info about something!"
|
||||
/>
|
||||
);
|
||||
|
||||
// mouseOver info button
|
||||
component.find('div.info-button').simulate('mouseOver');
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
setTimeout(function () { // necessary because mouseover uses debounce
|
||||
// crucial: if we don't call update(), then find() below looks through an OLD
|
||||
// version of the DOM! see https://github.com/airbnb/enzyme/issues/1233#issuecomment-358915200
|
||||
component.update();
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
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');
|
||||
const buttonRef = component.instance().buttonRef;
|
||||
|
||||
// click on info button
|
||||
mockedAddEventListener.mousedown({target: buttonRef});
|
||||
component.update();
|
||||
expect(component.find('div.info-button').exists()).toEqual(true);
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
});
|
||||
test('after message is visible, mouseOut makes it vanish', () => {
|
||||
|
||||
test('clicking on info button, then mousing out makes info message still appear', done => {
|
||||
const component = mountWithIntl(
|
||||
<InfoButton
|
||||
message="Here is some info about something!"
|
||||
/>
|
||||
);
|
||||
component.find('div.info-button').simulate('mouseOver');
|
||||
const buttonRef = component.instance().buttonRef;
|
||||
|
||||
// click on info button
|
||||
mockedAddEventListener.mousedown({target: buttonRef});
|
||||
component.update();
|
||||
expect(component.find('div.info-button').exists()).toEqual(true);
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
component.find('div.info-button').simulate('mouseOut');
|
||||
|
||||
// mouseLeave from info button
|
||||
component.find('div.info-button').simulate('mouseLeave');
|
||||
setTimeout(function () { // necessary because mouseover uses debounce
|
||||
component.update();
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
test('clicking on info button, then clicking on it again makes info message go away', () => {
|
||||
const component = mountWithIntl(
|
||||
<InfoButton
|
||||
message="Here is some info about something!"
|
||||
/>
|
||||
);
|
||||
const buttonRef = component.instance().buttonRef;
|
||||
|
||||
// click on info button
|
||||
mockedAddEventListener.mousedown({target: buttonRef});
|
||||
component.update();
|
||||
expect(component.find('div.info-button').exists()).toEqual(true);
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
|
||||
// click on info button again
|
||||
mockedAddEventListener.mousedown({target: buttonRef});
|
||||
component.update();
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(false);
|
||||
});
|
||||
|
||||
test('clicking on info button, then clicking somewhere else', () => {
|
||||
const component = mountWithIntl(
|
||||
<InfoButton
|
||||
message="Here is some info about something!"
|
||||
/>
|
||||
);
|
||||
const buttonRef = component.instance().buttonRef;
|
||||
|
||||
// click on info button
|
||||
mockedAddEventListener.mousedown({target: buttonRef});
|
||||
component.update();
|
||||
expect(component.find('div.info-button').exists()).toEqual(true);
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
|
||||
// click on some other target
|
||||
mockedAddEventListener.mousedown({target: null});
|
||||
component.update();
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(false);
|
||||
});
|
||||
|
||||
test('after message is visible, mouseLeave makes it vanish', () => {
|
||||
const component = mountWithIntl(
|
||||
<InfoButton
|
||||
message="Here is some info about something!"
|
||||
/>
|
||||
);
|
||||
|
||||
// mouseOver info button
|
||||
component.find('div.info-button').simulate('mouseOver');
|
||||
component.update();
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(true);
|
||||
|
||||
// mouseLeave away from info button
|
||||
component.find('div.info-button').simulate('mouseLeave');
|
||||
component.update();
|
||||
expect(component.find('div.info-button-message').exists()).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue