Unverified Commit 78b32597 authored by Eric Rosenbaum's avatar Eric Rosenbaum Committed by GitHub

Merge branch 'develop' into hotfix/extension-landing-pages

parents 179edefa 7851789f
......@@ -5,3 +5,4 @@ intl/*
locales/*
**/*.min.js
**/node_modules/*
scratch-gui/*
......@@ -18,11 +18,26 @@ env:
- API_HOST_VAR=API_HOST_$TRAVIS_BRANCH
- API_HOST=${!API_HOST_VAR}
- API_HOST=${API_HOST:-$API_HOST_STAGING}
- ASSET_HOST_master=https://assets.scratch.mit.edu
- ASSET_HOST_STAGING=https://assets.scratch.ly
- ASSET_HOST_VAR=ASSET_HOST_$TRAVIS_BRANCH
- ASSET_HOST=${!ASSET_HOST_VAR}
- ASSET_HOST=${ASSET_HOST:-$ASSET_HOST_STAGING}
- BACKPACK_HOST_master=https://backpack.scratch.mit.edu
- BACKPACK_HOST_STAGING=https://backpack.scratch.ly
- BACKPACK_HOST_VAR=BACKPACK_HOST_$TRAVIS_BRANCH
- BACKPACK_HOST=${!BACKPACK_HOST_VAR}
- BACKPACK_HOST=${BACKPACK_HOST:-$BACKPACK_HOST_STAGING}
- ROOT_URL_master=https://scratch.mit.edu
- ROOT_URL_STAGING=https://scratch.ly
- ROOT_URL_VAR=ROOT_URL_$TRAVIS_BRANCH
- ROOT_URL=${!ROOT_URL_VAR}
- ROOT_URL=${ROOT_URL:-$ROOT_URL_STAGING}
- PROJECT_HOST_master=https://projects.scratch.mit.edu
- PROJECT_HOST_STAGING=https://projects.scratch.ly
- PROJECT_HOST_VAR=PROJECT_HOST_$TRAVIS_BRANCH
- PROJECT_HOST=${!PROJECT_HOST_VAR}
- PROJECT_HOST=${PROJECT_HOST:-$PROJECT_HOST_STAGING}
- PATH=$PATH:$PWD/test/integration/node_modules/chromedriver/bin
- AWS_ACCESS_KEY_ID=$EB_AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY=$EB_AWS_SECRET_ACCESS_KEY
......@@ -69,6 +84,7 @@ addons:
install:
- sudo -H pip install -r requirements.txt
- npm --production=false install
- npm --production=false update
jobs:
include:
- stage: test
......@@ -94,8 +110,8 @@ jobs:
- cd test/integration
- npm install
- cd -
script: npm run smoke
script: npm run smoke-sauce
stages:
- test
- name: smoke
if: branch IN (travis) and type != pull_request
if: type != pull_request
......@@ -70,7 +70,7 @@ integration:
smoke:
$(TAP) ./test/integration/smoke-testing/*.js --timeout=3600
smoke-verbose:
$(TAP) ./test/integration/smoke-testing/*.js --timeout=3600 -R spec
......
......@@ -81,14 +81,17 @@ To stop the process that is making the site available to your web browser (creat
`npm start` can be configured with the following environment variables
| Variable | Default | Description |
| ------------- | ----------------------------- | ---------------------------------------------- |
| `API_HOST` | `https://api.scratch.mit.edu` | Hostname for API requests |
| `SENTRY_DSN` | `''` | DSN for Sentry |
| `FALLBACK` | `''` | Pass-through location for old site |
| `GA_TRACKER` | `''` | Where to log Google Analytics data |
| `NODE_ENV` | `null` | If not `production`, app acts like development |
| `PORT` | `8333` | Port for devserver (http://localhost:XXXX) |
| Variable | Default | Description |
| --------------- | ---------------------------------- | ---------------------------------------------- |
| `API_HOST` | `https://api.scratch.mit.edu` | Hostname for API requests |
| `ASSETS_HOST` | `https://assets.scratch.mit.edu` | Hostname for asset requests |
| `BACKPACK_HOST` | `https://backpack.scratch.mit.edu` | Hostname for backpack requests |
| `PROJECTS_HOST` | `https://projects.scratch.mit.edu` | Hostname for project requests |
| `SENTRY_DSN` | `''` | DSN for Sentry |
| `FALLBACK` | `''` | Pass-through location for old site |
| `GA_TRACKER` | `''` | Where to log Google Analytics data |
| `NODE_ENV` | `null` | If not `production`, app acts like development |
| `PORT` | `8333` | Port for devserver (http://localhost:XXXX) |
**NOTE:** Because by default `API_HOST=https://api.scratch.mit.edu`, please be aware that, by default, you will be seeing and interacting with real data on the Scratch website.
......
......@@ -21,7 +21,18 @@ routes.forEach(route => {
app.get(route.pattern, handler(route));
});
app.use(webpackDevMiddleware(compiler));
var middlewareOptions = {};
if (process.env.USE_DOCKER_WATCHOPTIONS) {
middlewareOptions = {
watchOptions: {
aggregateTimeout: 500,
poll: 2500,
ignored: ['node_modules', 'build']
}
};
}
app.use(webpackDevMiddleware(compiler, middlewareOptions));
var proxyHost = process.env.FALLBACK || '';
if (proxyHost !== '') {
......
......@@ -2,7 +2,6 @@ version: '3.4'
volumes:
npm_data:
runtime_data:
intl_data:
networks:
scratch-api_scratch_network:
......@@ -15,7 +14,8 @@ services:
environment:
- API_HOST=http://localhost:8491
- FALLBACK=http://localhost:8080
build:
- USE_DOCKER_WATCHOPTIONS=true
build:
context: ./
dockerfile: Dockerfile
image: scratch-www:latest
......@@ -33,7 +33,6 @@ services:
nocopy: true
- npm_data:/var/app/current/node_modules
- runtime_data:/runtime
- intl_data:/var/app/current/intl
ports:
- "8333:8333"
networks:
......
......@@ -5,6 +5,7 @@ $ui-blue-10percent: hsla(215, 100, 65, .1);
$ui-blue-25percent: hsla(215, 100, 65, .25);
$ui-orange: hsla(38, 100, 55, 1); // #FFAB19 Control Primary
$ui-orange-10percent: hsla(35, 90, 55, .1);
$ui-orange-25percent: hsla(35, 90, 55, .25);
$ui-light-gray: hsla(0, 0, 98, 1); //#FAFAFA
......@@ -13,7 +14,6 @@ $ui-dark-gray: hsla(0, 0, 70, 1); //#B3B3B3
$background-color: hsla(0, 0, 99, 1); //#FDFDFD
/* UI Secondary Colors */
/* 3.0 colors */
/* Using www naming convention for now, should be consistent with gui */
......@@ -27,20 +27,25 @@ $ui-coral-dark: hsla(350, 100, 60, 1); // #FF3355 More Blocks tertiary
$ui-white: hsla(0, 100%, 100%, 1); //#FFF
$ui-white-15percent: hsla(0, 100%, 100%, .15); //#FFF
$ui-light-primary: hsl(215, 100, 95);
$ui-border: hsla(0, 0, 85, 1); //#D9D9D9
/* modals */
$ui-mint-green: hsl(163, 69, 44);
$ui-light-mint: hsl(163, 53, 67);
/* Overlay UI Gray Colors */
$active-gray: hsla(0, 0, 0, .1);
$active-dark-gray: hsla(0, 0, 0, .2);
$box-shadow-gray: hsla(0, 0, 0, .25);
$overlay-gray: hsla(0, 0, 0, .75);
$transparent-light-blue: rgba(229, 240, 254, 0);
/* Typography Colors */
$header-gray: hsla(225, 15, 40, 1); //#575E75
$type-gray: hsla(225, 15, 40, 1); //#575E75
$type-gray-75percent: hsla(225, 15, 40, .75);
$type-white: hsla(0, 100, 100, 1); //#FFF
$link-blue: $ui-blue;
......
......@@ -108,7 +108,7 @@ const Footer = props => (
</a>
</dd>
<dd>
<a href="https://wiki.scratch.mit.edu/">
<a href="https://en.scratch-wiki.info/">
<FormattedMessage id="general.wiki" />
</a>
</dd>
......
......@@ -2,7 +2,7 @@
.char-count {
letter-spacing: 1px;
color: lighten($type-gray, 30%);
color: $type-gray-75percent;
font-weight: 500;
&.overmax {
......
......@@ -2,7 +2,6 @@
@import "../../frameless";
$base-bg: $ui-light-gray;
$pass-bg: lighten($ui-aqua, 35%);
.row {
label {
......@@ -32,8 +31,7 @@ $pass-bg: lighten($ui-aqua, 35%);
}
&.pass {
border: 1px solid $active-dark-gray;
background-color: $pass-bg;
border: 1px solid $ui-aqua;
}
/* IE10/11-specific style resets */
......
......@@ -28,8 +28,8 @@ module.exports.validationHOCFactory = defaultValidationErrors => (Component => {
<Component
validationErrors={defaults(
{},
defaultValidationErrors,
props.validationErrors
props.validationErrors,
defaultValidationErrors
)}
{...omit(props, ['validationErrors'])}
/>
......
const bindAll = require('lodash.bindall');
const PropTypes = require('prop-types');
const React = require('react');
const AddToStudioModalPresentation = require('./presentation.jsx');
class AddToStudioModal extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleRequestClose',
'handleSubmit'
]);
this.state = {
waitingToClose: false
};
}
componentWillUpdate () {
this.closeIfFinishedUpdating();
}
hasOutstandingUpdates () {
return (this.props.studios.some(studio => (studio.hasRequestOutstanding === true)));
}
closeIfFinishedUpdating () {
if (this.state.waitingToClose === true && this.hasOutstandingUpdates() === false) {
this.closeAndStopWaiting();
}
}
// before closing, set waitingToClose to false. That way, if user reopens
// modal, it won't unexpectedly close.
closeAndStopWaiting () {
this.setState({waitingToClose: false}, () => {
this.props.onRequestClose();
});
}
handleRequestClose () {
this.closeAndStopWaiting();
}
handleSubmit () {
this.setState({waitingToClose: true}, () => {
this.closeIfFinishedUpdating();
});
}
render () {
return (
<AddToStudioModalPresentation
isOpen={this.props.isOpen}
studios={this.props.studios}
waitingToClose={this.state.waitingToClose}
onRequestClose={this.handleRequestClose}
onSubmit={this.handleSubmit}
onToggleStudio={this.props.onToggleStudio}
/>
);
}
}
AddToStudioModal.propTypes = {
isOpen: PropTypes.bool,
onRequestClose: PropTypes.func,
onToggleStudio: PropTypes.func,
studios: PropTypes.arrayOf(PropTypes.object)
};
module.exports = AddToStudioModal;
@import "../../../colors";
@import "../../../frameless";
.mod-addToStudio * {
box-sizing: border-box;
}
.mod-addToStudio {
margin: 100px auto;
outline: none;
padding: 0;
width: 36.25rem; /* 580px; */
height: 388px; /* 24.25rem; */
overflow: hidden;
user-select: none;
}
.addToStudio-modal-header {
box-shadow: inset 0 -1px 0 0 $ui-blue-dark;
background-color: $ui-blue;
padding-top: .75rem;
width: 100%;
height: 3rem;
box-sizing: border-box;
}
.addToStudio-content-label {
text-align: center;
color: $type-white;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1rem;
font-weight: bold;
}
.addToStudio-modal-content {
margin: 0 auto;
width: 100%;
font-size: .875rem;
}
.studio-list-outer-scrollbox {
position: relative;
background-color: $ui-blue-10percent;
}
.studio-list-inner-scrollbox {
margin-right: .5rem;
padding-right: .5rem;
height: 16.9375rem;
overflow: scroll;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: $active-dark-gray;
height: 92px;
}
&::-webkit-scrollbar-track {
margin-top: 8px;
margin-bottom: 10px;
}
}
.studio-list-container {
display: flex;
padding: .40625rem 0 0 1.46875rem;
justify-content: flex-start;
flex-flow: row wrap;
}
/* NOTE: force scrolling: add to above:
min-height: 30rem;
*/
.studio-list-bottom-gradient {
position: absolute;
right: 1rem;
bottom: 0;
left: 0;
background: linear-gradient(
$transparent-light-blue,
$ui-light-primary
);
height: 32px;
pointer-events: none; /* pass clicks through to buttons underneath */
}
.studio-selector-button {
display: flex;
position: relative;
margin: .21875rem .21875rem;
border-radius: .5rem;
background-color: $ui-white;
padding: 0;
width: 16.1875rem; /* 259px */
height: 2.5rem;
box-sizing: border-box;
justify-content: space-between;
}
.studio-selector-button-text {
position: absolute;
/* per spec, should be:
margin: .375rem 2.18375rem .375rem .6875rem
but in practice, our css seems to vertically align text to top, where
invision spec aligned to middle.
*/
margin: .575rem 2.18375rem .175rem .6875rem;
width: 13.3125rem;
height: 1rem; /* diff from spec, in case we ever do valign to middle */
line-height: 1.25rem;
font-family: "Helvetica Neue";
font-size: .875rem;
font-weight: regular;
}
.studio-selector-button-selected {
background-color: $ui-mint-green;
color: $ui-white;
}
.studio-selector-button-waiting {
background-color: $ui-light-mint;
color: $ui-white;
}
.studio-selector-button-text-selected {
color: $ui-white;
}
.studio-selector-button-text-unselected {
color: $type-gray;
}
.studio-status-icon {
position: absolute;
margin: .5rem .625rem .5rem 14.0625rem;
border-radius: .75rem;
padding: .0625rem .075rem;
width: 1.5rem;
height: 1.5rem;
color: $ui-white;
box-sizing: border-box;
}
.studio-status-icon-unselected {
background-color: $ui-blue;
}
.submit-button {
background-color: $ui-blue;
}
.submit-button-waiting {
background-color: $ui-blue;
}
.studio-status-icon-plus-img {
width: 1.4rem;
height: 1.4rem;
}
.studio-status-icon--img {
width: 1.4rem;
height: 1.4rem;
}
.action-button-text .spinner-smooth {
margin: .2125rem auto;
width: 1.875rem;
height: 1rem;
}
.studio-status-icon .spinner-smooth {
position: unset; /* don't understand why neither relative nor absolute work */
}
.studio-status-icon .spinner-smooth .circle {
/* overlay spinner on circle */
position: absolute;
margin: .1875rem; /* stay within boundaries of circle */
width: 75%; /* stay within boundaries of circle */
height: 75%; /* stay within boundaries of circle */
}
const PropTypes = require('prop-types');
const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const Modal = require('../base/modal.jsx');
const Form = require('../../forms/form.jsx');
const Button = require('../../forms/button.jsx');
const Spinner = require('../../spinner/spinner.jsx');
const FlexRow = require('../../flex-row/flex-row.jsx');
const StudioButton = require('./studio-button.jsx');
require('../../forms/button.scss');
require('./modal.scss');
const AddToStudioModalPresentation = ({
intl,
isOpen,
studios,
waitingToClose,
onToggleStudio,
onRequestClose,
onSubmit
}) => {
const contentLabel = intl.formatMessage({id: 'addToStudio.title'});
const studioButtons = studios.map(studio => (
<StudioButton
hasRequestOutstanding={studio.hasRequestOutstanding}
id={studio.id}
includesProject={studio.includesProject}
key={studio.id}
title={studio.title}
onToggleStudio={onToggleStudio}
/>
));
return (
<Modal
className="mod-addToStudio"
contentLabel={contentLabel}
isOpen={isOpen}
onRequestClose={onRequestClose}
>
<div>
<div className="addToStudio-modal-header">
<div className="addToStudio-content-label">
{contentLabel}
</div>
</div>
<div className="addToStudio-modal-content">
<div className="studio-list-outer-scrollbox">
<div className="studio-list-inner-scrollbox">
<div className="studio-list-container">
{studioButtons}
</div>
<div className="studio-list-bottom-gradient" />
</div>
</div>
<Form
className="add-to-studio"
onSubmit={onSubmit}
>
<FlexRow className="action-buttons">
<Button
className="action-button close-button white"
key="closeButton"
name="closeButton"
type="button"
onClick={onRequestClose}
>
<div className="action-button-text">
<FormattedMessage id="general.close" />
</div>
</Button>
{waitingToClose ? [
<Button
className="action-button submit-button submit-button-waiting"
disabled="disabled"
key="submitButton"
type="submit"
>
<div className="action-button-text">
<Spinner mode="smooth" />
<FormattedMessage id="addToStudio.finishing" />
</div>
</Button>
] : [
<Button
className="action-button submit-button"
key="submitButton"
type="submit"
>
<div className="action-button-text">
<FormattedMessage id="general.okay" />
</div>
</Button>
]}
</FlexRow>
</Form>
</div>
</div>
</Modal>
);
};
AddToStudioModalPresentation.propTypes = {
intl: intlShape,
isOpen: PropTypes.bool,
onRequestClose: PropTypes.func,
onSubmit: PropTypes.func,
onToggleStudio: PropTypes.func,
studios: PropTypes.arrayOf(PropTypes.object),
waitingToClose: PropTypes.bool
};
module.exports = injectIntl(AddToStudioModalPresentation);
const truncateAtWordBoundary = require('../../../lib/truncate').truncateAtWordBoundary;
const PropTypes = require('prop-types');
const React = require('react');
const classNames = require('classnames');
const Spinner = require('../../spinner/spinner.jsx');
require('./modal.scss');
const StudioButton = ({
hasRequestOutstanding,
id,
includesProject,
title,
onToggleStudio
}) => {
const checkmark = (
<img
alt="checkmark-icon"
className="studio-status-icon-checkmark-img"
src="/svgs/modal/confirm.svg"
/>
);
const plus = (
<img
alt="plus-icon"
className="studio-status-icon-plus-img"
src="/svgs/modal/add.svg"
/>
);
return (
<div
className={classNames(
'studio-selector-button',
{'studio-selector-button-waiting': hasRequestOutstanding},
{'studio-selector-button-selected':
includesProject && !hasRequestOutstanding}
)}
data-id={id}
onClick={onToggleStudio}
>
<div
className={classNames(
'studio-selector-button-text',
{'studio-selector-button-text-selected': includesProject || hasRequestOutstanding},
{'studio-selector-button-text-unselected': !includesProject && !hasRequestOutstanding}
)}
>
{truncateAtWordBoundary(title, 25)}
</div>
<div
className={classNames(
'studio-status-icon',
{'studio-status-icon-unselected': !includesProject}
)}
>
{(hasRequestOutstanding ?
(<Spinner mode="smooth" />) :
(includesProject ? checkmark : plus))}
</div>
</div>
);
};
StudioButton.propTypes = {
hasRequestOutstanding: PropTypes.bool,
id: PropTypes.number,
includesProject: PropTypes.bool,
onToggleStudio: PropTypes.func,
title: PropTypes.string
};
module.exports = StudioButton;
......@@ -5,10 +5,14 @@
position: relative;
margin: 3.75rem auto;
border-radius: 1rem;
box-shadow: 0 0 0 1px $active-gray;
box-shadow: 0 0 0 4px $ui-white-15percent;
background-color: $ui-white;
padding: 0;
width: 48.75rem;
&:focus {
outline: none;
}
}
.modal-overlay {
......@@ -21,10 +25,6 @@
background-color: transparentize($ui-blue, .3);
}
.modal-content:focus {
outline: none;
}
$modal-close-size: 1rem;
.modal-content-close {
position: absolute;
......@@ -59,3 +59,52 @@ $modal-close-size: 1rem;
position: fixed;
}
}
/* Close button, Submit button, etc. */
.action-buttons {
display: flex;
margin: 1.125rem .8275rem .9375rem .8275rem;
line-height: 1.5rem;
justify-content: flex-end !important;
align-items: flex-start;
flex-wrap: nowrap;
}
/* setting overall modal to contain overflow looks good, but isn't
compatible with elements (like validation popups) that need to bleed
past modal boundary. This class can be used to force modal button
row to appear to contain overflow. */
.action-buttons-overflow-fix {
margin-bottom: .9375rem;
}
.action-button {
margin: 0 0 0 .54625rem;
border-radius: .25rem;
padding: 6px 1.25rem 14px 1.25rem;
height: 36px;
}
.action-button.close-button {
border: 1px solid $active-gray;
}
.action-button-text {
display: flex;
}
.action-button.disabled {
background-color: $active-dark-gray;
}
.error-text
{
display: block;
border: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
padding: 1rem;
min-height: 1rem;
overflow: visible;
color: $type-white;
}
This diff is collapsed.
......@@ -9,7 +9,7 @@
margin: 100px auto;
outline: none;
padding: 0;
width: 30rem;
width: 36.25rem; /* 580px; */
user-select: none;
}
......@@ -34,26 +34,45 @@
.report-modal-content {
margin: 1rem auto;
width: 80%;
line-height: 1.5rem;
font-size: .875rem;
.instructions {
line-height: 1.5rem;
}
.received {
margin: 0 auto;
width: 90%;
text-align: center;
line-height: 1.65rem;
.received-header {
font-weight: bold;
}
}
.error-text {
margin-top: .9375rem;
}
.validation-message {
$arrow-border-width: 1rem;
display: block;
position: absolute;
top: 0;
left: 0;
transform: translate(23.5rem, 0);
left: 100%; /* position to the right of parent */
margin-left: $arrow-border-width;
border: 1px solid $active-gray;
border-radius: 5px;
background-color: $ui-orange;
padding: 1rem;
min-width: 12rem;
max-width: 18.75rem;
min-height: 1rem;
overflow: visible;
color: $type-white;
/* arrow on box that points to the left */
&:before {
display: block;
position: absolute;
......@@ -78,3 +97,13 @@
.report-modal-field {
position: relative;
}
.form-group.has-error {
.textarea, select {
border: 1px solid $ui-orange;
}
}
.report-text .textarea {
margin-bottom: 0;
}
......@@ -249,7 +249,7 @@ class Navigation extends React.Component {
'message-count': true,
'show': this.props.unreadMessageCount > 0
})}
>{this.props.unreadMessageCount}</span>
>{this.props.unreadMessageCount} </span>
<FormattedMessage id="general.messages" />
</a>
</li>,
......
......@@ -41,7 +41,7 @@ a.social-messages-profile-link {
color: $type-gray;
&:hover {
color: darken($type-gray, 10);
color: $link-blue;
}
}
......
const range = require('lodash.range');
const PropTypes = require('prop-types');
const React = require('react');
require('./spinner.scss');
// Adapted from http://tobiasahlin.com/spinkit/
const Spinner = () => (
<div className="spinner">
{range(1, 13).map(id => (
<div
className={`circle${id} circle`}
key={`circle${id}`}
/>
))}
</div>
);
const Spinner = ({
mode
}) => {
const spinnerClassName = (mode === 'smooth' ? 'spinner-smooth' : 'spinner');
const spinnerDivCount = (mode === 'smooth' ? 24 : 12);
return (
<div className={spinnerClassName}>
{range(1, spinnerDivCount + 1).map(id => (
<div
className={`circle${id} circle`}
key={`circle${id}`}
/>
))}
</div>
);
};
Spinner.propTypes = {
mode: PropTypes.string
};
module.exports = Spinner;
......@@ -18,13 +18,13 @@
animation: circleFadeDelay 1.2s infinite ease-in-out both;
margin: 0 auto;
border-radius: 100%;
background-color: darken($ui-white, 8%);
background-color: $ui-gray;
width: 15%;
height: 15%;
content: "";
.white & {
background-color: $ui-blue-dark
background-color: $ui-blue-dark;
}
}
}
......@@ -37,7 +37,7 @@
transform: rotate($rotation);
&:before {
animation-delay: $delay;
animation-delay: $delay;
}
}
......@@ -54,5 +54,65 @@
40% {
opacity: 1;
}
}
}
/*********************/
/* type === "smooth" */
/*********************/
.spinner-smooth {
position: relative;
margin: 0 auto;
width: 20px;
height: 20px;
.circle {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
&:before {
display: block;
animation: circleFadeDelaySmooth 1.8s infinite ease-in-out both;
margin: 0 auto;
border-radius: 100%;
background-color: $ui-white;
width: 30%;
height: 20%;
content: "";
.white & {
background-color: darken($ui-blue, 8%);
}
}
}
@for $i from 1 through 24 {
$rotation: 15deg * ($i - 1);
$delay: -1.9s + $i * .075;
.circle#{$i} {
transform: rotate($rotation);
&:before {
animation-delay: $delay;
}
}
}
}
@keyframes circleFadeDelaySmooth {
0%,
35% {
opacity: 0;
},
40% {
opacity: 1;
}
}
......@@ -106,7 +106,7 @@
font-weight: 500;
&:hover {
background-color: lighten($link-blue, 40%);
background-color: $ui-blue-10percent;
}
}
......
......@@ -6,6 +6,7 @@
"general.birthMonth": "Birth Month",
"general.birthYear": "Birth Year",
"general.donate": "Donate",
"general.close": "Close",
"general.collaborators": "Collaborators",
"general.community": "Community",
"general.confirmEmail": "Confirm Email",
......@@ -52,6 +53,7 @@
"general.noDeletionDescription": "Your account was scheduled for deletion but you logged in. Your account has been reactivated. If you didn’t request for your account to be deleted, you should {resetLink} to make sure your account is secure.",
"general.noDeletionLink": "change your password",
"general.notRequired": "Not Required",
"general.okay": "Okay",
"general.other": "Other",
"general.offlineEditor": "Offline Editor",
"general.password": "Password",
......@@ -170,6 +172,7 @@
"registration.welcomeStepTitle": "Hurray! Welcome to Scratch!",
"thumbnail.by": "by",
"report.error": "Something went wrong when trying to send your message. Please try again.",
"report.project": "Report Project",
"report.projectInstructions": "From the dropdown below, please select the reason why you feel this project is disrespectful or inappropriate or otherwise breaks the {CommunityGuidelinesLink}.",
"report.CommunityGuidelinesLinkText": "Scratch Community Guidelines",
......@@ -179,8 +182,11 @@
"report.reasonScary": "Too Violent or Scary",
"report.reasonLanguage": "Inappropriate Language",
"report.reasonMusic": "Inappropriate Music",
"report.reasonMissing": "Please select a reason",
"report.reasonImage": "Inappropriate Images",
"report.reasonPersonal": "Sharing Personal Contact Information",
"report.receivedHeader": "We have received your report!",
"report.receivedBody": "The Scratch Team will review the project based on the Scratch community guidelines.",
"report.promptPlaceholder": "Select a reason why above.",
"report.promptCopy": "Please provide a link to the original project",
"report.promptUncredited": "Please provide links to the uncredited content",
......@@ -192,5 +198,7 @@
"report.promptImage": "Please say the name of the sprite or the backdrop with the inappropriate image",
"report.tooLongError": "That's too long! Please find a way to shorten your text.",
"report.tooShortError": "That's too short. Please describe in detail what's inappropriate or disrespectful about the project.",
"report.send": "Send"
}
\ No newline at end of file
"report.send": "Send",
"report.sending": "Sending...",
"report.textMissing": "Please tell us why you are reporting this project"
}
const lodashTruncate = require('lodash.truncate');
/*
* Function that applies regex for word boundaries, replaces removed string
* with indication of ellipsis (...)
*/
module.exports.truncateAtWordBoundary = (str, length) => (
lodashTruncate(str, {length: length, separator: /[.,:;]*\s+/})
);
......@@ -74,7 +74,7 @@ p {
padding: 1.25em;
&.orange {
background-color: lighten($ui-orange, 30);
background-color: $ui-orange-10percent;
}
}
......@@ -138,7 +138,7 @@ p {
}
::selection {
background-color: lighten($ui-blue, 30);
background-color: $ui-blue-25percent;
}
ol,
......
This diff is collapsed.
......@@ -67,7 +67,7 @@
display: block;
&:hover {
background-color: lighten($ui-blue, 40);
background-color: $ui-blue-25percent;
}
}
}
......
......@@ -27,7 +27,7 @@
}
.conf2017-title-band {
background-color: lighten($ui-blue, 10%);
background-color: $ui-blue-dark;
padding: 1.5rem;
text-align: center;
color: $type-white;
......
......@@ -174,6 +174,14 @@ const Credits = () => (
<span className="name">Tracy Tang</span>
</li>
<li>
<img
alt="Bryce Avatar"
src="//cdn.scratch.mit.edu/get_image/user/2029640_170x170.png"
/>
<span className="name">Bryce Taylor</span>
</li>
<li>
<img
alt="Matthew Avatar"
......@@ -198,6 +206,14 @@ const Credits = () => (
<span className="name">Chris Willis-Ford</span>
</li>
<li>
<img
alt="Kathy Avatar"
src="//cdn.scratch.mit.edu/get_image/user/26779669_170x170.png"
/>
<span className="name">Kathy Wu</span>
</li>
<li>
<img
alt="Julia Avatar"
......
......@@ -54,9 +54,9 @@
}
.admin-message {
border: 1px solid darken($ui-gray, 10);
border: 1px solid $active-dark-gray;
border-radius: 5px;
background-color: lighten($ui-blue, 40);
background-color: $ui-blue-25percent;
padding: 1rem;
}
......
const React = require('react');
const PropTypes = require('prop-types');
const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const Avatar = require('../../../components/avatar/avatar.jsx');
const FormattedRelative = require('react-intl').FormattedRelative;
require('./comment.scss');
const Comment = ({
author,
content,
datetimeCreated,
id
}) => (
<div
className="flex-row comment"
id={`comments-${id}`}
>
<a href={`/users/${author.username}`}>
<Avatar src={author.image} />
</a>
<FlexRow className="comment-body column">
<FlexRow className="comment-top-row">
<a
className="username"
href={`/users/${author.username}`}
>{author.username}</a>
<div className="action-list">
{/* TODO: Hook these up to API calls/logic */}
<span className="comment-delete">Delete</span>
<span className="comment-report">Report</span>
</div>
</FlexRow>
<div className="comment-bubble">
{/* TODO: at the moment, comment content does not properly display
* emojis/easter eggs
* @user links in replies
* links to scratch.mit.edu pages
*/}
<span className="comment-content">{content}</span>
<FlexRow className="comment-bottom-row">
<span className="comment-time">
<FormattedRelative value={new Date(datetimeCreated)} />
</span>
<a
className="comment-reply"
href={`#comments-${id}`}
>
reply
</a>
</FlexRow>
</div>
</FlexRow>
</div>
);
Comment.propTypes = {
author: PropTypes.shape({
id: PropTypes.number,
image: PropTypes.string,
username: PropTypes.string
}),
content: PropTypes.string,
datetimeCreated: PropTypes.string,
id: PropTypes.number
};
module.exports = Comment;
@import "../../../colors";
.compose-comment {
width: 100%;
.textarea-row {
width: 100%;
textarea {
&:not(:focus) {
border: 1px solid $active-dark-gray;
}
}
}
.compose-bottom-row {
width: 100%;
justify-content: space-between;
.compose-post {
margin-right: .5rem;
}
.compose-cancel {
background-color: $ui-dark-gray;
}
.compose-limit {
margin-left: auto;
height: 100%;
font-size: .75rem;
}
.button {
margin-left: 0;
border-radius: .25rem;
}
}
}
.comment-container {
position: relative;
width: 100%;
justify-content: flex-end;
}
.comment {
position: relative;
width: 100%;
flex-wrap: nowrap;
justify-content: space-between;
align-items: flex-start;
.comment-top-row {
margin-bottom: 8px;
width: 100%;
.username {
margin-right: auto;
}
.comment-delete,
.comment-report {
opacity: .5;
font-size: .75rem;
font-weight: 500;
&:before {
display: inline-block;
margin-right: .5rem;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
content: "";
}
}
.comment-delete {
margin-right: 1rem;
&:before {
background-image: url("/svgs/project/delete-gray.svg");
width: 1rem;
height: 1rem;
vertical-align: -.25rem;
}
}
.comment-report {
&:before {
margin-right: .25rem;
background-image: url("/svgs/project/report-gray.svg");
width: .75rem;
height: .75rem;
vertical-align: -.125rem;
}
}
}
.avatar {
margin-right: .5rem;
}
.comment-body {
margin-bottom: 1.5rem;
min-width: 50%;
flex-grow: 1;
align-items: flex-start;
.comment-bubble {
position: relative;
margin-left: .5rem;
border: 1px solid $active-gray;
border-radius: 0 .5rem .5rem .5rem;
background-color: $ui-white;
padding: .75rem;
width: calc(100% - .5rem);
box-sizing: border-box;
&:before {
display: inline-block;
position: absolute;
top: -1px;
left: -11px; // width + 1px
border-width: 1px 0 1px 1px;
border-style: solid;
border-radius: 0 0 0 8px;
border-color: $active-gray transparent $active-gray $active-gray;
background: $ui-white;
width: 10px;
height: 9px;
content: "";
}
}
.comment-content {
overflow-wrap: break-word;
}
.comment-bottom-row {
padding-top: 1rem;
font-size: .75rem;
justify-content: space-between;
.comment-time {
color: $ui-dark-gray;
}
.comment-reply {
display: inline-flex;
&:after {
margin-left: .25rem;
background-image: url("/svgs/project/comment-reply.svg");
background-size: cover;
width: 1rem;
height: 1rem;
content: "";
}
}
}
}
}
.replies {
width: calc(100% - 4rem);
&.collapsed .comment {
&:last-child {
&:after {
position: absolute;
bottom: 0;
background: linear-gradient(
transparent,
$ui-light-primary
);
width: 100%;
height: 100%;
content: "";
pointer-events: none;
}
}
}
}
.expand-thread {
margin-bottom: 24px;
width: 100%;
overflow: hidden;
text-align: center;
&:before,
&:after {
display: inline-block;
position: relative;
background-color: $active-gray;
width: 50%;
height: 2px;
vertical-align: middle;
content: "";
}
&:before {
right: .5em;
margin-left: -50%;
}
&:after {
left: .5em;
margin-right: -50%;
}
}
const React = require('react');
const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const InplaceInput = require('../../../components/forms/inplace-input.jsx');
const Button = require('../../../components/forms/button.jsx');
require('./comment.scss');
const onUpdate = update => update;
const ComposeComment = () => (
<FlexRow className="compose-comment column">
<InplaceInput
handleUpdate={onUpdate}
name="compose-comment"
type="textarea"
/>
<FlexRow className="compose-bottom-row">
<Button className="compose-post">Post</Button>
<Button className="compose-cancel">Cancel</Button>
<span className="compose-limit">500 characters left</span>
</FlexRow>
</FlexRow>
);
module.exports = ComposeComment;
const React = require('react');
const PropTypes = require('prop-types');
const bindAll = require('lodash.bindall');
const classNames = require('classnames');
const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const Comment = require('./comment.jsx');
require('./comment.scss');
class TopLevelComment extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleExpandThread'
]);
this.state = {
expanded: false
};
}
handleExpandThread () {
this.setState({
expanded: true
});
}
render () {
const {
author,
content,
datetimeCreated,
id,
replies
} = this.props;
return (
<FlexRow className="comment-container">
<Comment {...{author, content, datetimeCreated, id}} />
{replies.length > 0 &&
<FlexRow
className={classNames(
'replies',
'column',
{collapsed: !this.state.expanded && replies.length > 3}
)}
key={id}
>
{(this.state.expanded ? replies : replies.slice(0, 3)).map(reply => (
<Comment
author={reply.author}
content={reply.content}
datetimeCreated={reply.datetime_created}
id={reply.id}
key={reply.id}
/>
))}
</FlexRow>
}
{!this.state.expanded && replies.length > 3 &&
<a
className="expand-thread"
onClick={this.handleExpandThread}
>See all {replies.length} replies</a>
}
</FlexRow>
);
}
}
TopLevelComment.propTypes = {
author: PropTypes.shape({
id: PropTypes.number,
image: PropTypes.string,
username: PropTypes.string
}),
content: PropTypes.string,
datetimeCreated: PropTypes.string,
id: PropTypes.number,
parentId: PropTypes.number,
projectId: PropTypes.string,
replies: PropTypes.arrayOf(PropTypes.object)
};
module.exports = TopLevelComment;
{
"addToStudio.title": "Add to Studio",
"addToStudio.finishing": "Finishing up...",
"preview.musicExtensionChip": "Music",
"preview.penExtensionChip": "Pen",
"preview.speechExtensionChip": "Google Speech",
"preview.translateExtensionChip": "Google Translate",
"preview.videoMotionChip": "Video Motion"
}
\ No newline at end of file
}
This diff is collapsed.
This diff is collapsed.
@import "../../colors";
@import "../../frameless";
/* stage size constants
* this is a hack right now - stage includes padding of .5rem (8px) for alignment in gui
* in www the player is placed with margin -.5rem to align the edge.
* the height is calculated from the actual height on the page (404)
*/
$gui-width: 496px;
/* stage size constants */
$player-width: 482px;
$player-height: 406px;
$stage-width: 480px;
$stage-height: 404px;
/* override view padding for share banner */
#view {
padding: 0 0 20px 0;
padding: 0;
}
.gui {
......@@ -29,34 +25,34 @@ $stage-height: 404px;
.project-title {
font-size: 1.75rem;
font-weight: 500;
&.has-error {
.validation-message {
transform: translate(22rem, 0);
}
}
}
.project-header {
margin-right: 2rem;
flex-grow: 1;
justify-content: flex-start;
align-items: flex-start;
.inplace-input {
height: calc(3rem - 4px);
}
}
img {
&.avatar {
border: 0;
border-radius: 5px;
width: 3rem;
height: 3rem;
&.remix {
margin-right: .5em;
width: 2rem;
......@@ -64,14 +60,14 @@ $stage-height: 404px;
}
}
}
.title {
margin-left: 1rem;
text-align: left;
font-size: .8rem;
flex-grow: 1;
}
.validation-message {
$arrow-border-width: 1rem;
display: block;
......@@ -108,8 +104,8 @@ $stage-height: 404px;
content: "";
}
}
.row {
.row {
&.has-error {
.inplace-input,
......@@ -122,21 +118,21 @@ $stage-height: 404px;
position: relative;
}
}
.button {
margin-left: 1rem;
}
.comments-container {
width: 60%;
}
.remix-button,
.see-inside-button {
margin-top: 0;
font-size: .875rem;
font-weight: normal;
&:before {
display: inline-block;
margin-right: .5rem;
......@@ -152,19 +148,19 @@ $stage-height: 404px;
.remix-button {
background-color: $ui-aqua;
&:before {
background-image: url("/svgs/project/remix-white.svg");
}
}
.see-inside-button {
&:before {
background-image: url("/svgs/project/see-inside-white.svg");
}
}
.preview-row {
margin-top: 1rem;
justify-content: space-between;
......@@ -174,14 +170,13 @@ $stage-height: 404px;
.guiPlayer {
display: inline-block;
margin-left: -.5rem;
width: $gui-width;
width: $player-width;
}
.project-notes {
// not 1.5rem because of stage padding
margin-left: 1rem;
height: $stage-height;
height: $player-height;
align-items: flex-start;
flex: 1;
flex-flow: column;
......@@ -194,7 +189,7 @@ $stage-height: 404px;
color: $type-gray;
font-size: .875rem;
}
.subactions {
margin-left: 1.5rem;
justify-content: flex-end;
......@@ -216,7 +211,7 @@ $stage-height: 404px;
font-size: .875rem;
flex-shrink: 1;
}
.description-block {
display: flex;
width: 100%;
......@@ -225,7 +220,7 @@ $stage-height: 404px;
align-items: flex-start;
flex: 1;
}
.project-textlabel {
margin: 0 0 .5rem 0;
font-size: 1rem;
......@@ -240,12 +235,12 @@ $stage-height: 404px;
padding: .5rem;
width: calc(100% - (1rem + 2px));
overflow: auto;
white-space: pre-line;
white-space: pre-line;
font-size: 1rem;
// flex-grow
flex: 1;
}
.project-description.last {
margin-bottom: 0;
}
......@@ -261,29 +256,29 @@ $stage-height: 404px;
white-space: pre-line;
// flex-grow
flex: 1;
&.last {
margin-bottom: 0;
}
&.textarea-row {
border: 0;
background-color: inherit;
padding: 0;
}
&.has-error {
.validation-message {
transform: translate(26rem, 0);
}
}
& > .grow {
display: flex;
flex: 1;
}
}
.copyleft {
display: inline-block;
transform: scale(-1, 1);
......@@ -320,9 +315,9 @@ $stage-height: 404px;
}
.project-loves {
cursor: pointer;
&:before {
opacity: .5;
background-image: url("/svgs/project/love-gray.svg");
......@@ -330,7 +325,7 @@ $stage-height: 404px;
}
.project-loves.loved {
&:before {
opacity: 1;
background-image: url("/svgs/project/love-red.svg");
......@@ -338,9 +333,9 @@ $stage-height: 404px;
}
.project-favorites {
cursor: pointer;
&:before {
opacity: .5;
background-image: url("/svgs/project/fav-gray.svg");
......@@ -348,7 +343,7 @@ $stage-height: 404px;
}
.project-favorites.favorited {
&:before {
opacity: 1;
background-image: url("/svgs/project/fav-yellow.svg");
......@@ -356,7 +351,7 @@ $stage-height: 404px;
}
.project-remixes {
&:before {
opacity: .5;
background-image: url("/svgs/project/remix-gray.svg");
......@@ -364,7 +359,7 @@ $stage-height: 404px;
}
.project-views {
&:before {
opacity: .5;
background-image: url("/svgs/project/views-gray.svg");
......@@ -396,24 +391,24 @@ $stage-height: 404px;
// border-color: transparent;
// background-color: $active-gray;
// }
//
//
// &:active {
// border: 0 solid transparent;
// box-shadow: inset 0 0 5px $box-shadow-gray;
// background-color: $active-dark-gray;
// padding: calc(.75em + 1px) calc(1.5em + 1px);
// }
//
//
// &.report {
// border: 1px solid $ui-coral;
// background-color: $ui-coral;
//
//
// &:hover {
// transition: background-color .25s ease;
// border-color: transparent;
// background-color: $active-gray;
// }
//
//
// &:active {
// border: 0 solid transparent;
// box-shadow: inset 0 0 5px $box-shadow-gray;
......@@ -422,7 +417,16 @@ $stage-height: 404px;
// }
// }
}
.comments-header {
padding: 0 0 1rem 0;
justify-content: space-between;
h4 {
font-size: 1.25rem;
}
}
.studio-button,
.copy-link-button,
.report-button {
......@@ -452,29 +456,45 @@ $stage-height: 404px;
.report-button {
background-color: $ui-coral;
&:before {
background-image: url("/svgs/project/report-white.svg");
}
}
.project-lower-container {
margin-top: 1rem;
background-color: $ui-blue-10percent;
padding: 1rem 0;
min-height: 12rem;
}
.create-comment {
margin-bottom: 2rem;
}
.load-more-button {
margin-left: 0;
width: 100%;
}
.extension-list {
justify-content: flex-start;
}
.remix-list,
.studio-list {
flex-direction: column;
.project {
margin-bottom: 1.5rem;
}
.creator-image img {
max-width: 2rem;
max-height: 2rem;
}
.thumbnail-column {
display: inline-block;
width: 100%;
......
{
"search.trending": "Trending",
"search.popular": "Popular"
}
......@@ -8,14 +8,18 @@ const React = require('react');
const api = require('../../lib/api');
const Button = require('../../components/forms/button.jsx');
const Form = require('../../components/forms/form.jsx');
const Grid = require('../../components/grid/grid.jsx');
const navigationActions = require('../../redux/navigation.js');
const Select = require('../../components/forms/select.jsx');
const TitleBanner = require('../../components/title-banner/title-banner.jsx');
const Tabs = require('../../components/tabs/tabs.jsx');
const Page = require('../../components/page/www/page.jsx');
const render = require('../../lib/render.jsx');
const ACCEPTABLE_MODES = ['trending', 'popular', ''];
require('./search.scss');
class Search extends React.Component {
......@@ -23,14 +27,31 @@ class Search extends React.Component {
super(props);
bindAll(this, [
'getSearchState',
'handleChangeSortMode',
'handleGetSearchMore',
'getTab'
]);
this.state = this.getSearchState();
this.state.loaded = [];
this.state.loadNumber = 16;
this.state.mode = '';
this.state.offset = 0;
this.state.loadMore = false;
let mode = '';
const query = window.location.search;
const m = query.lastIndexOf('mode=');
if (m !== -1) {
mode = query.substring(m + 5, query.length).toLowerCase();
}
while (mode.indexOf('/') > -1) {
mode = mode.substring(0, mode.indexOf('/'));
}
while (mode.indexOf('&') > -1) {
mode = mode.substring(0, mode.indexOf('&'));
}
mode = decodeURIComponent(mode.split('+').join(' '));
this.state.mode = mode;
}
componentDidMount () {
const query = window.location.search;
......@@ -65,6 +86,13 @@ class Search extends React.Component {
loadNumber: 16
};
}
handleChangeSortMode (name, value) {
if (ACCEPTABLE_MODES.indexOf(value) !== -1) {
const term = this.props.searchTerm.split(' ').join('+');
window.location =
`${window.location.origin}/search/${this.state.tab}?q=${term}&mode=${value}`;
}
}
handleGetSearchMore () {
let termText = '';
if (this.props.searchTerm !== '') {
......@@ -73,7 +101,8 @@ class Search extends React.Component {
const locale = this.props.intl.locale;
const loadNumber = this.state.loadNumber;
const offset = this.state.offset;
const queryString = `limit=${loadNumber}&offset=${offset}&language=${locale}&mode=popular${termText}`;
const mode = this.state.mode;
const queryString = `limit=${loadNumber}&offset=${offset}&language=${locale}&mode=${mode}${termText}`;
api({
uri: `/search/${this.state.tab}?${queryString}`
......@@ -167,6 +196,25 @@ class Search extends React.Component {
{this.getTab('projects')}
{this.getTab('studios')}
</Tabs>
<div className="sort-controls">
<Form className="sort-mode">
<Select
name="sort"
options={[
{
value: 'trending',
label: this.props.intl.formatMessage({id: 'search.trending'})
},
{
value: 'popular',
label: this.props.intl.formatMessage({id: 'search.popular'})
}
]}
value={this.state.mode}
onChange={this.handleChangeSortMode}
/>
</Form>
</div>
{this.getProjectBox()}
</div>
</div>
......
......@@ -104,15 +104,40 @@ $base-bg: $ui-white;
}
}
}
/* HACK: sort controls are terrible. There's some sort of magic formula for height of formsy components that I can't control. */
.select {
select {
margin-bottom: 0;
color: $header-gray;
}
.sort-controls {
display: flex;
margin: 0 auto;
border-bottom: 1px solid $ui-border;
padding: 8px 0;
width: 58.75rem;
justify-content: space-between;
}
.sort-mode {
margin-top: -4px;
width: 13.75rem;
.select {
select {
margin-bottom: 0;
border: 0;
background-color: transparent;
height: 32px;
color: $header-gray;
.help-block {
display: none;
&:focus,
&:active {
background-color: transparent;
}
}
.help-block {
display: none;
}
}
}
......
......@@ -466,7 +466,7 @@ const Terms = () => (
<p>
This document, together with all appendices, constitutes the entire Terms
of Use and supersedes all previous agreements with the Scratch Team relating
to the use of Scratch. Revision date: 4 March 2015.
to the use of Scratch. Revision date: April 2016.
</p>
</section>
<section id="appendix-a">
......
......@@ -31,25 +31,24 @@ $base-bg: $ui-white;
max-width: calc(100% - 2rem);
}
$darken-button: rgba(0, 0, 0, .1);
.tips-button {
margin-right: .75rem;
background-color: $ui-blue;
color: $ui-white;
font-size: 1rem;
&.getting-started-button {
margin-right: 0;
background-color: $darken-button;
background-color: $ui-white;
color: $link-blue;
}
img {
margin-right: 1rem;
height: 1.25rem;
vertical-align: middle;
}
a {
color: $ui-white;
}
......@@ -95,26 +94,26 @@ img.tips-icon {
}
}
}
.ttt-head {
p {
max-width: $cols4;
}
}
//put the image first if in 4-column
.tips-info-body {
max-width: $cols4;
text-align: center;
&.tips-illustration {
order: -1;
img {
width: $cols4;
}
}
.button {
width: 100%;
}
......@@ -131,13 +130,13 @@ img.tips-icon {
}
}
}
.ttt-head {
p {
max-width: $cols6;
}
}
.tips-info-body.tips-illustration {
order: -1;
img {
......@@ -163,27 +162,27 @@ img.tips-icon {
}
}
}
.ttt-head {
p {
max-width: $cols6;
}
}
.tips-info-section {
&.mod-align-top {
align-items: flex-start;
}
}
.tips-info-body {
max-width: $cols4;
}
.tips-button {
width: 100%;
}
img.mod-flow-left {
transform: translate(-1*$cols2);
}
......@@ -200,19 +199,19 @@ img.tips-icon {
}
}
}
.ttt-head {
p {
max-width: $cols8;
}
}
.tips-info-section {
&.mod-align-top {
align-items: flex-start;
}
}
.tips-info-body {
max-width: $cols6;
&.mod-narrow {
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="27px" height="28px" viewBox="0 0 27 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>blocks icon</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="12-col" transform="translate(-496.000000, -285.000000)" fill="#FFFFFF">
<g id="Band---Masthead" transform="translate(0.000000, 52.000000)">
<g id="buttons-v1" transform="translate(474.000000, 214.000000)">
<g id="guide-tutorial">
<g id="blocks-icon" transform="translate(22.000000, 19.000000)">
<path d="M0.850455083,0.114928725 L2.97546016,0.114928725 C3.19321976,0.114928725 3.40235116,0.203530037 3.55642635,0.360996823 L4.71835875,1.54850635 C4.87243394,1.70597313 5.08156534,1.79457444 5.29973581,1.79457444 L7.90545549,1.79457444 C8.12362597,1.79457444 8.33275736,1.70597313 8.48683256,1.54850635 L9.64876495,0.360996823 C9.80284014,0.203530037 10.0119715,0.114928725 10.230142,0.114928725 L25.5024861,0.114928725 C25.9560835,0.114928725 26.3242205,0.491169366 26.3242205,0.954751584 L26.3242205,5.99368874 C26.3242205,6.45727096 25.9560835,6.8335116 25.5024861,6.8335116 L10.230142,6.8335116 C10.0119715,6.8335116 9.80284014,6.92211291 9.64876495,7.0795797 L8.48683256,8.26708922 C8.33275736,8.42455601 8.12362597,8.51315732 7.90545549,8.51315732 L5.29973581,8.51315732 C5.08156534,8.51315732 4.87243394,8.42455601 4.71835875,8.26708922 L3.55642635,7.0795797 C3.40235116,6.92211291 3.19321976,6.8335116 2.97546016,6.8335116 L0.850455083,6.8335116 C0.396857712,6.8335116 0.028720716,6.45727096 0.028720716,5.99368874 L0.028720716,0.954751584 C0.028720716,0.491169366 0.396857712,0.114928725 0.850455083,0.114928725 Z" id="Stroke-1"></path>
<path d="M0.850455083,9.8008857 L2.97546016,9.8008857 C3.19321976,9.8008857 3.40235116,9.88948701 3.55642635,10.0469538 L4.71835875,11.2344633 C4.87243394,11.3919301 5.08156534,11.4805314 5.29973581,11.4805314 L7.90545549,11.4805314 C8.12362597,11.4805314 8.33275736,11.3919301 8.48683256,11.2344633 L9.64876495,10.0469538 C9.80284014,9.88948701 10.0119715,9.8008857 10.230142,9.8008857 L19.7503455,9.8008857 C20.2039429,9.8008857 20.5720799,10.1771263 20.5720799,10.6407086 L20.5720799,15.6796457 C20.5720799,16.1432279 20.2039429,16.5194686 19.7503455,16.5194686 L10.230142,16.5194686 C10.0119715,16.5194686 9.80284014,16.6080699 9.64876495,16.7655367 L8.48683256,17.9530462 C8.33275736,18.110513 8.12362597,18.1991143 7.90545549,18.1991143 L5.29973581,18.1991143 C5.08156534,18.1991143 4.87243394,18.110513 4.71835875,17.9530462 L3.55642635,16.7655367 C3.40235116,16.6080699 3.19321976,16.5194686 2.97546016,16.5194686 L0.850455083,16.5194686 C0.396857712,16.5194686 0.028720716,16.1432279 0.028720716,15.6796457 L0.028720716,10.6407086 C0.028720716,10.1771263 0.396857712,9.8008857 0.850455083,9.8008857 Z" id="Stroke-3"></path>
<path d="M0.850455083,19.4868427 L2.97546016,19.4868427 C3.19321976,19.4868427 3.40235116,19.575444 3.55642635,19.7329108 L4.71835875,20.9204203 C4.87243394,21.0778871 5.08156534,21.1664884 5.29973581,21.1664884 L7.90545549,21.1664884 C8.12362597,21.1664884 8.33275736,21.0778871 8.48683256,20.9204203 L9.64876495,19.7329108 C9.80284014,19.575444 10.0119715,19.4868427 10.230142,19.4868427 L23.037283,19.4868427 C23.4908804,19.4868427 23.8590174,19.8630833 23.8590174,20.3266655 L23.8590174,25.3656027 C23.8590174,25.8291849 23.4908804,26.2054256 23.037283,26.2054256 L10.230142,26.2054256 C10.0119715,26.2054256 9.80284014,26.2940269 9.64876495,26.4514937 L8.48683256,27.6390032 C8.33275736,27.79647 8.12362597,27.8850713 7.90545549,27.8850713 L5.29973581,27.8850713 C5.08156534,27.8850713 4.87243394,27.79647 4.71835875,27.6390032 L3.55642635,26.4514937 C3.40235116,26.2940269 3.19321976,26.2054256 2.97546016,26.2054256 L0.850455083,26.2054256 C0.396857712,26.2054256 0.028720716,25.8291849 0.028720716,25.3656027 L0.028720716,20.3266655 C0.028720716,19.8630833 0.396857712,19.4868427 0.850455083,19.4868427 Z" id="Stroke-5"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="27px" height="28px" viewBox="0 0 27 28" style="enable-background:new 0 0 27 28;" xml:space="preserve">
<style type="text/css">
.st0{fill:#4C97FF;}
</style>
<title>blocks icon</title>
<desc>Created with Sketch.</desc>
<g id="Page-1">
<g id="_x31_2-col" transform="translate(-496.000000, -285.000000)">
<g id="Band---Masthead" transform="translate(0.000000, 52.000000)">
<g id="buttons-v1" transform="translate(474.000000, 214.000000)">
<g id="guide-tutorial">
<g id="blocks-icon" transform="translate(22.000000, 19.000000)">
<path id="Stroke-1" class="st0" d="M0.9,0.1H3c0.2,0,0.4,0.1,0.6,0.2l1.2,1.2c0.2,0.2,0.4,0.2,0.6,0.2h2.6
c0.2,0,0.4-0.1,0.6-0.2l1.2-1.2c0.2-0.2,0.4-0.2,0.6-0.2h15.3c0.5,0,0.8,0.4,0.8,0.8v5c0,0.5-0.4,0.8-0.8,0.8H10.2
c-0.2,0-0.4,0.1-0.6,0.2L8.5,8.3C8.3,8.4,8.1,8.5,7.9,8.5H5.3c-0.2,0-0.4-0.1-0.6-0.2L3.6,7.1C3.4,6.9,3.2,6.8,3,6.8H0.9
C0.4,6.8,0,6.5,0,6V1C0,0.5,0.4,0.1,0.9,0.1z"/>
<path id="Stroke-3" class="st0" d="M0.9,9.8H3c0.2,0,0.4,0.1,0.6,0.2l1.2,1.2c0.2,0.2,0.4,0.2,0.6,0.2h2.6
c0.2,0,0.4-0.1,0.6-0.2L9.6,10c0.2-0.2,0.4-0.2,0.6-0.2h9.5c0.5,0,0.8,0.4,0.8,0.8v5c0,0.5-0.4,0.8-0.8,0.8h-9.5
c-0.2,0-0.4,0.1-0.6,0.2L8.5,18c-0.2,0.2-0.4,0.2-0.6,0.2H5.3c-0.2,0-0.4-0.1-0.6-0.2l-1.2-1.2c-0.2-0.2-0.4-0.2-0.6-0.2H0.9
c-0.5,0-0.8-0.4-0.8-0.8v-5C0,10.2,0.4,9.8,0.9,9.8z"/>
<path id="Stroke-5" class="st0" d="M0.9,19.5H3c0.2,0,0.4,0.1,0.6,0.2l1.2,1.2c0.2,0.2,0.4,0.2,0.6,0.2h2.6
c0.2,0,0.4-0.1,0.6-0.2l1.2-1.2c0.2-0.2,0.4-0.2,0.6-0.2H23c0.5,0,0.8,0.4,0.8,0.8v5c0,0.5-0.4,0.8-0.8,0.8H10.2
c-0.2,0-0.4,0.1-0.6,0.2l-1.2,1.2c-0.2,0.2-0.4,0.2-0.6,0.2H5.3c-0.2,0-0.4-0.1-0.6-0.2l-1.2-1.2c-0.2-0.2-0.4-0.2-0.6-0.2
H0.9c-0.5,0-0.8-0.4-0.8-0.8v-5C0,19.9,0.4,19.5,0.9,19.5z"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M8.75 11.25h-2.5a1.25 1.25 0 1 1 0-2.5h2.5v-2.5a1.25 1.25 0 0 1 2.5 0v2.5h2.5a1.25 1.25 0 1 1 0 2.5h-2.5v2.5a1.249 1.249 0 1 1-2.5 0v-2.5z" fill="#FFF" fill-rule="evenodd"/></svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<g>
<g>
<path class="st0" d="M12.5,10.3c0.6,0.6,0.6,1.5,0,2.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4L8,10.1l-2.3,2.3
c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.6-0.6-0.6-1.5,0-2.1L5.9,8L3.5,5.7C3,5.1,3,4.1,3.5,3.5
C4.1,3,5.1,3,5.7,3.5L8,5.9l2.3-2.3c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L10.1,8L12.5,10.3z"/>
</g>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M12.5 10.3c.6.6.6 1.5 0 2.1-.3.3-.7.4-1.1.4-.4 0-.8-.1-1.1-.4L8 10.1l-2.3 2.3c-.3.3-.7.4-1.1.4-.4 0-.8-.1-1.1-.4-.6-.6-.6-1.5 0-2.1L5.9 8 3.5 5.7c-.5-.6-.5-1.6 0-2.2.6-.5 1.6-.5 2.2 0L8 5.9l2.3-2.3c.6-.6 1.5-.6 2.1 0 .6.6.6 1.5 0 2.1L10.1 8l2.4 2.3z" fill="#fff"/></svg>
\ No newline at end of file
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path stroke="#FFF" stroke-width="2.5" d="M6 10l3 3 6-6" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"/></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>open-blue</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="open-blue" fill="#4C97FF">
<path d="M16.904265,9.16353035 L15.4298099,7.69520276 L10.2383662,11.6050888 C9.64794591,12.057779 8.79050666,11.9379804 8.33625534,11.3315664 C7.9692373,10.844951 7.97668404,10.1706865 8.33625534,9.70951497 L12.2606889,4.53591307 L10.8213398,3.10151073 C10.4107052,2.6922873 10.7011281,2.00848131 11.2670806,2.00848131 L17.3585163,2 C17.709577,2.00848131 18,2.29896634 18,2.64033925 L18,8.71932149 C18,9.28332892 17.3063891,9.56427246 16.904265,9.16353035 Z M15.3807332,18 L3.03722491,18 C2.46488952,18 2,17.5367082 2,16.9652796 L2,4.66419295 C2,4.09382454 2.46488952,3.62947257 3.03722491,3.62947257 L7.82335296,3.62947257 C8.39781598,3.62947257 8.86164168,4.09382454 8.86164168,4.66419295 C8.86164168,5.23562152 8.39781598,5.69891333 7.82335296,5.69891333 L4.07657745,5.69891333 L4.07657745,15.9305592 L14.3424445,15.9305592 L14.3424445,13.2101776 C14.3424445,12.638749 14.807334,12.1754572 15.3807332,12.1754572 C15.9541324,12.1754572 16.4200857,12.638749 16.4200857,13.2101776 L16.4200857,16.9652796 C16.4200857,17.5367082 15.9541324,18 15.3807332,18 Z" id="open-white"></path>
</g>
</g>
</svg>
\ No newline at end of file
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M16.904 9.164L15.43 7.695l-5.192 3.91a1.353 1.353 0 0 1-1.902-.273 1.343 1.343 0 0 1 0-1.622l3.925-5.174-1.44-1.434a.637.637 0 0 1 .446-1.094L17.36 2a.657.657 0 0 1 .641.64v6.08c0 .563-.694.844-1.096.444zM15.381 18H3.037A1.036 1.036 0 0 1 2 16.965v-12.3c0-.571.465-1.036 1.037-1.036h4.786a1.036 1.036 0 0 1 0 2.07H4.077V15.93h10.265v-2.72c0-.572.465-1.036 1.039-1.036.573 0 1.04.464 1.04 1.035v3.755c0 .572-.467 1.035-1.04 1.035z" fill="#4C97FF" fill-rule="evenodd"/></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>open-modal-icon</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="open-modal-icon" fill="#FFFFFF">
<path d="M16.904265,9.16353035 L15.4298099,7.69520276 L10.2383662,11.6050888 C9.64794591,12.057779 8.79050666,11.9379804 8.33625534,11.3315664 C7.9692373,10.844951 7.97668404,10.1706865 8.33625534,9.70951497 L12.2606889,4.53591307 L10.8213398,3.10151073 C10.4107052,2.6922873 10.7011281,2.00848131 11.2670806,2.00848131 L17.3585163,2 C17.709577,2.00848131 18,2.29896634 18,2.64033925 L18,8.71932149 C18,9.28332892 17.3063891,9.56427246 16.904265,9.16353035 Z M15.3807332,18 L3.03722491,18 C2.46488952,18 2,17.5367082 2,16.9652796 L2,4.66419295 C2,4.09382454 2.46488952,3.62947257 3.03722491,3.62947257 L7.82335296,3.62947257 C8.39781598,3.62947257 8.86164168,4.09382454 8.86164168,4.66419295 C8.86164168,5.23562152 8.39781598,5.69891333 7.82335296,5.69891333 L4.07657745,5.69891333 L4.07657745,15.9305592 L14.3424445,15.9305592 L14.3424445,13.2101776 C14.3424445,12.638749 14.807334,12.1754572 15.3807332,12.1754572 C15.9541324,12.1754572 16.4200857,12.638749 16.4200857,13.2101776 L16.4200857,16.9652796 C16.4200857,17.5367082 15.9541324,18 15.3807332,18 Z"></path>
</g>
</g>
</svg>
\ No newline at end of file
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M16.904 9.164L15.43 7.695l-5.192 3.91a1.353 1.353 0 0 1-1.902-.273 1.343 1.343 0 0 1 0-1.622l3.925-5.174-1.44-1.434a.637.637 0 0 1 .446-1.094L17.36 2a.657.657 0 0 1 .641.64v6.08c0 .563-.694.844-1.096.444zM15.381 18H3.037A1.036 1.036 0 0 1 2 16.965v-12.3c0-.571.465-1.036 1.037-1.036h4.786a1.036 1.036 0 0 1 0 2.07H4.077V15.93h10.265v-2.72c0-.572.465-1.036 1.039-1.036.573 0 1.04.464 1.04 1.035v3.755c0 .572-.467 1.035-1.04 1.035z" fill="#FFF" fill-rule="evenodd"/></svg>
\ No newline at end of file
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M15 10a5 5 0 1 0-5 5" stroke="#FFF" stroke-width="2.5" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"/></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch -->
<title>comment-reply</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="comment-reply" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M9.4379644,9.95564876 C9.4397719,9.83000629 9.4365177,9.69150012 9.42535762,9.54182983 C9.39320024,9.16970313 9.30581606,8.72820912 9.12405695,8.26937328 C9.07512181,8.1566514 9.02758481,8.03887149 8.96746449,7.92470447 C8.89406178,7.78886016 8.89825622,7.75634423 8.7905989,7.59665491 C8.62352034,7.33435977 8.47531676,7.14143195 8.29006229,6.92104623 C7.92234964,6.50701011 7.47144725,6.15222524 6.99467914,5.89065268 C6.51441566,5.63486073 6.01387905,5.47228111 5.5671711,5.38846228 C5.12465758,5.30464345 4.71639867,5.29597253 4.48150998,5.29741768 C4.35847305,5.29452738 4.20677411,5.32270785 4.12917695,5.32632073 C4.04388999,5.33571422 3.99845021,5.34004968 3.99845021,5.34004968 C3.50070989,5.39279773 3.05540009,5.0185033 3.0050668,4.5040291 C2.96242332,4.07337373 3.20989533,3.68173747 3.58180242,3.53433194 C3.58180242,3.53433194 3.62514497,3.51771269 3.70553842,3.48591934 C3.79501983,3.454126 3.88310309,3.40571339 4.07395015,3.34862988 C4.46403315,3.22506936 4.96317161,3.09789597 5.6119118,3.03286412 C6.25575847,2.96999999 7.02893374,2.9880644 7.85034507,3.16076009 C8.66826104,3.33851382 9.53091772,3.67740202 10.3222689,4.17525697 C10.7053612,4.422378 11.1087266,4.72441482 11.4247078,5.00910981 C11.5596289,5.12183169 11.8001102,5.36533984 11.9385268,5.51708083 C12.1000127,5.68616364 12.2468182,5.86536252 12.3922255,6.04311624 C12.9591741,6.76424721 13.37722,7.545352 13.6547522,8.2534766 C13.9134224,8.91884719 14.055538,9.51573832 14.1351159,9.95564876 L15.4897593,9.95564876 C16.1024852,9.95564876 16.6500676,10.3333021 16.8842064,10.9178352 C17.1192892,11.5033442 16.9908905,12.1708245 16.5575451,12.6187388 L12.7603439,16.5426249 C12.4752234,16.8373311 12.0956921,17.0002978 11.6935022,17.0002978 C11.2903682,17.0002978 10.9108369,16.8373311 10.6266605,16.5426249 L6.82851524,12.6187388 C6.39611391,12.1708245 6.26677115,11.5033442 6.50185397,10.9178352 C6.73693679,10.3333021 7.28451925,9.95564876 7.89630104,9.95564876 L9.4379644,9.95564876 Z" fill="#4C97FF" transform="translate(10.000298, 10.000000) scale(-1, 1) rotate(-90.000000) translate(-10.000298, -10.000000) "></path>
</g>
</svg>
\ No newline at end of file
......@@ -3,6 +3,8 @@ const bindAll = require('lodash.bindall');
const headless = process.env.SMOKE_HEADLESS || false;
const remote = process.env.SMOKE_REMOTE || false;
const ci = process.env.CI || false;
const buildID = process.env.TRAVIS_BUILD_NUMBER;
const {SAUCE_USERNAME, SAUCE_ACCESS_KEY} = process.env;
const {By, until} = webdriver;
......@@ -24,7 +26,13 @@ class SeleniumHelper {
}
buildDriver (name) {
if (remote === 'true'){
this.driver = this.getSauceDriver(SAUCE_USERNAME, SAUCE_ACCESS_KEY, name);
let nameToUse;
if (ci === 'true'){
nameToUse = 'travis ' + buildID + ' : ' + name;
} else {
nameToUse = name;
}
this.driver = this.getSauceDriver(SAUCE_USERNAME, SAUCE_ACCESS_KEY, nameToUse);
} else {
this.driver = this.getDriver();
}
......
......@@ -165,6 +165,9 @@ module.exports = {
'process.env.NODE_ENV': '"' + (process.env.NODE_ENV || 'development') + '"',
'process.env.SENTRY_DSN': '"' + (process.env.SENTRY_DSN || '') + '"',
'process.env.API_HOST': '"' + (process.env.API_HOST || 'https://api.scratch.mit.edu') + '"',
'process.env.ASSET_HOST': '"' + (process.env.ASSET_HOST || 'https://assets.scratch.mit.edu') + '"',
'process.env.BACKPACK_HOST': '"' + (process.env.BACKPACK_HOST || 'https://backpack.scratch.mit.edu') + '"',
'process.env.PROJECT_HOST': '"' + (process.env.PROJECT_HOST || 'https://projects.scratch.mit.edu') + '"',
'process.env.SCRATCH_ENV': '"' + (process.env.SCRATCH_ENV || 'development') + '"'
}),
new webpack.optimize.CommonsChunkPlugin({
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment