feat: [UEPR-90] Added new section to the ideas page

This commit is contained in:
MiroslavDionisiev 2024-11-01 13:10:04 +02:00
parent a906ade362
commit c27046698a
8 changed files with 377 additions and 407 deletions

View file

@ -59,6 +59,7 @@ $active-dark-gray: hsla(0, 0%, 0%, .2);
$box-shadow-gray: hsla(0, 0%, 0%, .25);
$box-shadow-light-gray: hsla(0, 0%, 0%, .15);
$overlay-gray: hsla(0, 0%, 0%, .75);
$gray-50: hsla(226, 15%, 97%, 1);
$transparent-light-blue: rgba(229, 240, 254, 0);
/* Typography Colors */

View file

@ -1,284 +1,261 @@
const bindAll = require('lodash.bindall');
const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl;
const React = require('react');
const Button = require('../../components/forms/button.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const MasonryGrid = require('../../components/masonrygrid/masonrygrid.jsx');
const TitleBanner = require('../../components/title-banner/title-banner.jsx');
const TTTModal = require('../../components/modal/ttt/modal.jsx');
const TTTTile = require('../../components/ttt-tile/ttt-tile.jsx');
const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx');
const Tiles = require('./ttt.json');
const {useIntl} = require('react-intl');
require('./ideas.scss');
class Ideas extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleShowTTTModal',
'handleHideTTTModal',
'renderTiles'
]);
this.state = {
currentTile: Tiles[0],
TTTModalOpen: false
};
const tipsSectionData = [
{
tipImage: {
altTextId: 'ideas.gettingStartedImageDescription',
imageSrc: '/images/ideas/getting-started-illustration.svg'
},
button: {
href: '/projects/editor/?tutorial=getStarted',
buttonTextId: 'ideas.gettingStartedButtonText'
}
},
{
tipImage: {
altTextId: 'ideas.seeTutorialsLibraryImageDescription',
imageSrc: '/images/ideas/see-tutorials-library.png'
},
button: {
href: '/projects/editor/?tutorial=all',
buttonTextId: 'ideas.seeTutorialsLibraryButtonText'
}
},
{
tipImage: {
altTextId: 'ideas.starterProjectsImageDescription',
imageSrc: '/images/ideas/starter-projects-illustration.svg'
},
button: {
href: '/starter_projects',
buttonTextId: 'ideas.starterProjectsButton'
}
},
{
tipImage: {
altTextId: 'ideas.cardsIllustrationDescription',
imageSrc: '/images/ideas/cards-illustration.svg'
},
button: {
hrefId: 'cards.scratch-cards-allLink',
buttonImageSrc: '/images/ideas/download-icon.svg',
buttonTextId: 'ideas.codingCards'
}
}
handleShowTTTModal (tile) {
// expects translated tile
this.setState({
currentTile: tile,
TTTModalOpen: true
});
];
const physicalIdeasData = [
{
physicalIdeasImage: {
imageSrc: '/images/ideas/micro-bit.png',
imageClass: 'micro-bit'
},
physicalIdeasDescription: {
headerId: 'ideas.microBitHeader',
bodyId: 'ideas.microBitBody',
hrefId: 'cards.microbit-cardsLink',
buttonImageSrc: '/images/ideas/download-icon.svg',
buttonTextId: 'ideas.codingCards'
}
},
{
physicalIdeasImage: {
imageSrc: '/images/ideas/make-board.svg',
imageClass: 'makey-makey-img'
},
physicalIdeasDescription: {
headerId: 'ideas.makeyMakeyHeader',
bodyId: 'ideas.makeyMakeyBody',
hrefId: 'cards.makeymakey-cardsLink',
buttonImageSrc: '/images/ideas/download-icon.svg',
buttonTextId: 'ideas.codingCards'
}
}
handleHideTTTModal () {
this.setState({
TTTModalOpen: false
});
}
renderTiles () {
return Tiles.map((tile, key) => {
const translatedTile = {
tutorialUrl: `/projects/editor/?tutorial=${tile.tutorialUrl}`,
modalImage: tile.modalImage,
modalImageDescription: this.props.intl.formatMessage({id: tile.modalImageDescription}),
description: this.props.intl.formatMessage({id: tile.description}),
guideUrl: this.props.intl.formatMessage({id: tile.guideUrl}),
thumbImage: tile.thumbImage,
thumbImageDescription: this.props.intl.formatMessage({id: tile.thumbImageDescription}),
title: this.props.intl.formatMessage({id: tile.title}),
cardsUrl: this.props.intl.formatMessage({id: tile.cardsUrl})
};
return (
<TTTTile
key={key}
onClick={() => { // eslint-disable-line react/jsx-no-bind
this.handleShowTTTModal(translatedTile);
}}
{...translatedTile}
/>
);
});
}
render () {
return (
<div>
<div className="banner-wrapper">
<TitleBanner className="masthead ideas-banner">
<div className="title-banner-p">
<img
alt={this.props.intl.formatMessage({id: 'ideas.headerImageDescription'})}
src="/images/ideas/masthead-illustration.svg"
/>
<h1 className="title-banner-h1">
<FormattedMessage id="ideas.headerMessage" />
</h1>
<a href="/projects/editor/?tutorial=all">
<Button className="banner-button">
<img
alt=""
src="/images/ideas/bulb-yellow-icon.svg"
/>
<FormattedMessage id="ideas.headerButtonMessage" />
</Button>
</a>
</div>
</TitleBanner>
</div>
<div className="tips-getting-started">
<div className="inner">
<FlexRow
as="section"
className="tips-info-section tips-left"
>
<div className="ideas-image">
];
const Ideas = () => {
const intl = useIntl();
return (
<div>
<div className="banner-wrapper">
<TitleBanner className="masthead ideas-banner">
<div className="title-banner-p">
<img
alt={intl.formatMessage({id: 'ideas.headerImageDescription'})}
src="/images/ideas/masthead-illustration.svg"
/>
<h1 className="title-banner-h1">
<FormattedMessage id="ideas.headerMessage" />
</h1>
<a href="/projects/editor/?tutorial=all">
<Button className="banner-button">
<img
alt={this.props.intl.formatMessage({id: 'ideas.gettingStartedImageDescription'})}
src="/images/ideas/getting-started-illustration.svg"
alt=""
src="/images/ideas/bulb-yellow-icon.svg"
/>
</div>
<div>
<h2>
<FormattedMessage id="ideas.gettingStartedTitle" />
</h2>
<p>
<FormattedMessage id="ideas.gettingStartedText" />
</p>
<a href="/projects/editor/?tutorial=getStarted">
<Button className="ideas-button">
<img
alt=""
src="/images/ideas/try-it-icon.svg"
/>
<FormattedMessage id="ideas.tryIt" />
</Button>
</a>
</div>
</FlexRow>
<FormattedMessage id="ideas.headerButtonMessage" />
</Button>
</a>
</div>
</div>
<div className="tips-activity-guides">
<div className="inner">
<section className="ttt-section">
<div className="ttt-head">
<h2>
<FormattedMessage id="ideas.activityGuidesTitle" />
</h2>
<p>
<FormattedMessage id="ideas.activityGuidesText" />
</p>
</div>
<MasonryGrid >
{this.renderTiles()}
</MasonryGrid>
<TTTModal
isOpen={this.state.TTTModalOpen}
onRequestClose={this.handleHideTTTModal}
{...this.state.currentTile}
/>
<a
className="wide-button"
href="/projects/editor/?tutorial=all"
>
<Button className="ideas-button wide-button">
<FormattedMessage id="ideas.seeAllTutorials" />
</Button>
</a>
</section>
</div>
</div>
<div>
<div className="inner">
<FlexRow
as="section"
className="tips-info-section cards-info ideas-all-cards"
>
<div className="tips-info-body">
<h2>
<FormattedMessage id="ideas.cardsTitle" />
</h2>
<p>
<FormattedMessage id="ideas.cardsText" />
</p>
<a
href={this.props.intl.formatMessage({
id: 'cards.scratch-cards-allLink'
})}
rel="noopener noreferrer"
target="_blank"
>
<Button className="ideas-button">
<img
alt=""
src="/images/ideas/download-icon.svg"
/>
<FormattedMessage id="general.downloadPDF" />
</Button>
</a>
</div>
<div className="tips-info-body tips-illustration">
<img
alt={this.props.intl.formatMessage({id: 'ideas.cardsIllustrationDescription'})}
src="/images/ideas/cards-illustration.svg"
/>
</div>
</FlexRow>
</div>
</div>
</TitleBanner>
</div>
<div className="tips">
<div className="inner">
<div className="tips-divider" />
</div>
<div>
<div className="inner">
<FlexRow
as="section"
className="ideas-starter tips-info-section tips-left"
>
<div className="ideas-image">
<img
alt={this.props.intl.formatMessage({id: 'ideas.starterProjectsImageDescription'})}
src="/images/ideas/starter-projects-illustration.svg"
/>
</div>
<div className="tips-info-body">
<h2>
<FormattedMessage id="ideas.starterProjectsTitle" />
</h2>
<p>
<FormattedMessage id="ideas.starterProjectsText" />
</p>
<p>
<a href="/starter_projects">
<Button className="ideas-button">
<FormattedMessage id="ideas.starterProjectsButton" />
</Button>
</a>
</p>
</div>
</FlexRow>
<div className="section-header">
<FormattedMessage id="ideas.startHereText" />
</div>
</div>
<div className="gray-area">
<div className="inner">
<FlexRow
as="section"
className="tips-info-section mod-align-top"
>
<div className="tips-info-body mod-narrow">
<FlexRow
as="section"
className="tips-section"
>
{tipsSectionData.map((tipData, index) => (
<div
key={index}
className="tip"
>
<img
className="tips-icon"
alt=""
src="/images/tips/download-icon.svg"
alt={intl.formatMessage({
id: tipData.tipImage.altTextId
})}
src={tipData.tipImage.imageSrc}
className="tips-img"
/>
<h3>
<FormattedMessage id="ideas.desktopEditorHeader" />
</h3>
<p>
<FormattedMessage
id="ideas.desktopEditorBodyHTML"
values={{a: chunks => <a href="/download">{chunks}</a>}}
/>
</p>
<a
href={
tipData.button.href ?
tipData.button.href :
intl.formatMessage({
id: tipData.button.hrefId
})
}
>
<Button className="tips-button">
{tipData.button.buttonImageSrc && (
<img src={tipData.button.buttonImageSrc} />
)}
<FormattedMessage id={tipData.button.buttonTextId} />
</Button>
</a>
</div>
<div className="tips-info-body mod-narrow">
<img
className="tips-icon"
alt=""
src="/images/tips/question-icon.svg"
/>
<h3>
<FormattedMessage id="ideas.questionsHeader" />
</h3>
<p>
<FormattedMessage
id="ideas.questionsBodyHTML"
values={{
faq: chunks => <a href="/info/faq">{chunks}</a>,
forum: chunks => <a href="/discuss/7/">{chunks}</a>
}}
/>
</p>
</div>
</FlexRow>
</div>
))}
</FlexRow>
</div>
</div>
);
}
}
Ideas.propTypes = {
intl: intlShape
<div className="physical-ideas">
<div className="inner">
<div className="section-header">
<FormattedMessage id="ideas.physicalPlayIdeas" />
</div>
<FlexRow
as="section"
className="physical-ideas-section"
>
{physicalIdeasData.map((physicalIdea, index) => (
<div
key={index}
className="physical-idea"
>
<img
src={physicalIdea.physicalIdeasImage.imageSrc}
className={physicalIdea.physicalIdeasImage.imageClass}
/>
<div className="physical-idea-description">
<h3>
<FormattedMessage
id={physicalIdea.physicalIdeasDescription.headerId}
/>
</h3>
<p>
<FormattedMessage
id={physicalIdea.physicalIdeasDescription.bodyId}
/>
</p>
<a
href={intl.formatMessage({
id: physicalIdea.physicalIdeasDescription.hrefId
})}
>
<Button className="tips-button">
<img
src={
physicalIdea.physicalIdeasDescription.buttonImageSrc
}
/>
<FormattedMessage
id={physicalIdea.physicalIdeasDescription.buttonTextId}
/>
</Button>
</a>
</div>
</div>
))}
</FlexRow>
</div>
</div>
<div className="gray-area">
<div className="inner">
<FlexRow
as="section"
className="tips-info-section"
>
<div className="tips-info-body mod-narrow">
<img
className="tips-icon"
alt=""
src="/images/tips/download-icon.svg"
/>
<h3>
<FormattedMessage id="ideas.desktopEditorHeader" />
</h3>
<p>
<FormattedMessage
id="ideas.desktopEditorBodyHTML"
values={{a: chunks => <a href="/download">{chunks}</a>}}
/>
</p>
</div>
<div className="tips-info-body mod-narrow">
<img
className="tips-icon"
alt=""
src="/images/tips/question-icon.svg"
/>
<h3>
<FormattedMessage id="ideas.questionsHeader" />
</h3>
<p>
<FormattedMessage
id="ideas.questionsBodyHTML"
values={{
faq: chunks => <a href="/info/faq">{chunks}</a>,
forum: chunks => <a href="/discuss/7/">{chunks}</a>
}}
/>
</p>
</div>
</FlexRow>
</div>
</div>
</div>
);
};
const WrappedIdeas = injectIntl(Ideas);
render(
<Page><WrappedIdeas /></Page>, document.getElementById('app'));
<Page>
<Ideas />
</Page>,
document.getElementById('app')
);

