-
Notifications
You must be signed in to change notification settings - Fork 480
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38953 from code-dot-org/hbergam/signin_callout_ui
Hbergam/signin callout UI
- Loading branch information
Showing
4 changed files
with
186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
apps/src/code-studio/components/header/SignInCallout.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import React from 'react'; | ||
import i18n from '@cdo/locale'; | ||
import PropTypes from 'prop-types'; | ||
|
||
const CALLOUT_COLOR = '#454545'; | ||
const TRIANGLE_BASE = 30; | ||
const TRIANGLE_HEIGHT = 15; | ||
const CALLOUT_Z_INDEX = 1040; | ||
const CALLOUT_TOP = 30; | ||
|
||
const styles = { | ||
container: { | ||
// The outermost div is relatively positioned so it can be used as a positional | ||
// anchor for its children (which will be absolutely positioned to avoid affecting | ||
// layout). This element must be 0-sized to avoid affecting layout. | ||
position: 'relative', | ||
height: 0, | ||
width: 0 | ||
}, | ||
content: { | ||
position: 'absolute', | ||
top: CALLOUT_TOP, | ||
right: -90, | ||
zIndex: CALLOUT_Z_INDEX, | ||
backgroundColor: CALLOUT_COLOR, | ||
borderRadius: 3 | ||
}, | ||
modalBackdrop: { | ||
// Most backdrop attributes come from the 'modal-backdrop' class defined by bootstrap | ||
// but we need to override the opacity as the default opacity of 0.8 is too dark. | ||
// Note that bootstrap defaults the z-index of the backdrop to 1040. | ||
opacity: 0.5 | ||
}, | ||
upTriangle: { | ||
position: 'absolute', | ||
top: CALLOUT_TOP - TRIANGLE_HEIGHT, | ||
left: -(TRIANGLE_HEIGHT / 2.0), | ||
width: 0, | ||
height: 0, | ||
borderStyle: 'solid', | ||
borderTopWidth: 0, | ||
borderRightWidth: TRIANGLE_BASE, | ||
borderBottomWidth: TRIANGLE_HEIGHT, | ||
borderLeftWidth: TRIANGLE_BASE, | ||
borderTopColor: 'transparent', | ||
borderRightColor: 'transparent', | ||
borderBottomColor: CALLOUT_COLOR, | ||
borderLeftColor: 'transparent', | ||
zIndex: CALLOUT_Z_INDEX | ||
}, | ||
contentContainer: { | ||
display: 'flex', | ||
padding: 20 | ||
}, | ||
imageContainer: { | ||
width: 100, | ||
marginRight: 20 | ||
}, | ||
textContainer: { | ||
width: 400, | ||
textAlign: 'left', | ||
whiteSpace: 'normal' | ||
}, | ||
textHeader: { | ||
marginTop: 0 | ||
} | ||
}; | ||
|
||
/* | ||
* This is a callout attached to the sign-in button that's used on CSF level | ||
* pages to remind the user to sign-in. Note that the sign-in button is | ||
* defined in shared/haml/user_header.haml and is not a React component. | ||
* This component is injected into the page by src/code-studio/header.js. | ||
*/ | ||
export default class SignInCallout extends React.Component { | ||
static propTypes = { | ||
handleClose: PropTypes.func.isRequired | ||
}; | ||
|
||
constructor(props) { | ||
super(props); | ||
|
||
this.renderContent = this.renderContent.bind(this); | ||
} | ||
|
||
renderContent() { | ||
return ( | ||
<div style={styles.contentContainer}> | ||
<img | ||
style={styles.imageContainer} | ||
src="/shared/images/user-not-signed-in.png" | ||
/> | ||
<div style={styles.textContainer}> | ||
<h2 style={styles.textHeader}>{i18n.notSignedInHeader()}</h2> | ||
<p> {i18n.notSignedInBody()}</p> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
render() { | ||
return ( | ||
<div style={styles.container}> | ||
<div | ||
className="modal-backdrop" | ||
style={styles.modalBackdrop} | ||
onClick={this.props.handleClose} | ||
/> | ||
<div style={styles.upTriangle} /> | ||
<div style={styles.content}>{this.renderContent()}</div> | ||
</div> | ||
); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
apps/src/code-studio/components/header/SignInCalloutWrapper.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React from 'react'; | ||
import cookies from 'js-cookie'; | ||
import SignInCallout from './SignInCallout'; | ||
|
||
const HideSignInCallout = 'hide_signin_callout'; | ||
|
||
// This class hold the display logic for the sign in callout, which prompts | ||
// students to log in in order to save their progress. The callout's freqency | ||
// is regulated here by the use of both session storage and cookies. | ||
export default class SignInCalloutWrapper extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.closeCallout = this.closeCallout.bind(this); | ||
// Before the cookies are set, searching for them will return false, so the | ||
// desired flag should logically read 'true' when set: resulting in the | ||
// 'hide' name. Though this leads to a bit of a double negative in the | ||
// display logic (if not hide: display), it is the clearest option for now. | ||
this.state = { | ||
hideCallout: | ||
// The use of both session storage and cookies is to check for 1 day | ||
// and 1 session, and display the callout again once BOTH have passed. | ||
cookies.get(HideSignInCallout) === 'true' || | ||
sessionStorage.getItem(HideSignInCallout) === 'true' | ||
}; | ||
} | ||
|
||
closeCallout(event) { | ||
this.setState({hideCallout: true}); | ||
cookies.set(HideSignInCallout, 'true', {expires: 1, path: '/'}); | ||
sessionStorage.setItem(HideSignInCallout, 'true'); | ||
event.preventDefault(); | ||
} | ||
|
||
// For readibility: returning an empty div here explicitly if the callout is | ||
// not supposed to be displayed. This avoids using a render statement that | ||
// often returns *nothing. | ||
render() { | ||
if (this.state.hideCallout) { | ||
return null; | ||
} else { | ||
return ( | ||
<div className="uitest-signincallout"> | ||
<SignInCallout handleClose={this.closeCallout} /> | ||
</div> | ||
); | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
apps/test/unit/code-studio/components/header/SignInCalloutTest.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
import SignInCalloutWrapper from '@cdo/apps/code-studio/components/header/SignInCalloutWrapper'; | ||
import {shallow} from 'enzyme'; | ||
import {expect} from '../../../../util/reconfiguredChai'; | ||
import i18n from '@cdo/locale'; | ||
|
||
const wrapper = shallow(<SignInCalloutWrapper />); | ||
|
||
describe('ViewPopup', () => { | ||
it('displays the correct background darkness', () => { | ||
wrapper.setState({hideCallout: false}); | ||
expect(wrapper.html().includes('opacity:0.5')).to.be.true; | ||
}); | ||
|
||
it('shows the correct header', () => { | ||
expect(wrapper.html().includes(i18n.notSignedInHeader())).to.be.true; | ||
}); | ||
|
||
it('shows the correct image', () => { | ||
expect(wrapper.html().includes('user-not-signed-in.png')).to.be.true; | ||
}); | ||
}); |