Merge branch 'develop' into bugfix/popupstyle
This commit is contained in:
commit
c98fe59bf0
5 changed files with 90 additions and 35 deletions
|
@ -3,3 +3,21 @@ import '@babel/polyfill';
|
||||||
// See: https://github.com/testing-library/jest-dom
|
// See: https://github.com/testing-library/jest-dom
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import '@testing-library/jest-dom';
|
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;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
|
|
||||||
|
@ -72,7 +74,7 @@ const getConsoleFeedStyle = (theme, times, fontSize) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Console = () => {
|
const Console = ({ t }) => {
|
||||||
const consoleEvents = useSelector(state => state.console);
|
const consoleEvents = useSelector(state => state.console);
|
||||||
const isExpanded = useSelector(state => state.ide.consoleIsExpanded);
|
const isExpanded = useSelector(state => state.ide.consoleIsExpanded);
|
||||||
const { theme, fontSize } = useSelector(state => state.preferences);
|
const { theme, fontSize } = useSelector(state => state.preferences);
|
||||||
|
@ -98,19 +100,19 @@ const Console = () => {
|
||||||
return (
|
return (
|
||||||
<section className={consoleClass} >
|
<section className={consoleClass} >
|
||||||
<header className="preview-console__header">
|
<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">
|
<div className="preview-console__header-buttons">
|
||||||
<button className="preview-console__clear" onClick={clearConsole} aria-label="Clear console">
|
<button className="preview-console__clear" onClick={clearConsole} aria-label={t('Console.ClearARIA')}>
|
||||||
Clear
|
{t('Console.Clear')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="preview-console__collapse"
|
className="preview-console__collapse"
|
||||||
onClick={collapseConsole}
|
onClick={collapseConsole}
|
||||||
aria-label="Close console"
|
aria-label={t('Console.CloseARIA')}
|
||||||
>
|
>
|
||||||
<DownArrowIcon focusable="false" aria-hidden="true" />
|
<DownArrowIcon focusable="false" aria-hidden="true" />
|
||||||
</button>
|
</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" />
|
<UpArrowIcon focusable="false" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -140,5 +142,9 @@ const Console = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Console.propTypes = {
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default Console;
|
|
||||||
|
export default withTranslation()(Console);
|
||||||
|
|
|
@ -3,6 +3,8 @@ import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { withTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import * as IDEActions from '../actions/ide';
|
import * as IDEActions from '../actions/ide';
|
||||||
import * as FileActions from '../actions/files';
|
import * as FileActions from '../actions/files';
|
||||||
import DownArrowIcon from '../../../images/down-filled-triangle.svg';
|
import DownArrowIcon from '../../../images/down-filled-triangle.svg';
|
||||||
|
@ -152,7 +154,9 @@ export class FileNode extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickDelete = () => {
|
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.setState({ isDeleting: true });
|
||||||
this.props.resetSelectedFile(this.props.id);
|
this.props.resetSelectedFile(this.props.id);
|
||||||
setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100);
|
setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100);
|
||||||
|
@ -237,6 +241,8 @@ export class FileNode extends React.Component {
|
||||||
const isFolder = this.props.fileType === 'folder';
|
const isFolder = this.props.fileType === 'folder';
|
||||||
const isRoot = this.props.name === 'root';
|
const isRoot = this.props.name === 'root';
|
||||||
|
|
||||||
|
const { t } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={itemClass} >
|
<div className={itemClass} >
|
||||||
{ !isRoot &&
|
{ !isRoot &&
|
||||||
|
@ -252,14 +258,14 @@ export class FileNode extends React.Component {
|
||||||
<button
|
<button
|
||||||
className="sidebar__file-item-closed"
|
className="sidebar__file-item-closed"
|
||||||
onClick={this.showFolderChildren}
|
onClick={this.showFolderChildren}
|
||||||
aria-label="Open folder contents"
|
aria-label={t('FileNode.OpenFolderARIA')}
|
||||||
>
|
>
|
||||||
<FolderRightIcon className="folder-right" focusable="false" aria-hidden="true" />
|
<FolderRightIcon className="folder-right" focusable="false" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="sidebar__file-item-open"
|
className="sidebar__file-item-open"
|
||||||
onClick={this.hideFolderChildren}
|
onClick={this.hideFolderChildren}
|
||||||
aria-label="Close file contents"
|
aria-label={t('FileNode.CloseFolderARIA')}
|
||||||
>
|
>
|
||||||
<FolderDownIcon className="folder-down" focusable="false" aria-hidden="true" />
|
<FolderDownIcon className="folder-down" focusable="false" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -286,7 +292,7 @@ export class FileNode extends React.Component {
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="sidebar__file-item-show-options"
|
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; }}
|
ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }}
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
onClick={this.toggleFileOptions}
|
onClick={this.toggleFileOptions}
|
||||||
|
@ -301,35 +307,35 @@ export class FileNode extends React.Component {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="add folder"
|
aria-label={t('FileNode.AddFolderARIA')}
|
||||||
onClick={this.handleClickAddFolder}
|
onClick={this.handleClickAddFolder}
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
className="sidebar__file-item-option"
|
className="sidebar__file-item-option"
|
||||||
>
|
>
|
||||||
Create folder
|
{t('FileNode.AddFolder')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="add file"
|
aria-label={t('FileNode.AddFileARIA')}
|
||||||
onClick={this.handleClickAddFile}
|
onClick={this.handleClickAddFile}
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
className="sidebar__file-item-option"
|
className="sidebar__file-item-option"
|
||||||
>
|
>
|
||||||
Create file
|
{t('FileNode.AddFile')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{ this.props.authenticated &&
|
{ this.props.authenticated &&
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="upload file"
|
aria-label={t('FileNode.UploadFileARIA')}
|
||||||
onClick={this.handleClickUploadFile}
|
onClick={this.handleClickUploadFile}
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
>
|
>
|
||||||
Upload file
|
{t('FileNode.UploadFile')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@ -342,7 +348,7 @@ export class FileNode extends React.Component {
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
className="sidebar__file-item-option"
|
className="sidebar__file-item-option"
|
||||||
>
|
>
|
||||||
Rename
|
{t('FileNode.Rename')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -352,7 +358,7 @@ export class FileNode extends React.Component {
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
className="sidebar__file-item-option"
|
className="sidebar__file-item-option"
|
||||||
>
|
>
|
||||||
Delete
|
{t('FileNode.Delete')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -388,6 +394,7 @@ FileNode.propTypes = {
|
||||||
canEdit: PropTypes.bool.isRequired,
|
canEdit: PropTypes.bool.isRequired,
|
||||||
openUploadFileModal: PropTypes.func.isRequired,
|
openUploadFileModal: PropTypes.func.isRequired,
|
||||||
authenticated: PropTypes.bool.isRequired,
|
authenticated: PropTypes.bool.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
onClickFile: PropTypes.func
|
onClickFile: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -408,5 +415,8 @@ function mapDispatchToProps(dispatch) {
|
||||||
return bindActionCreators(Object.assign(FileActions, IDEActions), 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;
|
export default ConnectedFileNode;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { withTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import ConnectedFileNode from './FileNode';
|
import ConnectedFileNode from './FileNode';
|
||||||
|
|
||||||
import DownArrowIcon from '../../../images/down-filled-triangle.svg';
|
import DownArrowIcon from '../../../images/down-filled-triangle.svg';
|
||||||
|
@ -71,11 +73,11 @@ class Sidebar extends React.Component {
|
||||||
<section className={sidebarClass}>
|
<section className={sidebarClass}>
|
||||||
<header className="sidebar__header" onContextMenu={this.toggleProjectOptions}>
|
<header className="sidebar__header" onContextMenu={this.toggleProjectOptions}>
|
||||||
<h3 className="sidebar__title">
|
<h3 className="sidebar__title">
|
||||||
<span>Sketch Files</span>
|
<span>{this.props.t('Sidebar.Title')}</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div className="sidebar__icons">
|
<div className="sidebar__icons">
|
||||||
<button
|
<button
|
||||||
aria-label="Toggle open/close sketch file options"
|
aria-label={this.props.t('Sidebar.ToggleARIA')}
|
||||||
className="sidebar__add"
|
className="sidebar__add"
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
ref={(element) => { this.sidebarOptions = element; }}
|
ref={(element) => { this.sidebarOptions = element; }}
|
||||||
|
@ -88,7 +90,7 @@ class Sidebar extends React.Component {
|
||||||
<ul className="sidebar__project-options">
|
<ul className="sidebar__project-options">
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="add folder"
|
aria-label={this.props.t('Sidebar.AddFolderARIA')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.newFolder(rootFile.id);
|
this.props.newFolder(rootFile.id);
|
||||||
setTimeout(this.props.closeProjectOptions, 0);
|
setTimeout(this.props.closeProjectOptions, 0);
|
||||||
|
@ -96,12 +98,12 @@ class Sidebar extends React.Component {
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
>
|
>
|
||||||
Create folder
|
{this.props.t('Sidebar.AddFolder')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="add file"
|
aria-label={this.props.t('Sidebar.AddFileARIA')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.newFile(rootFile.id);
|
this.props.newFile(rootFile.id);
|
||||||
setTimeout(this.props.closeProjectOptions, 0);
|
setTimeout(this.props.closeProjectOptions, 0);
|
||||||
|
@ -109,14 +111,14 @@ class Sidebar extends React.Component {
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
>
|
>
|
||||||
Create file
|
{this.props.t('Sidebar.AddFile')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{
|
{
|
||||||
this.props.user.authenticated &&
|
this.props.user.authenticated &&
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="upload file"
|
aria-label={this.props.t('Sidebar.UploadFileARIA')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.openUploadFileModal(rootFile.id);
|
this.props.openUploadFileModal(rootFile.id);
|
||||||
setTimeout(this.props.closeProjectOptions, 0);
|
setTimeout(this.props.closeProjectOptions, 0);
|
||||||
|
@ -124,7 +126,7 @@ class Sidebar extends React.Component {
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
onFocus={this.onFocusComponent}
|
onFocus={this.onFocusComponent}
|
||||||
>
|
>
|
||||||
Upload file
|
{this.props.t('Sidebar.UploadFile')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@ -159,11 +161,12 @@ Sidebar.propTypes = {
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
authenticated: PropTypes.bool.isRequired
|
authenticated: PropTypes.bool.isRequired
|
||||||
}).isRequired
|
}).isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
Sidebar.defaultProps = {
|
Sidebar.defaultProps = {
|
||||||
owner: undefined
|
owner: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Sidebar;
|
export default withTranslation()(Sidebar);
|
||||||
|
|
|
@ -174,15 +174,33 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Sidebar": {
|
"Sidebar": {
|
||||||
"Create": "Create",
|
"Title": "Sketch Files",
|
||||||
"EnterName": "enter a name",
|
"ToggleARIA": "Toggle open/close sketch file options",
|
||||||
"Add": "Add",
|
"AddFolder": "Create folder",
|
||||||
"Folder": "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": {
|
"Common": {
|
||||||
"Error": "Error",
|
"Error": "Error",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"p5logoARIA": "p5.js Logo"
|
"p5logoARIA": "p5.js Logo",
|
||||||
|
"DeleteConfirmation": "Are you sure you want to delete {{name}}?"
|
||||||
},
|
},
|
||||||
"IDEView": {
|
"IDEView": {
|
||||||
"SubmitFeedback": "Submit Feedback"
|
"SubmitFeedback": "Submit Feedback"
|
||||||
|
|
Loading…
Reference in a new issue