View file

@ -17,127 +17,133 @@ $base-bg: $ui-white;
background: bottom left url("/images/ideas/left-juice.png") no-repeat;
}
.ttt-section {
.tips {
.inner {
display: flex;
flex-direction: column;
gap: 0.75rem;
.tips-section {
display: flex;
justify-content: space-between;
.tip {
display: flex;
flex-direction: column;
.tips-img {
width: $cols3;
height: 10rem;
}
}
}
}
}
.physical-ideas {
.inner {
display: flex;
flex-direction: column;
gap: 1.5rem;
.physical-ideas-section {
justify-content: space-between;
align-items: flex-start;
gap: 1.5rem;
.physical-idea {
display: flex;
flex-direction: row;
gap: 1.5rem;
text-align: start;
.makey-makey-img {
height: 6.5rem;
}
.micro-bit {
height: 8.5rem;
}
.physical-idea-description {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
p {
margin: 0;
font-size: 1rem;
font-weight: 400;
line-height: 1.5rem;
}
.tips-button {
width: auto;
padding: 0.75rem;
}
}
}
.physical-idea:first-child {
flex: 1.5;
}
.physical-idea:last-child {
flex: 2;
}
}
}
}
.tips, .physical-ideas {
display: flex;
margin: 0 auto;
text-align: center;
justify-content: center;
flex-wrap: wrap;
align-items: center;
}
.tips-divider {
border-top: 1px solid $ui-gray;
width: 100%;
}
.tips-banner-image {
max-width: calc(100% - 2rem);
}
.banner-button {
background-color: $ui-white;
color: $ui-aqua;
font-size: 1rem;
img {
margin-right: 1rem;
height: 1.5rem;
vertical-align: middle;
}
a {
color: $ui-white;
}
span {
vertical-align: middle;
}
}
.ideas-button {
margin-right: .75rem;
background-color: $ui-purple-dark;
color: $ui-white;
font-size: 1rem;
img {
margin-right: 1rem;
height: 1.5rem;
vertical-align: middle;
}
a {
color: $ui-white;
}
span {
vertical-align: middle;
}
}
.wide-button {
width: 100%;
}
.ideas-all-cards {
padding: 5rem 0 !important;
}
.ideas-starter {
padding: 5rem 0 !important;
}
.tips-getting-started {
background-color: $ui-light-gray;
padding-top: 2.5rem;
}
.tips-left {
justify-content: flex-start;
}
.ideas-image {
margin-right: 2rem;
}
.tips-activity-guides {
background-color: $ui-light-gray;
padding-bottom: 2rem;
}
.purchase-button {
img {
margin-right: 0;
margin-left: .75rem;
width: 1rem;
vertical-align: baseline;
}
}
.tips-info-section {
padding: 2.5rem 0;
width: 100%;
flex-wrap: nowrap;
}
.tips-info-body {
text-align: left;
}
.tips-cards-buttons {
a {
white-space: normal;
}
padding: 3rem 0;
background-color: $gray-50;
}
.gray-area {
background-color: $ui-gray;
.tips-info-section {
padding: 2.5rem 0;
width: 100%;
flex-wrap: nowrap;
.tips-info-body {
text-align: left;
}
}
img.tips-icon {
height: 1.75rem;
}
}
img.tips-icon {
height: 1.75rem;
.section-header {
font-size: 2rem;
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
font-weight: 700;
}
.tips-button {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.5rem;
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
font-weight: 700;
line-height: 1.25rem;
width: 100%;
padding: 0.5rem 0;
border-radius: 4px;
}
//4 columns
@media #{$small} {
@ -151,14 +157,7 @@ img.tips-icon {
}
}
.ttt-head {
p {
max-width: $cols4;
}
}
//put the image first if in 4-column
// put the image first if in 4-column
.tips-info-body {
max-width: $cols4;
text-align: center;
@ -187,19 +186,6 @@ img.tips-icon {
}
}
.ttt-head {
p {
max-width: $cols6;
}
}
.tips-info-body.tips-illustration {
order: -1;
img {
width: $cols4;
}
}
.tips-info-body {
max-width: $cols4;
text-align: center;
@ -219,23 +205,23 @@ 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;
}
}
@media #{$intermediate-and-smaller}{
.physical-ideas {
.inner {
.physical-ideas-section {
.physical-idea:first-child {
flex: 2;
}
}
}
}
}
// 12 columns
@media #{$big} {
.title-banner {
@ -248,12 +234,6 @@ img.tips-icon {
}
}
.ttt-head {
p {
max-width: $cols8;
}
}
.tips-info-section {
&.mod-align-top {
align-items: flex-start;

View file

@ -2,6 +2,8 @@
"cards.scratch-cards-allLink": "https://resources.scratch.mit.edu/www/cards/en/scratch-cards-all.pdf",
"cards.name-cardsLink": "https://resources.scratch.mit.edu/www/cards/en/name-cards.pdf",
"cards.animation-cardsLink": "https://resources.scratch.mit.edu/www/cards/en/animation-cards.pdf",
"cards.microbit-cardsLink": "https://resources.scratch.mit.edu/www/cards/en/microbit-cards.pdf",
"cards.makeymakey-cardsLink": "https://resources.scratch.mit.edu/www/cards/en/makeymakey-cards.pdf",
"cards.music-cardsLink": "https://resources.scratch.mit.edu/www/cards/en/music-cards.pdf",
"cards.story-cardsLink": "https://resources.scratch.mit.edu/www/cards/en/story-cards.pdf",
"cards.chase-cardsLink": "https://resources.scratch.mit.edu/www/cards/en/chase-cards.pdf",

View file

@ -4,7 +4,11 @@
"ideas.headerButtonMessage": "Choose a tutorial",
"ideas.gettingStartedTitle": "Getting Started",
"ideas.gettingStartedText": "New to Scratch? Try the Getting Started tutorial.",
"ideas.startHereText": "New to Scratch? Stay here!",
"ideas.gettingStartedButtonText": "Try Getting Started Tutorial",
"ideas.seeTutorialsLibraryButtonText": "See Tutorials Library",
"ideas.gettingStartedImageDescription": "An illustrated boy plants his flag on top of a freshly painted mountaintop.",
"ideas.seeTutorialsLibraryImageDescription": "An illustration of three tutorial thumbnails.",
"ideas.tryIt": "Try it!",
"ideas.activityGuidesTitle": "Activity Guides",
"ideas.activityGuidesText": "What do you want to make with Scratch? For each activity, you can try the Tutorial, download a set of Coding Cards, or view the Educator Guide.",
@ -37,6 +41,11 @@
"ideas.tryTheTutorial": "Try the tutorial",
"ideas.codingCards": "Coding Cards",
"ideas.educatorGuide": "Educator Guide",
"ideas.physicalPlayIdeas": "Physical Play Ideas",
"ideas.microBitHeader": "Have a micro:bit?",
"ideas.microBitBody": "Connect your Scratch project to the real world.",
"ideas.makeyMakeyHeader": "Have a MakeyMakey?",
"ideas.makeyMakeyBody": "Turn anything into a key that connects with your Scratch project!",
"ideas.desktopEditorHeader": "Scratch App Download",
"ideas.desktopEditorBody": "To create projects without an Internet connection, you can <a href=\"/download\">download the Scratch app</a>.",
"ideas.desktopEditorBodyHTML": "To create projects without an Internet connection, you can <a>download the Scratch app</a>.",

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB