Commit b604b196 authored by Connor Hudson's avatar Connor Hudson Committed by Eric Rosenbaum

Refactor Extension Landing Pages (#2006)

* Add extension-landing generalized styles, install scratch link component

* Make EV3 page use generalized stuff

* Use OS_ENUM file instead of class variable

* use extension-landing/os-enum in os chooser

* Use extension-landing class in extension-landing.scss

* Use extension-landing styles and components on microbit page

* Add view-specific styles

* Move install scratch link l10n strings to src/l10n.json

* Start moving steps display to its own components

* Finish initial pass at Step, Steps components for extension landing pages

* Create ProjectCard component

* Use new components on InstallScratchLInk component

* Use new components on EV3 page

* allow className prop in Steps component

* Use new components on micro:bit landing page

* imageUrl -> imageSrc in ProjectCard

* Create ExtensionHeader component and use it on micro:bit and EV3 pages

* Fix a spacing issue in the InstallScratchLink component

* Add ExtensionRequirements component

* Use ExtensionRequirements component on landing pages

* Remove requirements l10n string for ev3 page

* Move project card styles out of things-to-try section

* Don't render the number row in a step if compact and number props are not set

* Add ExtensionSection component

* Use ExtensionSection on ev3 and microbit pages

* Move state configuration to ExtensionLanding class

* Move tip box, screenshot styles outside of specific section

* Add TipBox component and use it on the EV3 page

* Use hr element instead of section-separator div

* Remove refactor TODO comments :)
parent 078feca0
const PropTypes = require('prop-types');
const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
require('./extension-landing.scss');
const ExtensionHeader = props => (
<div className="extension-header">
<FlexRow className="inner">
<FlexRow className="column extension-info">
{props.children}
</FlexRow>
<div className="extension-image">
<img
alt={props.imageAlt}
src={props.imageSrc}
/>
</div>
</FlexRow>
</div>
);
ExtensionHeader.propTypes = {
children: PropTypes.node,
imageAlt: PropTypes.string,
imageSrc: PropTypes.string
};
module.exports = ExtensionHeader;
const bindAll = require('lodash.bindall');
const React = require('react');
const OS_ENUM = require('./os-enum.js');
class ExtensionLanding extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'onSetOS'
]);
this.state = {
OS: OS_ENUM.WINDOWS
};
}
onSetOS (os) {
this.setState({
OS: os
});
}
}
module.exports = ExtensionLanding;
@import "../../colors";
@import "../../frameless";
#view {
padding: 0;
}
.extension-landing {
&>div {
padding: 4rem 0;
}
h2 {
margin-bottom: 1rem;
}
h3 {
margin-bottom: 2rem;
}
span {
line-height: 1.7rem;
}
hr {
margin: 4rem 0;
border-width: 1px 0 0 0;
border-style: solid;
border-color: $ui-border;
width: 100%;
}
.download {
display: inline-block;
&::after {
display: inline-block;
margin-left: .5rem;
background-image: url("/svgs/extensions/download.svg");
background-repeat: no-repeat;
width: 20px;
height: 20px;
vertical-align: text-top;
content: "";
}
}
.screenshot {
border-radius: .5rem;
}
.tip-box {
margin-top: 4rem;
border: 1px solid $ui-blue-25percent;
border-radius: 1rem;
background-color: $ui-blue-10percent;
padding: 2rem 3rem;
width: 100%;
box-sizing: border-box;
.tip-content {
align-items: flex-start;
p {
margin-top: 0;
}
}
}
.extension-header {
background-size: cover;
color: $ui-white;
.inner {
justify-content: space-between;
flex-wrap: nowrap;
}
.extension-info {
max-width: $cols7;
align-items: flex-start;
.extension-copy {
margin-bottom: 5rem;
align-items: flex-start;
h2 {
display: flex;
margin-bottom: 2rem;
color: $ui-white;
}
h2 img {
padding-right: .5rem;
max-height: 100%;
}
span {
font-size: 1.2rem;
}
a {
border-bottom: 1px solid $ui-white;
color: $ui-white;
}
}
.extension-requirements-container {
font-weight: 500;
align-items: flex-start;
.requirements-header {
margin-bottom: 1.5rem;
}
.extension-requirements {
justify-content: space-between;
}
.extension-requirements span {
display: flex;
margin-right: 1rem;
font-size: 15px; // TODO: change to rem later
align-items: center;
}
.extension-requirements span img {
padding-right: .5rem;
}
}
}
.extension-image {
width: 100%;
max-width: $cols5;
img {
max-width: 100%;
max-height: 100%;
}
}
}
.os-chooser {
padding: 0;
}
.install-scratch-link {
padding: 2rem 0;
.inner {
align-items: flex-start;
}
.step-image.badge {
height: initial;
}
.download-button {
display: flex;
align-items: center;
img {
margin-left: .5rem;
}
}
}
.extension-section {
.inner {
align-items: flex-start;
}
}
.getting-started {
.getting-started-section {
width: 100%;
align-items: flex-start;
a {
margin: 1rem 0;
}
}
}
.things-to-try .inner {
align-items: center;
}
.project-card {
margin: 0 1.5rem;
border: 1px solid $ui-border;
border-radius: .5rem;
background-color: $ui-white;
overflow: hidden;
flex-basis: 0;
flex-grow: 1;
}
.project-card-image {
img {
max-width: 100%;
}
}
.project-card-info {
padding: 1rem;
p {
margin: .2rem 0;
}
}
.faq {
p {
margin-bottom: 1.25rem;
margin-left: 0;
max-width: $cols8;
text-align: left;
}
.faq-title {
margin-bottom: 0;
font-size: 1.4rem;
}
ul {
max-width: $cols8;
}
section {
ul {
max-width: $cols8;
}
.nav-spacer {
display: block;
visibility: hidden;
margin-top: -50px; // height of nav bar
height: 50px;
}
}
ul,
ol {
&.indented {
padding-left: $cols1 + (20px / $em);
}
}
}
.blue {
background-color: $ui-blue-10percent;
}
.inner {
max-width: $cols12;
}
}
const PropTypes = require('prop-types');
const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
require('./extension-landing.scss');
const ExtensionRequirements = props => (
<FlexRow className="column extension-requirements-container">
<span className="requirements-header">
<FormattedMessage id="extensionHeader.requirements" />
</span>
<FlexRow className="extension-requirements">
{props.children}
</FlexRow>
</FlexRow>
);
ExtensionRequirements.propTypes = {
children: PropTypes.node
};
module.exports = ExtensionRequirements;
const PropTypes = require('prop-types');
const classNames = require('classnames');
const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
require('./extension-landing.scss');
const ExtensionSection = props => (
<div className={classNames('extension-section', props.className)}>
<FlexRow className="inner column">
{props.children}
</FlexRow>
</div>
);
ExtensionSection.propTypes = {
children: PropTypes.node,
className: PropTypes.string
};
module.exports = ExtensionSection;
const PropTypes = require('prop-types');
const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react');
const OS_ENUM = require('./os-enum.js');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Steps = require('../../components/steps/steps.jsx');
const Step = require('../../components/steps/step.jsx');
require('./extension-landing.scss');
const InstallScratchLink = ({
currentOS
}) => (
<div className="blue install-scratch-link">
<FlexRow className="inner column">
<h2><FormattedMessage id="installScratchLink.installHeaderTitle" /></h2>
<Steps>
<div className="step">
<Step
compact
number={1}
>
<span className="step-description">
<FormattedMessage id="installScratchLink.downloadAndInstall" />
</span>
<a
className="step-image badge"
href={`https://downloads.scratch.mit.edu/link/${
currentOS === OS_ENUM.WINDOWS ? 'windows' : 'mac'
}.zip`}
>
<button className="button download-button">
{currentOS === OS_ENUM.WINDOWS ?
<FormattedMessage id="installScratchLink.windowsDownload" /> :
<FormattedMessage id="installScratchLink.macosDownload" />
}
<img src="/svgs/extensions/download-white.svg" />
</button>
</a>
</Step>
</div>
<Step
compact
number={2}
>
<span className="step-description">
<FormattedMessage id="installScratchLink.startScratchLink" />
</span>
<div className="step-image">
<img
className="screenshot"
src={`/images/scratchlink/${
currentOS === OS_ENUM.WINDOWS ? 'windows' : 'mac'
}-toolbar.png`}
/>
</div>
</Step>
</Steps>
</FlexRow>
</div>
);
InstallScratchLink.propTypes = {
currentOS: PropTypes.string
};
module.exports = InstallScratchLink;
const OS_ENUM = {
WINDOWS: 'Windows',
MACOS: 'macOS'
};
module.exports = OS_ENUM;
const PropTypes = require('prop-types');
const React = require('react');
const ProjectCard = props => (
<a
download
className="project-card"
href={props.cardUrl}
>
<div className="project-card-image">
<img src={props.imageSrc} />
</div>
<div className="project-card-info">
<h4>{props.title}</h4>
<p>{props.description}</p>
</div>
</a>
);
ProjectCard.propTypes = {
cardUrl: PropTypes.string,
description: PropTypes.string,
imageSrc: PropTypes.string,
title: PropTypes.string
};
module.exports = ProjectCard;
const PropTypes = require('prop-types');
const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const TipBox = props => (
<div className="tip-box">
<h4>{props.title}</h4>
<FlexRow className="column tip-content">
{props.children}
</FlexRow>
</div>
);
TipBox.propTypes = {
children: PropTypes.node,
title: PropTypes.string
};
module.exports = TipBox;
......@@ -8,12 +8,9 @@ const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Button = require('../../components/forms/button.jsx');
require('./os-chooser.scss');
const OS_ENUM = require('../../components/extension-landing/os-enum.js');
const OS_ENUM = {
WINDOWS: 'Windows',
MACOS: 'macOS'
};
require('./os-chooser.scss');
const OSChooser = props => (
<div className="os-chooser">
......
const PropTypes = require('prop-types');
const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
require('./steps.scss');
const Step = props => (
<div className="step">
{(props.compact || props.number) &&
<FlexRow className="step-number-row">
{props.number && <div className="step-number">{props.number}</div>}
{props.compact && <FlexRow className="step-content">{props.children}</FlexRow>}
</FlexRow>
}
{!props.compact &&
<div className="step-content">
{props.children}
</div>
}
</div>
);
Step.propTypes = {
children: PropTypes.node,
compact: PropTypes.bool,
number: PropTypes.number
};
module.exports = Step;
const PropTypes = require('prop-types');
const React = require('react');
const classNames = require('classnames');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
require('./steps.scss');
const Steps = props => (
<FlexRow className={classNames('steps', props.className)}>
{/* TODO: Should this component do something with automatically numbering individual steps? */}
{props.children}
</FlexRow>
);
Steps.propTypes = {
children: PropTypes.node,
className: PropTypes.string
};
module.exports = Steps;
@import "../../colors";
.steps {
display: flex;
width: 100%;
justify-content: space-between;
align-items: flex-start;
}
.step {
flex-basis: 0;
flex-grow: 1;
.step-number-row {
padding-bottom: 1.5rem;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: flex-start;
.step-content {
text-align: left;
align-items: flex-start;
.step-description {
margin-bottom: 1rem;
}
}
.step-number {
display: inline-flex;
border-radius: 2rem;
background-color: $ui-blue;
width: 2rem;
height: 2rem;
color: $ui-white;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
}
.step-content {
display: flex;
padding: 0 2rem;
text-align: center;
flex-flow: column;
align-items: center;
box-sizing: border-box;
.step-image {
height: 10rem;
img {
width: auto;
height: 100%;
}
}
}
}
......@@ -103,8 +103,16 @@
"navigation.signOut": "Sign out",
"extensionHeader.requirements": "Requirements",
"oschooser.choose": "Choose your OS:",
"installScratchLink.installHeaderTitle": "Install Scratch Link",
"installScratchLink.downloadAndInstall": "Download and install Scratch Link.",
"installScratchLink.windowsDownload": "Download for Windows",
"installScratchLink.macosDownload": "Download for macOS",
"installScratchLink.startScratchLink": "Start Scratch Link and make sure it is running. It should appear in your toolbar.",
"parents.FaqAgeRangeA": "While Scratch is primarily designed for 8 to 16 year olds, it is also used by people of all ages, including younger children with their parents.",
"parents.FaqAgeRangeQ": "What is the age range for Scratch?",
"parents.FaqResourcesQ": "What resources are available for learning Scratch?",
......
This diff is collapsed.
// TODO: Refactor this file and views/microbit/microbit.scss into something that can be used in both places (scratch-www#1982)
@import "../../colors";
@import "../../frameless";
#view {
padding: 0;
}
.ev3 {
&>div {
padding: 4rem 0;
}
h2 {
margin-bottom: 1rem;
}
h3 {
margin-bottom: 2rem;
}
span {
line-height: 1.7rem;
}
.download {
display: inline-block;
&::after {
display: inline-block;
margin-left: .5rem;
background-image: url("/svgs/extensions/download.svg");
background-repeat: no-repeat;
width: 20px;
height: 20px;
vertical-align: text-top;
content: "";
}
}
.extension-header {
background-color: $ui-aqua;
background-image: url("/images/ev3/ev3-pattern.svg");
background-size: cover;
color: $ui-white;
.inner {
justify-content: space-between;
flex-wrap: nowrap;
}
.extension-info {
max-width: $cols7;
align-items: flex-start;
.extension-copy {
margin-bottom: 5rem;
align-items: flex-start;
h2 {
display: flex;
margin-bottom: 2rem;
color: $ui-white;
}
h2 img {
padding-right: .5rem;
max-height: 100%;
}
span {
font-size: 1.2rem;
}
a {
border-bottom: 1px solid $ui-white;
color: $ui-white;
}
}
.extension-requirements-container {
font-weight: 500;
align-items: flex-start;
.requirements-header {
margin-bottom: 1.5rem;
}
.extension-requirements {
justify-content: space-between;
}
.extension-requirements span {
display: flex;
margin-right: 1rem;
font-size: 15px; // TODO: change to rem later
align-items: center;
}
.extension-requirements span img {
padding-right: .5rem;
}
}
}
.extension-image {
width: 100%;
max-width: $cols5;
img {
max-width: 100%;
max-height: 100%;
}
}
}
.os-chooser {
padding: 0;
}
.install-scratch-link,
.getting-started,
.faq {
.inner {
align-items: flex-start;
}
}
.install-scratch-link {
padding: 2rem 0;
.step-image.badge {
height: initial;
}
.download-button {
display: flex;
align-items: center;
img {
margin-left: .5rem;
}
}
}
.screenshot {
border-radius: .5rem;
}
.getting-started {
.connecting-ev3 {
width: 100%;
align-items: flex-start;
}
.tip-box {
margin-top: 4rem;
border: 1px solid $ui-blue-25percent;
border-radius: 1rem;
background-color: $ui-blue-10percent;
padding: 2rem 3rem;
width: 100%;
box-sizing: border-box;
.tip-content {
align-items: flex-start;
p {
margin-top: 0;
}
}
}
}
.things-to-try {
.project-card {
margin: 0 1.5rem;
border: 1px solid $ui-border;
border-radius: .5rem;
background-color: $ui-white;
overflow: hidden;
flex-basis: 0;
flex-grow: 1;
}
.project-card-image {
img {
max-width: 100%;
}
}
.project-card-info {
padding: 1rem;
p {
margin: .2rem 0;
}
}
.section-separator {
margin: 4rem 0;
border-top: 1px solid $ui-border;
width: 100%;
}
}
.faq {
p {
margin-bottom: 1.25rem;
margin-left: 0;
max-width: $cols8;
text-align: left;
}
.faq-title {
margin-bottom: 1rem;
font-size: 1.4rem;
}
ul {
max-width: $cols8;
}
section {
ul {
max-width: $cols8;
}
.nav-spacer {
display: block;
visibility: hidden;
margin-top: -50px; // height of nav bar
height: 50px;
}
}
ul,
ol {
&.indented {
padding-left: $cols1 + (20px / $em);
}
}
}
.blue {
background-color: $ui-blue-10percent;
}
.inner {
max-width: $cols12;
}
}
.steps {
display: flex;
width: 100%;
justify-content: space-between;
align-items: flex-start;
}
.step {
flex-basis: 0;
flex-grow: 1;
.step-number-row {
padding-bottom: 1.5rem;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: flex-start;
.step-content {
text-align: left;
align-items: flex-start;
.step-description {
margin-bottom: 1rem;
}
}
.step-number {
display: inline-flex;
border-radius: 2rem;
background-color: $ui-blue;
width: 2rem;
height: 2rem;
color: $ui-white;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
}
.step-content {
display: flex;
padding: 0 2rem;
text-align: center;
flex-flow: column;
align-items: center;
box-sizing: border-box;
.step-image {
height: 10rem;
img {
width: auto;
height: 100%;
}
}
}
}
{
"ev3.headerText": "{ev3Link} is an invention kit with motors and sensors you can use to build interactive robotic creations. Connecting it to Scratch expands the possibilities: build a robotic puppet and tell stories, make your own musical instruments and game controllers, or whatever else you can imagine.",
"ev3.requirements": "Requirements",
"ev3.installScratchLink": "Install Scratch Link",
"ev3.installScratchLinkStep": "Download and install Scratch Link.",
"ev3.windowsDownload": "Download for Windows",
"ev3.macosDownload": "Download for macOS",
"ev3.startScratchLink": "Start Scratch Link and make sure it is running. It should appear in your toolbar.",
"ev3.gettingStarted": "Getting Started",
"ev3.connectingEV3": "Connecting EV3 to Scratch",
"ev3.turnOnEV3": "Turn on your EV3 by holding down the center button.",
......
{
"microbit.headerText": "{microbitLink} is a tiny circuit board designed to help kids learn to code and create with technology. It has many features including an LED display, buttons, and a motion sensor. You can connect it to Scratch and build creative projects that combine the magic of the digital and physical worlds.",
"microbit.requirements": "Requirements",
"microbit.installScratchLink": "Install Scratch Link",
"microbit.installScratchLinkStep": "Download and install Scratch Link.",
"microbit.windowsDownload": "Download for Windows",
"microbit.macosDownload": "Download for macOS",
"microbit.startScratchLink": "Start Scratch Link and make sure it is running. It should appear in your toolbar.",
"microbit.gettingStarted": "Getting Started",
"microbit.installMicrobitHex": "Install Scratch micro:bit HEX",
"microbit.connectUSB": "Connect a micro:bit to your computer with a USB cable",
......
This diff is collapsed.
// TODO: Refactor this file and views/ev3/ev3.scss into something that can be used in both places (scratch-www#1982)
@import "../../colors";
@import "../../frameless";
#view {
padding: 0;
}
.microbit {
&>div {
padding: 4rem 0;
}
h2 {
margin-bottom: 1rem;
}
h3 {
margin-bottom: 2rem;
}
span {
line-height: 1.7rem;
}
.download {
display: inline-block;
&::after {
display: inline-block;
margin-left: .5rem;
background-image: url("/svgs/extensions/download.svg");
background-repeat: no-repeat;
width: 20px;
height: 20px;
vertical-align: text-top;
content: "";
}
}
.extension-header {
background-color: $ui-purple;
background-image: url("/images/microbit/mbit-pattern.svg");
background-size: cover;
color: $ui-white;
.inner {
justify-content: space-between;
flex-wrap: nowrap;
}
.extension-info {
padding-right: $cols1;
max-width: $cols7 + ($gutter / $em);
align-items: flex-start;
.extension-copy {
margin-bottom: 5rem;
align-items: flex-start;
h2 {
display: flex;
margin-bottom: 2rem;
color: $ui-white;
}
h2 img {
padding-right: .5rem;
max-height: 100%;
}
a {
border-bottom: 1px solid $ui-white;
color: $ui-white;
}
span {
font-size: 1.2rem;
}
}
.extension-requirements-container {
font-weight: 500;
align-items: flex-start;
.requirements-header {
margin-bottom: 1.5rem;
}
.extension-requirements {
justify-content: space-between;
}
.extension-requirements span {
display: flex;
margin-right: 1rem;
font-size: 15px; // TODO: change to rem later
align-items: center;
}
.extension-requirements span img {
padding-right: .5rem;
}
}
}
.extension-image {
width: 100%;
max-width: $cols4;
img {
max-width: 100%;
max-height: 100%;
}
}
}
.os-chooser {
padding: 0;
}
.install-scratch-link,
.getting-started,
.faq {
.inner {
align-items: flex-start;
}
}
.install-scratch-link {
padding: 2rem 0;
.step-image.badge {
height: initial;
}
.download-button {
display: flex;
align-items: center;
img {
margin-left: .5rem;
}
}
}
.screenshot {
border-radius: .5rem;
}
.getting-started {
.install-hex,
.connecting {
width: 100%;
align-items: flex-start;
a {
margin: 1rem 0;
}
}
.tip-box {
margin-top: 4rem;
border: 1px solid $ui-blue-25percent;
border-radius: 1rem;
background-color: $ui-blue-10percent;
padding: 0 2rem;
width: 100%;
box-sizing: border-box;
.tip-content {
align-items: flex-start;
}
}
}
......@@ -182,140 +22,5 @@
align-self: center;
}
}
.project-card {
margin: 0 1.5rem;
border: 1px solid $ui-border;
border-radius: .5rem;
background-color: $ui-white;
overflow: hidden;
flex-basis: 0;
flex-grow: 1;
}
.project-card-image {
img {
max-width: 100%;
}
}
.project-card-info {
padding: 1rem;
p {
margin: .2rem 0;
}
}
}
.faq {
p {
margin-bottom: 1.25rem;
margin-left: 0;
max-width: $cols8;
text-align: left;
}
.faq-title {
margin-bottom: 1rem;
font-size: 1.4rem;
}
ul {
max-width: $cols8;
}
section {
ul {
max-width: $cols8;
}
.nav-spacer {
display: block;
visibility: hidden;
margin-top: -50px; // height of nav bar
height: 50px;
}
}
ul,
ol {
&.indented {
padding-left: $cols1 + (20px / $em);
}
}
}
.blue {
background-color: $ui-blue-10percent;
}
.inner {
max-width: $cols12;
}
.section-separator {
margin: 4rem 0;
border-top: 1px solid $ui-border;
width: 100%;
}
}
.steps {
display: flex;
width: 100%;
justify-content: space-between;
align-items: flex-start;
}
.step {
flex-basis: 0;
flex-grow: 1;
.step-number-row {
padding-bottom: 1.5rem;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: flex-start;
.step-content {
text-align: left;
align-items: flex-start;
.step-description {
margin-bottom: 1rem;
}
}
.step-number {
display: inline-flex;
border-radius: 2rem;
background-color: $ui-blue;
width: 2rem;
height: 2rem;
color: $ui-white;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
}
.step-content {
display: flex;
padding: 0 2rem;
text-align: center;
flex-flow: column;
align-items: center;
box-sizing: border-box;
.step-image {
height: 10rem;
img {
width: auto;
height: 100%;
}
}
}
}
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