From 8dc26d8d307ff0fd71d142f2dfde649368edd5f2 Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Wed, 20 Dec 2017 14:49:27 -0500 Subject: [PATCH] Partially Implement GH-830: Add coming soon tooltips (#1069) * Implement GH-830: Add coming soon tooltips This implements coming soon tooltips for: * the small stage size * save, load and language It also creates the component that can be copied over into the paint editor for use there as well. Lastly, this does a small refactor on the full screen implementation to include multiple small screen sizes too. --- package.json | 1 + src/components/button/button.jsx | 34 +-- src/components/coming-soon/aww-cat.png | Bin 0 -> 4467 bytes src/components/coming-soon/coming-soon.css | 68 ++++++ src/components/coming-soon/coming-soon.jsx | 143 ++++++++++++ src/components/coming-soon/cool-cat.png | Bin 0 -> 3829 bytes src/components/gui/gui.css | 8 - src/components/gui/gui.jsx | 8 +- .../language-selector/language-icon.svg | 90 ++------ .../language-selector/language-selector.css | 5 +- .../language-selector/language-selector.jsx | 49 +++-- src/components/load-button/load-button.jsx | 26 ++- src/components/menu-bar/menu-bar.css | 10 +- .../prompt/icon--dropdown-caret.svg | 12 ++ src/components/prompt/prompt.css | 26 +++ src/components/prompt/prompt.jsx | 32 ++- .../stage-header/icon--fullscreen.svg | 17 ++ .../stage-header/icon--large-stage.svg | 16 ++ .../stage-header/icon--small-stage.svg | 16 ++ .../stage-header/icon--unfullscreen.svg | 17 ++ src/components/stage-header/icon--unzoom.svg | 89 -------- src/components/stage-header/icon--zoom.svg | 89 -------- src/components/stage-header/stage-header.css | 45 +++- src/components/stage-header/stage-header.jsx | 204 ++++++++++++------ src/components/stage/stage.jsx | 14 +- src/containers/save-button.jsx | 18 +- src/containers/stage-header.jsx | 17 +- src/containers/stage.jsx | 6 +- src/css/colors.css | 2 + src/reducers/gui.js | 4 +- src/reducers/stage-size.js | 55 +++++ src/reducers/zoom.js | 23 -- test/integration/test.js | 6 +- 33 files changed, 711 insertions(+), 439 deletions(-) create mode 100644 src/components/coming-soon/aww-cat.png create mode 100644 src/components/coming-soon/coming-soon.css create mode 100644 src/components/coming-soon/coming-soon.jsx create mode 100644 src/components/coming-soon/cool-cat.png create mode 100644 src/components/prompt/icon--dropdown-caret.svg create mode 100644 src/components/stage-header/icon--fullscreen.svg create mode 100644 src/components/stage-header/icon--large-stage.svg create mode 100644 src/components/stage-header/icon--small-stage.svg create mode 100644 src/components/stage-header/icon--unfullscreen.svg delete mode 100644 src/components/stage-header/icon--unzoom.svg delete mode 100644 src/components/stage-header/icon--zoom.svg create mode 100644 src/reducers/stage-size.js delete mode 100644 src/reducers/zoom.js diff --git a/package.json b/package.json index 778cc2eea..50369a1f7 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "react-style-proptype": "3.1.0", "react-tabs": "2.1.1", "react-test-renderer": "16.2.0", + "react-tooltip": "3.4.0", "redux": "3.7.0", "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", diff --git a/src/components/button/button.jsx b/src/components/button/button.jsx index ae322d10f..59b39668f 100644 --- a/src/components/button/button.jsx +++ b/src/components/button/button.jsx @@ -9,23 +9,31 @@ const ButtonComponent = ({ onClick, children, ...props -}) => ( - - {children} - -); +}) => { + if (props.disabled === true) { + onClick = function () {}; + } + + return ( + + {children} + + ); +}; ButtonComponent.propTypes = { children: PropTypes.node, className: PropTypes.string, + disabled: PropTypes.bool, onClick: PropTypes.func.isRequired }; + export default ButtonComponent; diff --git a/src/components/coming-soon/aww-cat.png b/src/components/coming-soon/aww-cat.png new file mode 100644 index 0000000000000000000000000000000000000000..bddfb8de9ee95ccef600bcb6a67ce5ed3c5ed4d5 GIT binary patch literal 4467 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 + ) + }} + /> + ); + } + render () { + return ( + + ); + } +} + +ComingSoonContent.propTypes = { + className: PropTypes.string, + intl: intlShape, + place: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), + tooltipId: PropTypes.string.isRequired +}; + +ComingSoonContent.defaultProps = { + place: 'bottom' +}; + +const ComingSoon = injectIntl(ComingSoonContent); + +const ComingSoonTooltip = props => ( +
+
+ {props.children} +
+ +
+); + +ComingSoonTooltip.propTypes = { + children: PropTypes.node.isRequired, + className: PropTypes.string, + delayHide: PropTypes.number, + delayShow: PropTypes.number, + place: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), + tooltipClassName: PropTypes.string, + tooltipId: PropTypes.string.isRequired +}; + +ComingSoonTooltip.defaultProps = { + delayHide: 0, + delayShow: 0 +}; + +export { + ComingSoon as ComingSoonComponent, + ComingSoonTooltip +}; diff --git a/src/components/coming-soon/cool-cat.png b/src/components/coming-soon/cool-cat.png new file mode 100644 index 0000000000000000000000000000000000000000..15b2151e35bd84cfa154e6af8fd20607ca4d5e9e GIT binary patch literal 3829 zcmVyNHMgQ@$W?$+Vr5SsdCqX3)CM$V}T zPIJdnGF*;IOd)6y^t-j>0H9DH@QT*a03g&W!8I-(fuMUFGr@QgoCriqA}w#iptO5Z zWo8e4O2V$7*g>5WOu$>cgU-?YoC)M?k_B_MAH?2zu~%G2es0kVj#q<#E}51YFQgcQ zK)i8it#autfS5tLjV77|Ei8?Ig!&|yR3$J*kU?ZZ0SGO@$;na+XRK%uDrW+3p8_}r z-r_B4A}QYCms5RyDf(qow)ZSlJ0gIyg4x6|;nDnA@WPA)oxNoCityA)cJ$Oh$pN4k zK;49qhr@vaf{Y9R)F24NP&S)I21GD>W`pRS&x9_NELGH8yr~K&ssh1L3xa z<7NRkAF`ZhngB2&1DvrE#L26HGuYLaQ#pn>{G(izZx!LWLiy!92-=V`4g*8IY9ENM zdXOt-K>C|QV9H+7fvM0(C6Q|sor$>7q@Z(8)yQtU45@S`cTcF84&uZm1q6vHo&-mP zcuu>=T_N{iO;-Of!BvYOFeE8|FBh0E9)WD%x!iA~QG~7t2fZ=5YPc*kRu@prOUJ$7 z@PSyp7$2l_8W%L7@dvNd3|FA(ku@Ug?1AV=_&@>zLIHy@Pjh1o$VP%W_aPpO6@JtJ zpaudcmY*?YcEcB$dgyM8Lw8hWoHMO!K8}Ap71Vj~xp}M}xdHw^v5dFphXL_9QRm6dJ zlSNNdiy+LNjQtvpf#Jc=LlNZ}jblmrEyE{YWB*Em_AY4@V${QA$8@-MLKqxE1bJQU ze0kqX;j4}TC^Ri7HmSvo|BB)$oo1;olTl(q8h^{GeUdf|24!_3iXw)(PVe z)L!*_JRW~26e7i~9J327UbGN20OIjDM3F(e-3}9~oM1AU8KdOaiqJCCUY6zPEYlom zQM;y4uxK<2j*5yw=b~ryz1!^utJSKbtq88r&eJEFdivp>9&jMQ7K??^yW@`A;hAUt zP@*~jCnn)ZgD%$ua5}3PgA|%1486oeUf>imp3w)-oK=wZkfsK@1JO?)z(kt>usiHb z`!a&cXl9UhI+L465Yp^9$7#V;x%P#_%nFKiH-4lSK0JDiU6Y9f^F6cK3^ud?gVCVm zd1OK~7S6~Gi z4YpPYg^2g&F#~|nGoS?!9yK7bU?!spW>_%H_ark>iIr<=Nt-i!xxfvmQ&98RGcem5 zWu3-!Ps=FDWP;Jm(MOjezn9g8f-J)o?xl@2Ue&fzm0|2Ipr()@F{lM1iqeGuq}esi z(qaa?R3NaVW&s+TFcVM~SUdeZK;#_jG}h7^(THIzNG4NP%t7F2bn*q!m0isQ!Qs+h z(Ew1ZtLh0-t|j7%hLSlDh+%e3rCIPJ>(HXLUT_TvBgPPFizbC~^tG`8!AYS1#$$1I z&CS8)N#%m9LOYwIt=GPmnu+I<1VNTg>e?E$TuTh;nPcXk&TEOrnM|HhJs89orKlCP z7r|pq=jpve-!jm0+U=dLujQ*5sDHzgZ zq9~{Vh*SL@RAwaR26_OfS526pFn9g=D?!G42iqrN25_)qh{X!Dx_o9SoTIQV$b(E7 zig+cSOJV`q2Ilg*grY$#P&5}HKh67UTIUk!g1Ue@6+h%gccP$r?dz?8p3yrrhhhbZ z0akD&-MmXk3_RlT#woqSZoZD%1tif93xDu4W};NYkSplpfZjayU1P2SizhRFJ8OCk ztX(o?$a&})y+iL>QImO~2cJg|0GU4VMm#RL^Oub0lDR|}N7kkJ{=*M!WV{m3wu^rmQMM85#m9bV^~I;$ z*JP-g1(PS$K*QYY;Jsr3mhQ<^wZ#Xg?t9U1Vx|I_B2Ci$wQJYFmPeZ5__5>g{N5KJ zo#h}dUIS6{Jl1I(iKXDsp~Fz^@xX=+>seI!^wTz$Hc6{mrAPCUnQMz5=31Ddt!5KE z`rvw)IkOJZScztm{b25TWAAVO77t#v1;_@6!T_!7CwXDl=7-_X2Oq-W)1O0oXZN7X zQCx|^B3wwVU9$$qZg%gpGiR8&_VipHa<2D3_y88-ceIdMzI-|SkVP6CC3AtGwwXz$Ox00bEnnZz-GgH9UUF|C2K(=|Gx3Y@902u!-o6TvmV2xZ;)1{WCE!;STaeNYRB3n z-mjF~MhlgItaG$AW||0BbvXLnRjZx2@L7jz_!ZYqpV;(yup{T|nlHL59N?Ky3A5{F z!lIiRV8)zz!|r*}a`)bQ*5@`e3N}~N&5xJ<{32U!54$;wmh|C~x8akv&me@>)YTg) zD26CD)cL${=G?_SxCB3l*<}B}-+UunGyo|4IzbdSB9kYPSn&P_AJ{a(>GYH4tyIcp zQV{J8!}<0L(Aj+%y2HJ2>h#Cp!e#8;ci#oK-15C4nLm5>3wWgY5$MNdu?8eDZbN_G zv2zFd@Qfk9ef{-+!M=SjLw8pf%$qk4+|CMAfD@)onal#SY%oDAoyAf*20fR~2(=55cX^W-@ybg#BaM7XLJGaHrYn+gyPfr>JhAlZdQxDuX`3+<-KHM{EM*k<|VLXiMkCh(J_#^?|`PC zK?CT+tkKsOfkZgC>w5o+=8~KXe02I_zr}1m;lOpegH>t~xNzYDv}5Pg1ux6R=-Ukq zH-Xn%+!-K&K#hbircwNO_ucm(iLrw17pQaBPMr! zud}n0Wrwb=Zs_jrX0^uBo0r1W+S=gag$t&Y>}2THTh^?rvhzFV%wOue?z&m9Z22&1SVUTCFw=T2?@}D5%bgX{5=HOC5tg^VWEavYY{jHEhk$ zk7<$uYkw@>`qLl%_%?KrCJcUi-fDd_T(Yx4j0HY}K|F!^+-b33MzC7Ij9FnUmQadD znor<5ffi1&Se!rz;~If{KUge{QE^U$`JSVtc*}?;8`M*s(Z+}2{G8HkT0I1zl3Ce{NJ6?l6ijip z+lonfB?Um;aBzkezS_cBCvG;GP5?tHr=gl4Szvh)hMHCz{5VnXIc{Kn8L1bHI%7F%5d3Inzhb1{6^#2qA z`QtThON}-@H2`S0Ijx=yQAR>=wvE=`#|;3joj=Zie?@h4?a2gmfOiwr|EwVd;{Zv>i@y< r613dPt=!73+{&%o%57l#j{pMz(BhkzaVpG600000NkvXXu0mjf$GTEh literal 0 HcmV?d00001 diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css index 3d08cadf6..2c0533e98 100644 --- a/src/components/gui/gui.css +++ b/src/components/gui/gui.css @@ -143,14 +143,6 @@ overflow: hidden; } -.stage-menu-wrapper { - display: flex; - flex-shrink: 0; - align-items: center; - height: $stage-menu-height; - padding: $space; -} - .extension-button-container { width: 3.25rem; height: 3.25rem; diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 349e20d5e..639c8cbd3 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -112,13 +112,7 @@ const GUIComponent = props => { - {isFullSize => ( - - )} + {isFullSize => ( diff --git a/src/components/language-selector/language-icon.svg b/src/components/language-selector/language-icon.svg index 3644ab6fd..42bd409ab 100644 --- a/src/components/language-selector/language-icon.svg +++ b/src/components/language-selector/language-icon.svg @@ -1,72 +1,18 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Language + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/language-selector/language-selector.css b/src/components/language-selector/language-selector.css index a060131a8..0ae9e0a10 100644 --- a/src/components/language-selector/language-selector.css +++ b/src/components/language-selector/language-selector.css @@ -1,3 +1,4 @@ +@import "../../css/colors.css"; @import "../../css/units.css"; .group { @@ -9,7 +10,7 @@ .language-icon { height: 2rem; - display:none; + display: none; } .language-select { @@ -19,7 +20,7 @@ user-select: none; outline: none; background: rgba(255, 255, 255, 0.5); - color: #3373cc; + color: $motion-tertiary; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } diff --git a/src/components/language-selector/language-selector.jsx b/src/components/language-selector/language-selector.jsx index d3d3bb48d..db846fef5 100644 --- a/src/components/language-selector/language-selector.jsx +++ b/src/components/language-selector/language-selector.jsx @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import Box from '../box/box.jsx'; +import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import locales from 'scratch-l10n'; import languageIcon from './language-icon.svg'; import styles from './language-selector.css'; @@ -12,27 +13,33 @@ const LanguageSelector = ({ ...props }) => ( -
- - -
+ +
+ + +
+
); diff --git a/src/components/load-button/load-button.jsx b/src/components/load-button/load-button.jsx index dd94a2a11..0f580278c 100644 --- a/src/components/load-button/load-button.jsx +++ b/src/components/load-button/load-button.jsx @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import ButtonComponent from '../button/button.jsx'; +import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; import styles from './load-button.css'; @@ -13,13 +14,24 @@ const LoadButtonComponent = ({ ...props }) => ( - {title} - + + + {title} + + + ); diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css index a9d3b37fa..a85926d39 100644 --- a/src/components/menu-bar/menu-bar.css +++ b/src/components/menu-bar/menu-bar.css @@ -1,3 +1,4 @@ +@import "../../css/colors.css"; @import "../../css/units.css"; .menu-bar { @@ -23,7 +24,7 @@ width: 100%; */ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - background-color: #4c97ff; + background-color: $motion-primary; color: white; } @@ -56,11 +57,6 @@ line-height: $menu-bar-height; cursor: pointer; text-decoration: none; - color: white; + color: $motion-tertiary; user-select: none; } - -.menu-item:hover { - opacity: 0.8; - background: rgba(255, 255, 255, 0.2) -} diff --git a/src/components/prompt/icon--dropdown-caret.svg b/src/components/prompt/icon--dropdown-caret.svg new file mode 100644 index 000000000..8e08eda35 --- /dev/null +++ b/src/components/prompt/icon--dropdown-caret.svg @@ -0,0 +1,12 @@ + + + + dropdown-caret-gray + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/components/prompt/prompt.css b/src/components/prompt/prompt.css index 94b907c04..527901233 100644 --- a/src/components/prompt/prompt.css +++ b/src/components/prompt/prompt.css @@ -49,3 +49,29 @@ .button-row button + button { margin-left: 0.5rem; } + +.more-options { + border-top: 1px dashed hsla(0, 0%, 0%, .25); + overflow: visible; + padding: 1rem; + text-align: center; + margin: 0 0 1rem; +} + +.more-options-accordion { + width: 60%; + margin: 0 auto; +} + +.more-options-text { + opacity: .5; +} + +.more-options-icon { + width: .75rem; + height: .75rem; + margin-left: .5rem; + vertical-align: middle; + padding-bottom: .2rem; + opacity: .5; +} diff --git a/src/components/prompt/prompt.jsx b/src/components/prompt/prompt.jsx index 4fca4cb4a..b99580b79 100644 --- a/src/components/prompt/prompt.jsx +++ b/src/components/prompt/prompt.jsx @@ -1,10 +1,23 @@ +import {defineMessages, FormattedMessage} from 'react-intl'; import PropTypes from 'prop-types'; import React from 'react'; -import Modal from '../modal/modal.jsx'; + import Box from '../box/box.jsx'; +import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; +import Modal from '../modal/modal.jsx'; import styles from './prompt.css'; +import dropdownIcon from './icon--dropdown-caret.svg'; + +const messages = defineMessages({ + moreOptionsMessage: { + defaultMessage: 'More Options', + description: 'Dropdown message for variable/list options', + id: 'gui.gui.variablePrompt' + } +}); + const PromptComponent = props => ( ( onKeyPress={props.onKeyPress} />
+ + +
+ + +
+
+
+
- - ) : ( - - - - + ); + } else { + header = ( + + + +
+
+ +
+ {props.intl.formatMessage(messages.smallStageSizeMessage)} +
+
+
+ +
+
+
+ +
+
+
-
- ); + ); + } + + return header; }; + StageHeaderComponent.propTypes = { - className: PropTypes.string, - height: PropTypes.number, - isZoomed: PropTypes.bool.isRequired, - onUnzoom: PropTypes.func.isRequired, - onZoom: PropTypes.func.isRequired, - titleZoomIcon: PropTypes.string, - vm: PropTypes.instanceOf(VM).isRequired, - width: PropTypes.number + intl: intlShape, + isFullScreen: PropTypes.bool.isRequired, + onSetStageFull: PropTypes.func.isRequired, + onSetStageLarge: PropTypes.func.isRequired, + onSetStageUnFull: PropTypes.func.isRequired, + stageSize: PropTypes.oneOf(Object.keys(STAGE_SIZES)).isRequired, + vm: PropTypes.instanceOf(VM).isRequired }; -StageHeaderComponent.defaultProps = { - width: 480, - height: 360, - titleZoomIcon: 'Zoom Control' -}; -export default StageHeaderComponent; + +export default injectIntl(StageHeaderComponent); diff --git a/src/components/stage/stage.jsx b/src/components/stage/stage.jsx index 47981e04a..c8777a263 100644 --- a/src/components/stage/stage.jsx +++ b/src/components/stage/stage.jsx @@ -13,7 +13,7 @@ const StageComponent = props => { canvasRef, height, isColorPicking, - isZoomed, + isFullScreen, width, colorInfo, onDeactivateColorPicker, @@ -26,7 +26,7 @@ const StageComponent = props => { let widthCorrectedAspect = width; const spacingBorderAdjustment = 9; const stageMenuHeightAdjustment = 40; - if (isZoomed) { + if (isFullScreen) { heightCorrectedAspect = window.innerHeight - stageMenuHeightAdjustment - spacingBorderAdjustment; widthCorrectedAspect = heightCorrectedAspect + (heightCorrectedAspect / 3); if (widthCorrectedAspect > window.innerWidth) { @@ -38,15 +38,15 @@ const StageComponent = props => {
- Save - + + Save + + ); } } diff --git a/src/containers/stage-header.jsx b/src/containers/stage-header.jsx index d3acba94e..593851a41 100644 --- a/src/containers/stage-header.jsx +++ b/src/containers/stage-header.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import VM from 'scratch-vm'; -import {setZoomed} from '../reducers/zoom'; +import {setStageSize, setFullScreen, STAGE_SIZES} from '../reducers/stage-size'; import {connect} from 'react-redux'; @@ -22,19 +22,20 @@ class StageHeader extends React.Component { } StageHeader.propTypes = { - height: PropTypes.number, - isZoomed: PropTypes.bool, - vm: PropTypes.instanceOf(VM).isRequired, - width: PropTypes.number + stageSize: PropTypes.oneOf(Object.keys(STAGE_SIZES)), + vm: PropTypes.instanceOf(VM).isRequired }; const mapStateToProps = state => ({ - isZoomed: state.isZoomed + stageSize: state.stageSize.stageSize, + isFullScreen: state.stageSize.isFullScreen }); const mapDispatchToProps = dispatch => ({ - onZoom: () => dispatch(setZoomed(true)), - onUnzoom: () => dispatch(setZoomed(false)) + onSetStageLarge: () => dispatch(setStageSize(STAGE_SIZES.large)), + onSetStageSmall: () => dispatch(setStageSize(STAGE_SIZES.small)), + onSetStageFull: () => dispatch(setFullScreen(true)), + onSetStageUnFull: () => dispatch(setFullScreen(false)) }); export default connect( diff --git a/src/containers/stage.jsx b/src/containers/stage.jsx index 9f76aa7ab..cb2a38963 100644 --- a/src/containers/stage.jsx +++ b/src/containers/stage.jsx @@ -57,7 +57,7 @@ class Stage extends React.Component { this.props.height !== nextProps.height || this.props.isColorPicking !== nextProps.isColorPicking || this.state.colorInfo !== nextState.colorInfo || - this.props.isZoomed !== nextProps.isZoomed || + this.props.isFullScreen !== nextProps.isFullScreen || this.state.question !== nextState.question; } componentDidUpdate (prevProps) { @@ -279,7 +279,7 @@ class Stage extends React.Component { Stage.propTypes = { height: PropTypes.number, isColorPicking: PropTypes.bool, - isZoomed: PropTypes.bool, + isFullScreen: PropTypes.bool.isRequired, onActivateColorPicker: PropTypes.func, onDeactivateColorPicker: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired, @@ -288,7 +288,7 @@ Stage.propTypes = { const mapStateToProps = state => ({ isColorPicking: state.colorPicker.active, - isZoomed: state.isZoomed + isFullScreen: state.stageSize.isFullScreen }); const mapDispatchToProps = dispatch => ({ diff --git a/src/css/colors.css b/src/css/colors.css index d524795cd..e270217f9 100644 --- a/src/css/colors.css +++ b/src/css/colors.css @@ -16,4 +16,6 @@ $sound-tertiary: #A63FA6; $control-primary: #FFAB19; +$data-primary: #FF8C1A; + $form-border: #E9EEF2; diff --git a/src/reducers/gui.js b/src/reducers/gui.js index 83ede0203..d49144b4f 100644 --- a/src/reducers/gui.js +++ b/src/reducers/gui.js @@ -8,14 +8,14 @@ import monitorLayoutReducer from './monitor-layout'; import targetReducer from './targets'; import toolboxReducer from './toolbox'; import vmReducer from './vm'; -import zoomReducer from './zoom'; +import stageSizeReducer from './stage-size'; import {ScratchPaintReducer} from 'scratch-paint'; export default combineReducers({ colorPicker: colorPickerReducer, customProcedures: customProceduresReducer, intl: intlReducer, - isZoomed: zoomReducer, + stageSize: stageSizeReducer, modals: modalReducer, monitors: monitorReducer, monitorLayout: monitorLayoutReducer, diff --git a/src/reducers/stage-size.js b/src/reducers/stage-size.js new file mode 100644 index 000000000..ea2750e34 --- /dev/null +++ b/src/reducers/stage-size.js @@ -0,0 +1,55 @@ +const SET_STAGE_SIZE = 'scratch-gui/StageSize/SET_STAGE_SIZE'; +const SET_FULL_SCREEN = 'scratch-gui/StageSize/SET_FULL_SCREEN'; + +const initialState = { + isFullScreen: false, + stageSize: 'large' +}; + +// stage size constants +const STAGE_SIZES = { + small: 'small', + large: 'large' +}; + +const reducer = function (state, action) { + if (typeof state === 'undefined') state = initialState; + switch (action.type) { + case SET_STAGE_SIZE: + return { + isFullScreen: state.isFullScreen, + stageSize: action.stageSize + }; + case SET_FULL_SCREEN: + return { + isFullScreen: action.isFullScreen, + stageSize: state.stageSize + }; + default: + return state; + } +}; + +const setStageSize = function (stageSize) { + return { + type: SET_STAGE_SIZE, + stageSize: stageSize + }; +}; + +// `isFullScreen` is a separate value because "stage size" does not +// actually apply to full screen mode, so they are treated as separate +// values to be assessed. +const setFullScreen = function (isFullScreen) { + return { + type: SET_FULL_SCREEN, + isFullScreen: isFullScreen + }; +}; + +export { + reducer as default, + setStageSize, + setFullScreen, + STAGE_SIZES +}; diff --git a/src/reducers/zoom.js b/src/reducers/zoom.js deleted file mode 100644 index 3cd0f5f7f..000000000 --- a/src/reducers/zoom.js +++ /dev/null @@ -1,23 +0,0 @@ -const SET_ZOOMED = 'scratch-gui/Zoomed/SET_ZOOMED'; -const defaultZoomed = false; -const initialState = defaultZoomed; - -const reducer = function (state, action) { - if (typeof state === 'undefined') state = initialState; - switch (action.type) { - case SET_ZOOMED: - return action.isZoomed; - default: - return state; - } -}; -const setZoomed = function (isZoomed) { - return { - type: SET_ZOOMED, - isZoomed: isZoomed - }; -}; -export { - reducer as default, - setZoomed -}; diff --git a/test/integration/test.js b/test/integration/test.js index 58b488ecd..8668fce2a 100644 --- a/test/integration/test.js +++ b/test/integration/test.js @@ -143,7 +143,7 @@ describe('costumes, sounds and variables', () => { const projectId = '96708228'; await loadUri(`${uri}#${projectId}`); await new Promise(resolve => setTimeout(resolve, 2000)); - await clickXpath('//img[@title="Zoom Control"]'); + await clickXpath('//img[@title="Full Screen Control"]'); await clickXpath('//img[@title="Go"]'); await new Promise(resolve => setTimeout(resolve, 2000)); await clickXpath('//img[@title="Stop"]'); @@ -235,7 +235,9 @@ describe('costumes, sounds and variables', () => { await expect(logs).toEqual([]); }); - test('Localization', async () => { + // Skipped temporarily while the language selector is marked as + // "Coming Soon" + test.skip('Localization', async () => { await loadUri(uri); await clickText('Blocks'); await clickText('Extensions');