Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions client/jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,21 @@ import '@babel/polyfill';
// See: https://github.com/testing-library/jest-dom
// eslint-disable-next-line import/no-extraneous-dependencies
import '@testing-library/jest-dom';

import lodash from 'lodash';

// For testing, we use en-US and provide a mock implementation
// of t() that finds the correct translation
import translations from '../translations/locales/en-US/translations.json';

// This function name needs to be prefixed with "mock" so that Jest doesn't
// complain that it's out-of-scope in the mock below
const mockTranslate = key => lodash.get(translations, key);

jest.mock('react-i18next', () => ({
// this mock makes sure any components using the translate HoC receive the t function as a prop
withTranslation: () => (Component) => {
Component.defaultProps = { ...Component.defaultProps, t: mockTranslate };
return Component;
},
}));
20 changes: 13 additions & 7 deletions client/modules/IDE/components/Console.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';

import { bindActionCreators } from 'redux';

Expand Down Expand Up @@ -72,7 +74,7 @@ const getConsoleFeedStyle = (theme, times, fontSize) => {
}
};

const Console = () => {
const Console = ({ t }) => {
const consoleEvents = useSelector(state => state.console);
const isExpanded = useSelector(state => state.ide.consoleIsExpanded);
const { theme, fontSize } = useSelector(state => state.preferences);
Expand All @@ -96,19 +98,19 @@ const Console = () => {
return (
<section className={consoleClass} >
<header className="preview-console__header">
<h2 className="preview-console__header-title">Console</h2>
<h2 className="preview-console__header-title">{t('Console.Title')}</h2>
<div className="preview-console__header-buttons">
<button className="preview-console__clear" onClick={clearConsole} aria-label="Clear console">
Clear
<button className="preview-console__clear" onClick={clearConsole} aria-label={t('Console.ClearARIA')}>
{t('Console.Clear')}
</button>
<button
className="preview-console__collapse"
onClick={collapseConsole}
aria-label="Close console"
aria-label={t('Console.CloseARIA')}
>
<DownArrowIcon focusable="false" aria-hidden="true" />
</button>
<button className="preview-console__expand" onClick={expandConsole} aria-label="Open console" >
<button className="preview-console__expand" onClick={expandConsole} aria-label={t('Console.OpenARIA')} >
<UpArrowIcon focusable="false" aria-hidden="true" />
</button>
</div>
Expand Down Expand Up @@ -138,5 +140,9 @@ const Console = () => {
);
};

Console.propTypes = {
t: PropTypes.func.isRequired,
};


export default Console;
export default withTranslation()(Console);
38 changes: 24 additions & 14 deletions client/modules/IDE/components/FileNode.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import classNames from 'classnames';
import { withTranslation } from 'react-i18next';

import * as IDEActions from '../actions/ide';
import * as FileActions from '../actions/files';
import DownArrowIcon from '../../../images/down-filled-triangle.svg';
Expand Down Expand Up @@ -147,7 +149,9 @@ export class FileNode extends React.Component {
}

handleClickDelete = () => {
if (window.confirm(`Are you sure you want to delete ${this.props.name}?`)) {
const prompt = this.props.t('Common.DeleteConfirmation', { name: this.props.name });

if (window.confirm(prompt)) {
this.setState({ isDeleting: true });
this.props.resetSelectedFile(this.props.id);
setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100);
Expand Down Expand Up @@ -232,6 +236,8 @@ export class FileNode extends React.Component {
const isFolder = this.props.fileType === 'folder';
const isRoot = this.props.name === 'root';

const { t } = this.props;

return (
<div className={itemClass}>
{ !isRoot &&
Expand All @@ -247,14 +253,14 @@ export class FileNode extends React.Component {
<button
className="sidebar__file-item-closed"
onClick={this.showFolderChildren}
aria-label="Open folder contents"
aria-label={t('FileNode.OpenFolderARIA')}
>
<FolderRightIcon className="folder-right" focusable="false" aria-hidden="true" />
</button>
<button
className="sidebar__file-item-open"
onClick={this.hideFolderChildren}
aria-label="Close file contents"
aria-label={t('FileNode.CloseFolderARIA')}
>
<FolderDownIcon className="folder-down" focusable="false" aria-hidden="true" />
</button>
Expand All @@ -281,7 +287,7 @@ export class FileNode extends React.Component {
/>
<button
className="sidebar__file-item-show-options"
aria-label="Toggle open/close file options"
aria-label={t('FileNode.ToggleFileOptionsARIA')}
ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }}
tabIndex="0"
onClick={this.toggleFileOptions}
Expand All @@ -296,35 +302,35 @@ export class FileNode extends React.Component {
<React.Fragment>
<li>
<button
aria-label="add folder"
aria-label={t('FileNode.AddFolderARIA')}
onClick={this.handleClickAddFolder}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Create folder
{t('FileNode.AddFolder')}
</button>
</li>
<li>
<button
aria-label="add file"
aria-label={t('FileNode.AddFileARIA')}
onClick={this.handleClickAddFile}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Create file
{t('FileNode.AddFile')}
</button>
</li>
{ this.props.authenticated &&
<li>
<button
aria-label="upload file"
aria-label={t('FileNode.UploadFileARIA')}
onClick={this.handleClickUploadFile}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
Upload file
{t('FileNode.UploadFile')}
</button>
</li>
}
Expand All @@ -337,7 +343,7 @@ export class FileNode extends React.Component {
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Rename
{t('FileNode.Rename')}
</button>
</li>
<li>
Expand All @@ -347,7 +353,7 @@ export class FileNode extends React.Component {
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Delete
{t('FileNode.Delete')}
</button>
</li>
</ul>
Expand Down Expand Up @@ -382,7 +388,8 @@ FileNode.propTypes = {
hideFolderChildren: PropTypes.func.isRequired,
canEdit: PropTypes.bool.isRequired,
openUploadFileModal: PropTypes.func.isRequired,
authenticated: PropTypes.bool.isRequired
authenticated: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,
};

FileNode.defaultProps = {
Expand All @@ -401,5 +408,8 @@ function mapDispatchToProps(dispatch) {
return bindActionCreators(Object.assign(FileActions, IDEActions), dispatch);
}

const ConnectedFileNode = connect(mapStateToProps, mapDispatchToProps)(FileNode);
const TranslatedFileNode = withTranslation()(FileNode);

const ConnectedFileNode = connect(mapStateToProps, mapDispatchToProps)(TranslatedFileNode);

export default ConnectedFileNode;
23 changes: 13 additions & 10 deletions client/modules/IDE/components/Sidebar.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import PropTypes from 'prop-types';
import React from 'react';
import classNames from 'classnames';
import { withTranslation } from 'react-i18next';

import ConnectedFileNode from './FileNode';

import DownArrowIcon from '../../../images/down-filled-triangle.svg';
Expand Down Expand Up @@ -71,11 +73,11 @@ class Sidebar extends React.Component {
<section className={sidebarClass}>
<header className="sidebar__header" onContextMenu={this.toggleProjectOptions}>
<h3 className="sidebar__title">
<span>Sketch Files</span>
<span>{this.props.t('Sidebar.Title')}</span>
</h3>
<div className="sidebar__icons">
<button
aria-label="Toggle open/close sketch file options"
aria-label={this.props.t('Sidebar.ToggleARIA')}
className="sidebar__add"
tabIndex="0"
ref={(element) => { this.sidebarOptions = element; }}
Expand All @@ -88,43 +90,43 @@ class Sidebar extends React.Component {
<ul className="sidebar__project-options">
<li>
<button
aria-label="add folder"
aria-label={this.props.t('Sidebar.AddFolderARIA')}
onClick={() => {
this.props.newFolder(rootFile.id);
setTimeout(this.props.closeProjectOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
Create folder
{this.props.t('Sidebar.AddFolder')}
</button>
</li>
<li>
<button
aria-label="add file"
aria-label={this.props.t('Sidebar.AddFileARIA')}
onClick={() => {
this.props.newFile(rootFile.id);
setTimeout(this.props.closeProjectOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
Create file
{this.props.t('Sidebar.AddFile')}
</button>
</li>
{
this.props.user.authenticated &&
<li>
<button
aria-label="upload file"
aria-label={this.props.t('Sidebar.UploadFileARIA')}
onClick={() => {
this.props.openUploadFileModal(rootFile.id);
setTimeout(this.props.closeProjectOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
Upload file
{this.props.t('Sidebar.UploadFile')}
</button>
</li>
}
Expand Down Expand Up @@ -159,11 +161,12 @@ Sidebar.propTypes = {
user: PropTypes.shape({
id: PropTypes.string,
authenticated: PropTypes.bool.isRequired
}).isRequired
}).isRequired,
t: PropTypes.func.isRequired,
};

Sidebar.defaultProps = {
owner: undefined
};

export default Sidebar;
export default withTranslation()(Sidebar);
28 changes: 23 additions & 5 deletions translations/locales/en-US/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,33 @@
}
},
"Sidebar": {
"Create": "Create",
"EnterName": "enter a name",
"Add": "Add",
"Folder": "Folder"
"Title": "Sketch Files",
"ToggleARIA": "Toggle open/close sketch file options",
"AddFolder": "Create folder",
"AddFolderARIA": "add folder",
"AddFile": "Create file",
"AddFileARIA": "add file",
"UploadFile": "Upload file",
"UploadFileARIA": "upload file"
},
"FileNode": {
"OpenFolderARIA": "Open folder contents",
"CloseFolderARIA": "Close folder contents",
"ToggleFileOptionsARIA": "Toggle open/close file options",
"AddFolder": "Create folder",
"AddFolderARIA": "add folder",
"AddFile": "Create file",
"AddFileARIA": "add file",
"UploadFile": "Upload file",
"UploadFileARIA": "upload file",
"Rename": "Rename",
"Delete": "Delete"
},
"Common": {
"Error": "Error",
"Save": "Save",
"p5logoARIA": "p5.js Logo"
"p5logoARIA": "p5.js Logo",
"DeleteConfirmation": "Are you sure you want to delete {{name}}?"
},
"IDEView": {
"SubmitFeedback": "Submit Feedback"
Expand Down