diff --git a/src/components/asset-button/asset-button.css b/src/components/asset-button/asset-button.css
new file mode 100644
index 000000000..b36fa76b8
--- /dev/null
+++ b/src/components/asset-button/asset-button.css
@@ -0,0 +1,21 @@
+@import "../../css/colors.css";
+
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ background: $motion-primary;
+ border-radius: 100%;
+ width: 2.75rem;
+ height: 2.75rem;
+ outline: none;
+ border: none;
+}
+
+.icon {
+ width: 1.75rem;
+ height: 1.75rem;
+}
diff --git a/src/components/asset-button/asset-button.jsx b/src/components/asset-button/asset-button.jsx
new file mode 100644
index 000000000..7cd6cb6e6
--- /dev/null
+++ b/src/components/asset-button/asset-button.jsx
@@ -0,0 +1,31 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import classNames from 'classnames';
+import styles from './asset-button.css';
+
+const AssetButton = ({
+ img,
+ className,
+ title,
+ onClick
+}) => (
+
+);
+
+AssetButton.propTypes = {
+ className: PropTypes.string,
+ img: PropTypes.string,
+ onClick: PropTypes.func.isRequired,
+ title: PropTypes.node.isRequired
+};
+
+export default AssetButton;
diff --git a/src/components/asset-panel/icon--add-backdrop-lib.svg b/src/components/asset-panel/icon--add-backdrop-lib.svg
index 8c80f17aa..2294afd72 100644
--- a/src/components/asset-panel/icon--add-backdrop-lib.svg
+++ b/src/components/asset-panel/icon--add-backdrop-lib.svg
@@ -1,31 +1,21 @@
-
-
diff --git a/src/components/asset-panel/icon--add-blank-costume.svg b/src/components/asset-panel/icon--add-blank-costume.svg
index 20843fe61..1a5014698 100644
--- a/src/components/asset-panel/icon--add-blank-costume.svg
+++ b/src/components/asset-panel/icon--add-blank-costume.svg
@@ -1,18 +1,12 @@
-
-
-
- add sprite
+
+
+
+ paint
Created with Sketch.
-
-
-
-
-
-
-
-
-
+
+
+
-
\ No newline at end of file
+
diff --git a/src/components/asset-panel/icon--add-costume-lib.svg b/src/components/asset-panel/icon--add-costume-lib.svg
index cb364d3b0..5e30024a2 100644
--- a/src/components/asset-panel/icon--add-costume-lib.svg
+++ b/src/components/asset-panel/icon--add-costume-lib.svg
@@ -1,30 +1,12 @@
-
-
-
- add-sprite
+
+
+
+ sprite-library
Created with Sketch.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
\ No newline at end of file
+
diff --git a/src/components/asset-panel/icon--add-sound-lib.svg b/src/components/asset-panel/icon--add-sound-lib.svg
index 1293940a9..b279dc52c 100644
--- a/src/components/asset-panel/icon--add-sound-lib.svg
+++ b/src/components/asset-panel/icon--add-sound-lib.svg
@@ -1,26 +1,12 @@
-
-
-
- sound icon
+
+
+
+ sound-library
Created with Sketch.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
\ No newline at end of file
+
diff --git a/src/components/asset-panel/icon--add-sound-record.svg b/src/components/asset-panel/icon--add-sound-record.svg
index b82e7a412..9feb24cfd 100644
--- a/src/components/asset-panel/icon--add-sound-record.svg
+++ b/src/components/asset-panel/icon--add-sound-record.svg
@@ -1,25 +1,12 @@
-
-
-
- record icon
+
+
+
+ record
Created with Sketch.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
\ No newline at end of file
+
diff --git a/src/components/asset-panel/selector.css b/src/components/asset-panel/selector.css
index 546a7863d..2a19296d6 100644
--- a/src/components/asset-panel/selector.css
+++ b/src/components/asset-panel/selector.css
@@ -15,30 +15,13 @@
flex-direction: column;
align-items: center;
justify-content: space-around;
- margin: 1.25rem 0;
+ margin: 0.75rem 0;
color: $motion-primary;
text-align: center;
}
-.new-button {
- display: flex;
- flex-direction: column;
- align-items: center;
- font-size: 0.75rem;
- cursor: pointer;
- transition: 0.2s;
-}
-
-.new-button:hover {
- transform: scale(1.2);
-}
-
-.new-button + .new-button {
- margin-top: 1.25rem;
-}
-
-.new-button-icon + .new-button-label {
- margin-top: 0.5rem;
+.new-buttons > button + button {
+ margin-top: 0.75rem;
}
.list-area {
diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx
index c6eec7f78..012206517 100644
--- a/src/components/asset-panel/selector.jsx
+++ b/src/components/asset-panel/selector.jsx
@@ -4,7 +4,7 @@ import React from 'react';
import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
import Box from '../box/box.jsx';
-import IconButton from '../icon-button/icon-button.jsx';
+import AssetButton from '../asset-button/asset-button.jsx';
import styles from './selector.css';
const Selector = props => {
@@ -35,7 +35,7 @@ const Selector = props => {
{buttons.map(({message, img, onClick}, index) => (
- {
Selector.propTypes = {
buttons: PropTypes.arrayOf(PropTypes.shape({
- message: PropTypes.node.isRequired,
+ message: PropTypes.string.isRequired,
img: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired
})),
diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css
index 2c0533e98..92b83acc2 100644
--- a/src/components/gui/gui.css
+++ b/src/components/gui/gui.css
@@ -150,16 +150,26 @@
bottom: 0;
left: 0;
z-index: 50; /** Force extension button above the ScratchBlocks flyout. */
- background: white;
+ background: $motion-primary;
- border: 1px solid #ddd;
+ border: 1px solid $motion-primary;
box-sizing: content-box; /* To match scratch-block vertical toolbox borders */
}
.extension-button {
- font-size: 0.55rem;
- font-weight: bold;
+ background: none;
+ border: none;
+ outline: none;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
}
+
+.extension-button-icon {
+ width: 1.75rem;
+ height: 1.75rem;
+}
+
.extension-button > div {
margin-top: 0;
}
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index d50ea4577..33b13e454 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -1,9 +1,9 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
+import {defineMessages, injectIntl, intlShape} from 'react-intl';
import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
import MediaQuery from 'react-responsive';
-import {FormattedMessage} from 'react-intl';
import tabStyles from 'react-tabs/style/react-tabs.css';
import VM from 'scratch-vm';
import Renderer from 'scratch-render';
@@ -17,7 +17,6 @@ import Stage from '../../containers/stage.jsx';
import Box from '../box/box.jsx';
import FeedbackForm from '../feedback-form/feedback-form.jsx';
-import IconButton from '../icon-button/icon-button.jsx';
import MenuBar from '../menu-bar/menu-bar.jsx';
import PreviewModal from '../../containers/preview-modal.jsx';
import WebGlModal from '../../containers/webgl-modal.jsx';
@@ -26,19 +25,20 @@ import layout from '../../lib/layout-constants.js';
import styles from './gui.css';
import addExtensionIcon from './icon--extensions.svg';
-const addExtensionMessage = (
-
-);
+const messages = defineMessages({
+ addExtension: {
+ id: 'gui.gui.addExtension',
+ description: 'Button to add an extension in the target pane',
+ defaultMessage: 'Add Extension'
+ }
+});
const GUIComponent = props => {
const {
basePath,
children,
enableExtensions,
+ intl,
feedbackFormVisible,
vm,
previewInfoVisible,
@@ -108,14 +108,18 @@ const GUIComponent = props => {
/>
-
+ >
+
+
@@ -161,6 +165,7 @@ GUIComponent.propTypes = {
children: PropTypes.node,
enableExtensions: PropTypes.bool,
feedbackFormVisible: PropTypes.bool,
+ intl: intlShape.isRequired,
onExtensionButtonClick: PropTypes.func,
onTabSelect: PropTypes.func,
previewInfoVisible: PropTypes.bool,
@@ -170,4 +175,4 @@ GUIComponent.propTypes = {
GUIComponent.defaultProps = {
basePath: './'
};
-export default GUIComponent;
+export default injectIntl(GUIComponent);
diff --git a/src/components/gui/icon--extensions.svg b/src/components/gui/icon--extensions.svg
index de96e820c..5d50761c0 100644
--- a/src/components/gui/icon--extensions.svg
+++ b/src/components/gui/icon--extensions.svg
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/src/components/sprite-selector/icon--sprite.svg b/src/components/sprite-selector/icon--sprite.svg
index cb364d3b0..12c4a8b36 100644
--- a/src/components/sprite-selector/icon--sprite.svg
+++ b/src/components/sprite-selector/icon--sprite.svg
@@ -1,30 +1,12 @@
-
-
-
- add-sprite
+
+
+
+ sprite-library
Created with Sketch.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/components/sprite-selector/sprite-selector.css b/src/components/sprite-selector/sprite-selector.css
index 8c1e2315a..67b0532d3 100644
--- a/src/components/sprite-selector/sprite-selector.css
+++ b/src/components/sprite-selector/sprite-selector.css
@@ -61,8 +61,6 @@
}
.add-button {
- font-size: 0.55rem;
- font-weight: bold;
position: absolute;
bottom: 0.75rem;
right: 1rem;
diff --git a/src/components/sprite-selector/sprite-selector.jsx b/src/components/sprite-selector/sprite-selector.jsx
index 7196176ae..41e51a316 100644
--- a/src/components/sprite-selector/sprite-selector.jsx
+++ b/src/components/sprite-selector/sprite-selector.jsx
@@ -1,25 +1,26 @@
import PropTypes from 'prop-types';
import React from 'react';
-import {FormattedMessage} from 'react-intl';
+import {defineMessages, injectIntl, intlShape} from 'react-intl';
import Box from '../box/box.jsx';
import SpriteInfo from '../../containers/sprite-info.jsx';
import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
-import IconButton from '../icon-button/icon-button.jsx';
+import AssetButton from '../asset-button/asset-button.jsx';
import styles from './sprite-selector.css';
import spriteIcon from './icon--sprite.svg';
-const addSpriteMessage = (
-
-);
+const messages = defineMessages({
+ addSprite: {
+ id: 'gui.spriteSelector.addSprite',
+ description: 'Button to add a sprite in the target pane',
+ defaultMessage: 'Add Sprite'
+ }
+});
const SpriteSelectorComponent = function (props) {
const {
+ intl,
onChangeSpriteDirection,
onChangeSpriteName,
onChangeSpriteSize,
@@ -84,10 +85,10 @@ const SpriteSelectorComponent = function (props) {
}
-
@@ -95,6 +96,7 @@ const SpriteSelectorComponent = function (props) {
};
SpriteSelectorComponent.propTypes = {
+ intl: intlShape.isRequired,
onChangeSpriteDirection: PropTypes.func,
onChangeSpriteName: PropTypes.func,
onChangeSpriteSize: PropTypes.func,
@@ -121,4 +123,4 @@ SpriteSelectorComponent.propTypes = {
})
};
-export default SpriteSelectorComponent;
+export default injectIntl(SpriteSelectorComponent);
diff --git a/src/components/stage-selector/icon--backdrop.svg b/src/components/stage-selector/icon--backdrop.svg
index 8c80f17aa..12fa6c7ab 100644
--- a/src/components/stage-selector/icon--backdrop.svg
+++ b/src/components/stage-selector/icon--backdrop.svg
@@ -1,30 +1,20 @@
-
-
-
- add-bg
+
+
+
+ backdrop-library
Created with Sketch.
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/components/stage-selector/stage-selector.css b/src/components/stage-selector/stage-selector.css
index 06dff7210..a6fe0b042 100644
--- a/src/components/stage-selector/stage-selector.css
+++ b/src/components/stage-selector/stage-selector.css
@@ -76,10 +76,6 @@ $header-height: calc($stage-menu-height - 2px);
}
.add-button {
- font-size: 0.55rem;
- font-weight: bold;
- text-align: center;
position: absolute;
bottom: 0.75rem;
- left: 0.25rem;
}
diff --git a/src/components/stage-selector/stage-selector.jsx b/src/components/stage-selector/stage-selector.jsx
index 5ffad63e5..eea811409 100644
--- a/src/components/stage-selector/stage-selector.jsx
+++ b/src/components/stage-selector/stage-selector.jsx
@@ -1,25 +1,26 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
-import {FormattedMessage} from 'react-intl';
+import {defineMessages, intlShape, injectIntl, FormattedMessage} from 'react-intl';
import Box from '../box/box.jsx';
-import IconButton from '../icon-button/icon-button.jsx';
+import AssetButton from '../asset-button/asset-button.jsx';
import CostumeCanvas from '../costume-canvas/costume-canvas.jsx';
import styles from './stage-selector.css';
import backdropIcon from './icon--backdrop.svg';
-const addBackdropMessage = (
-
-);
+const messages = defineMessages({
+ addBackdrop: {
+ id: 'gui.stageSelector.targetPaneAddBackdrop',
+ description: 'Button to add a backdrop in the target pane',
+ defaultMessage: 'Add Backdrop'
+ }
+});
const StageSelector = props => {
const {
backdropCount,
+ intl,
selected,
url,
onClick,
@@ -53,10 +54,10 @@ const StageSelector = props => {
/>
{backdropCount}
-
@@ -65,9 +66,10 @@ const StageSelector = props => {
StageSelector.propTypes = {
backdropCount: PropTypes.number.isRequired,
+ intl: intlShape.isRequired,
onClick: PropTypes.func,
onNewBackdropClick: PropTypes.func,
selected: PropTypes.bool.isRequired,
url: PropTypes.string
};
-export default StageSelector;
+export default injectIntl(StageSelector);
diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx
index a142ff912..be4c554fe 100644
--- a/src/containers/costume-tab.jsx
+++ b/src/containers/costume-tab.jsx
@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import bindAll from 'lodash.bindall';
-import {FormattedMessage} from 'react-intl';
+import {defineMessages, intlShape, injectIntl} from 'react-intl';
import VM from 'scratch-vm';
import AssetPanel from '../components/asset-panel/asset-panel.jsx';
@@ -22,36 +22,28 @@ import addLibraryBackdropIcon from '../components/asset-panel/icon--add-backdrop
import addLibraryCostumeIcon from '../components/asset-panel/icon--add-costume-lib.svg';
import costumeLibraryContent from '../lib/libraries/costumes.json';
-const messages = {
- addLibraryBackdropMsg: (
-
- ),
- addLibraryCostumeMsg: (
-
- ),
- addBlankBackdropMsg: (
-
- ),
- addBlankCostumeMsg: (
-
- )
-};
+const messages = defineMessages({
+ addLibraryBackdropMsg: {
+ defaultMessage: 'Add Backdrop From Library',
+ description: 'Button to add a backdrop in the editor tab',
+ id: 'gui.costumeTab.addBackdrop'
+ },
+ addLibraryCostumeMsg: {
+ defaultMessage: 'Add Costume From Library',
+ description: 'Button to add a costume in the editor tab',
+ id: 'gui.costumeTab.addCostume'
+ },
+ addBlankBackdropMsg: {
+ defaultMessage: 'Add Blank Backdrop',
+ description: 'Button to add a blank backdrop in the editor tab',
+ id: 'gui.costumeTab.addBlankBackdrop'
+ },
+ addBlankCostumeMsg: {
+ defaultMessage: 'Add Blank Costume',
+ description: 'Button to add a blank costume in the editor tab',
+ id: 'gui.costumeTab.addBlankCostume'
+ }
+});
class CostumeTab extends React.Component {
constructor (props) {
@@ -108,6 +100,7 @@ class CostumeTab extends React.Component {
render () {
// For paint wrapper
const {
+ intl,
onNewLibraryBackdropClick,
onNewLibraryCostumeClick,
costumeLibraryVisible,
@@ -139,12 +132,12 @@ class CostumeTab extends React.Component {
({
}
});
-export default connect(
+export default injectIntl(connect(
mapStateToProps,
mapDispatchToProps
-)(CostumeTab);
+)(CostumeTab));
diff --git a/src/containers/sound-tab.jsx b/src/containers/sound-tab.jsx
index 01ddbe539..9e30ba2ed 100644
--- a/src/containers/sound-tab.jsx
+++ b/src/containers/sound-tab.jsx
@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import bindAll from 'lodash.bindall';
-import {FormattedMessage} from 'react-intl';
+import {defineMessages, intlShape, injectIntl} from 'react-intl';
import VM from 'scratch-vm';
import AssetPanel from '../components/asset-panel/asset-panel.jsx';
@@ -64,6 +64,7 @@ class SoundTab extends React.Component {
render () {
const {
+ intl,
vm,
onNewSoundFromLibraryClick,
onNewSoundFromRecordingClick
@@ -82,29 +83,27 @@ class SoundTab extends React.Component {
}
)) : [];
- const recordSoundMsg = (
-
- );
- const addSoundMsg = (
-
- );
+ const messages = defineMessages({
+ recordSound: {
+ defaultMessage: 'Record Sound',
+ description: 'Button to record a sound in the editor tab',
+ id: 'gui.soundTab.recordSound'
+ },
+ addSound: {
+ defaultMessage: 'Add Sound',
+ description: 'Button to add a sound in the editor tab',
+ id: 'gui.soundTab.addSound'
+ }
+ });
return (
({
}
});
-export default connect(
+export default injectIntl(connect(
mapStateToProps,
mapDispatchToProps
-)(SoundTab);
+)(SoundTab));
diff --git a/test/integration/costumes.test.js b/test/integration/costumes.test.js
index b19576bf2..98f31629a 100644
--- a/test/integration/costumes.test.js
+++ b/test/integration/costumes.test.js
@@ -27,7 +27,7 @@ describe('Working with costumes', () => {
await loadUri(uri);
await clickXpath('//button[@title="tryit"]');
await clickText('Costumes');
- await clickText('Add Costume');
+ await clickXpath('//button[@title="Add Costume"]');
const el = await findByXpath("//input[@placeholder='what are you looking for?']");
await el.sendKeys('abb');
await clickText('Abby-a'); // Should close the modal, then click the costumes in the selector
@@ -39,7 +39,7 @@ describe('Working with costumes', () => {
test('Adding a backdrop', async () => {
await loadUri(uri);
await clickXpath('//button[@title="tryit"]');
- await clickText('Add Backdrop');
+ await clickXpath('//button[@title="Add Backdrop"]');
const el = await findByXpath("//input[@placeholder='what are you looking for?']");
await el.sendKeys('blue');
await clickText('Blue Sky'); // Should close the modal
diff --git a/test/integration/localization.test.js b/test/integration/localization.test.js
index b99066a35..7c90b3221 100644
--- a/test/integration/localization.test.js
+++ b/test/integration/localization.test.js
@@ -30,7 +30,7 @@ describe('Localization', () => {
await loadUri(uri);
await clickXpath('//button[@title="tryit"]');
await clickText('Blocks');
- await clickText('Extensions');
+ await clickXpath('//button[@title="Add Extension"]');
await clickText('Pen', scope.modal); // Modal closes
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
await clickText('English');
diff --git a/test/integration/sounds.test.js b/test/integration/sounds.test.js
index 475d22719..244ebb9b4 100644
--- a/test/integration/sounds.test.js
+++ b/test/integration/sounds.test.js
@@ -37,13 +37,13 @@ describe('Working with sounds', () => {
.accept();
// Add it back
- await clickText('Add Sound');
+ await clickXpath('//button[@title="Add Sound"]');
let el = await findByXpath("//input[@placeholder='what are you looking for?']");
await el.sendKeys('meow');
await clickText('Meow', scope.modal); // Should close the modal
// Add a new sound
- await clickText('Add Sound');
+ await clickXpath('//button[@title="Add Sound"]');
el = await findByXpath("//input[@placeholder='what are you looking for?']");
await el.sendKeys('chom');
await clickText('Chomp'); // Should close the modal, then click the sounds in the selector
@@ -70,11 +70,11 @@ describe('Working with sounds', () => {
// Add a sound so this sprite has 2 sounds.
await clickText('Sounds');
- await clickText('Add Sound');
+ await clickXpath('//button[@title="Add Sound"]');
await clickText('A Bass'); // Closes the modal
// Now add a sprite with only one sound.
- await clickText('Add Sprite');
+ await clickXpath('//button[@title="Add Sprite"]');
await clickText('Abby'); // Doing this used to crash the editor.
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for error