Skip to content

Commit 5edee7e

Browse files
committed
Merge branch 'develop' into chore/authentication-improvements
2 parents 69ea487 + 7dbcde4 commit 5edee7e

File tree

15 files changed

+275
-428
lines changed

15 files changed

+275
-428
lines changed

client/common/icons.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import Exit from '../images/exit.svg';
1212
import DropdownArrow from '../images/down-filled-triangle.svg';
1313
import Preferences from '../images/preferences.svg';
1414
import Play from '../images/triangle-arrow-right.svg';
15+
import Code from '../images/code.svg';
16+
import Terminal from '../images/terminal.svg';
17+
1518

1619
// HOC that adds the right web accessibility props
1720
// https://www.scottohara.me/blog/2019/05/22/contextual-images-svgs-and-a11y.html
@@ -74,3 +77,4 @@ export const ExitIcon = withLabel(Exit);
7477
export const DropdownArrowIcon = withLabel(DropdownArrow);
7578
export const PreferencesIcon = withLabel(Preferences);
7679
export const PlayIcon = withLabel(Play);
80+
export const TerminalIcon = withLabel(Terminal);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import { bindActionCreators } from 'redux';
4+
import { useDispatch, useSelector } from 'react-redux';
5+
import { remSize } from '../../theme';
6+
import IconButton from './IconButton';
7+
import { TerminalIcon } from '../../common/icons';
8+
import * as IDEActions from '../../modules/IDE/actions/ide';
9+
10+
const BottomBarContent = styled.h2`
11+
padding: ${remSize(8)};
12+
13+
svg {
14+
max-height: ${remSize(32)};
15+
}
16+
`;
17+
18+
export default () => {
19+
const { expandConsole, collapseConsole } = bindActionCreators(IDEActions, useDispatch());
20+
const { consoleIsExpanded } = useSelector(state => state.ide);
21+
22+
const actions = [{ icon: TerminalIcon, aria: 'Say Something', action: consoleIsExpanded ? collapseConsole : expandConsole }];
23+
24+
return (
25+
<BottomBarContent>
26+
{actions.map(({ icon, aria, action }) =>
27+
(<IconButton
28+
icon={icon}
29+
aria-label={aria}
30+
key={`bottom-bar-${aria}`}
31+
onClick={() => action()}
32+
/>))}
33+
</BottomBarContent>
34+
);
35+
};

client/components/mobile/Footer.jsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import React from 'react';
22
import styled from 'styled-components';
3-
import { prop, remSize } from '../../theme';
3+
import { prop, grays } from '../../theme';
4+
45

56
const background = prop('MobilePanel.default.background');
67
const textColor = prop('primaryTextColor');
78

8-
const Footer = styled.div`
9+
export default styled.div`
910
position: fixed;
1011
width: 100%;
12+
bottom: 0;
1113
background: ${background};
1214
color: ${textColor};
13-
padding: ${remSize(12)};
14-
padding-left: ${remSize(32)};
15-
z-index: 1;
1615
17-
bottom: 0;
16+
& > * + * { border-top: dashed 1px ${prop('Separator')} }
1817
`;
19-
20-
export default Footer;

client/components/mobile/Header.jsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import styled from 'styled-components';
33
import PropTypes from 'prop-types';
44
import { prop, remSize } from '../../theme';
55

6-
const background = prop('MobilePanel.default.background');
6+
const background = transparent => prop(transparent ? 'backgroundColor' : 'MobilePanel.default.background');
77
const textColor = prop('primaryTextColor');
88

99

1010
const HeaderDiv = styled.div`
1111
position: fixed;
1212
width: 100%;
13-
background: ${props => (props.transparent ? 'transparent' : background)};
13+
background: ${props => background(props.transparent === true)};
1414
color: ${textColor};
1515
padding: ${remSize(12)};
1616
padding-left: ${remSize(16)};
@@ -23,7 +23,6 @@ const HeaderDiv = styled.div`
2323
justify-content: flex-start;
2424
align-items: center;
2525
26-
// TODO:
2726
svg {
2827
max-height: ${remSize(32)};
2928
padding: ${remSize(4)}

client/images/terminal.svg

Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 111 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import PropTypes from 'prop-types';
2-
import React from 'react';
1+
import React, { useRef } from 'react';
2+
3+
import { bindActionCreators } from 'redux';
4+
5+
import { useSelector, useDispatch } from 'react-redux';
36
import classNames from 'classnames';
47
import { Console as ConsoleFeed } from 'console-feed';
58
import {
@@ -22,132 +25,120 @@ import infoContrastUrl from '../../../images/console-info-contrast.svg?byUrl';
2225
import UpArrowIcon from '../../../images/up-arrow.svg';
2326
import DownArrowIcon from '../../../images/down-arrow.svg';
2427

25-
class Console extends React.Component {
26-
componentDidUpdate(prevProps) {
27-
this.consoleMessages.scrollTop = this.consoleMessages.scrollHeight;
28-
if (this.props.theme !== prevProps.theme) {
29-
this.props.clearConsole();
30-
this.props.dispatchConsoleEvent(this.props.consoleEvents);
31-
}
28+
import * as IDEActions from '../../IDE/actions/ide';
29+
import * as ConsoleActions from '../../IDE/actions/console';
30+
import { useDidUpdate } from '../../../utils/custom-hooks';
3231

33-
if (this.props.fontSize !== prevProps.fontSize) {
34-
this.props.clearConsole();
35-
this.props.dispatchConsoleEvent(this.props.consoleEvents);
36-
}
32+
const getConsoleFeedStyle = (theme, times, fontSize) => {
33+
const style = {};
34+
const CONSOLE_FEED_LIGHT_ICONS = {
35+
LOG_WARN_ICON: `url(${warnLightUrl})`,
36+
LOG_ERROR_ICON: `url(${errorLightUrl})`,
37+
LOG_DEBUG_ICON: `url(${debugLightUrl})`,
38+
LOG_INFO_ICON: `url(${infoLightUrl})`
39+
};
40+
const CONSOLE_FEED_DARK_ICONS = {
41+
LOG_WARN_ICON: `url(${warnDarkUrl})`,
42+
LOG_ERROR_ICON: `url(${errorDarkUrl})`,
43+
LOG_DEBUG_ICON: `url(${debugDarkUrl})`,
44+
LOG_INFO_ICON: `url(${infoDarkUrl})`
45+
};
46+
const CONSOLE_FEED_CONTRAST_ICONS = {
47+
LOG_WARN_ICON: `url(${warnContrastUrl})`,
48+
LOG_ERROR_ICON: `url(${errorContrastUrl})`,
49+
LOG_DEBUG_ICON: `url(${debugContrastUrl})`,
50+
LOG_INFO_ICON: `url(${infoContrastUrl})`
51+
};
52+
const CONSOLE_FEED_SIZES = {
53+
TREENODE_LINE_HEIGHT: 1.2,
54+
BASE_FONT_SIZE: fontSize,
55+
ARROW_FONT_SIZE: fontSize,
56+
LOG_ICON_WIDTH: fontSize,
57+
LOG_ICON_HEIGHT: 1.45 * fontSize,
58+
};
59+
60+
if (times > 1) {
61+
Object.assign(style, CONSOLE_FEED_WITHOUT_ICONS);
62+
}
63+
switch (theme) {
64+
case 'light':
65+
return Object.assign(CONSOLE_FEED_LIGHT_STYLES, CONSOLE_FEED_LIGHT_ICONS, CONSOLE_FEED_SIZES, style);
66+
case 'dark':
67+
return Object.assign(CONSOLE_FEED_DARK_STYLES, CONSOLE_FEED_DARK_ICONS, CONSOLE_FEED_SIZES, style);
68+
case 'contrast':
69+
return Object.assign(CONSOLE_FEED_CONTRAST_STYLES, CONSOLE_FEED_CONTRAST_ICONS, CONSOLE_FEED_SIZES, style);
70+
default:
71+
return '';
3772
}
73+
};
3874

39-
getConsoleFeedStyle(theme, times) {
40-
const style = {};
41-
const CONSOLE_FEED_LIGHT_ICONS = {
42-
LOG_WARN_ICON: `url(${warnLightUrl})`,
43-
LOG_ERROR_ICON: `url(${errorLightUrl})`,
44-
LOG_DEBUG_ICON: `url(${debugLightUrl})`,
45-
LOG_INFO_ICON: `url(${infoLightUrl})`
46-
};
47-
const CONSOLE_FEED_DARK_ICONS = {
48-
LOG_WARN_ICON: `url(${warnDarkUrl})`,
49-
LOG_ERROR_ICON: `url(${errorDarkUrl})`,
50-
LOG_DEBUG_ICON: `url(${debugDarkUrl})`,
51-
LOG_INFO_ICON: `url(${infoDarkUrl})`
52-
};
53-
const CONSOLE_FEED_CONTRAST_ICONS = {
54-
LOG_WARN_ICON: `url(${warnContrastUrl})`,
55-
LOG_ERROR_ICON: `url(${errorContrastUrl})`,
56-
LOG_DEBUG_ICON: `url(${debugContrastUrl})`,
57-
LOG_INFO_ICON: `url(${infoContrastUrl})`
58-
};
59-
const CONSOLE_FEED_SIZES = {
60-
TREENODE_LINE_HEIGHT: 1.2,
61-
BASE_FONT_SIZE: this.props.fontSize,
62-
ARROW_FONT_SIZE: this.props.fontSize,
63-
LOG_ICON_WIDTH: this.props.fontSize,
64-
LOG_ICON_HEIGHT: 1.45 * this.props.fontSize,
65-
};
75+
const Console = () => {
76+
const consoleEvents = useSelector(state => state.console);
77+
const isExpanded = useSelector(state => state.ide.consoleIsExpanded);
78+
const { theme, fontSize } = useSelector(state => state.preferences);
6679

67-
if (times > 1) {
68-
Object.assign(style, CONSOLE_FEED_WITHOUT_ICONS);
69-
}
70-
switch (theme) {
71-
case 'light':
72-
return Object.assign(CONSOLE_FEED_LIGHT_STYLES, CONSOLE_FEED_LIGHT_ICONS, CONSOLE_FEED_SIZES, style);
73-
case 'dark':
74-
return Object.assign(CONSOLE_FEED_DARK_STYLES, CONSOLE_FEED_DARK_ICONS, CONSOLE_FEED_SIZES, style);
75-
case 'contrast':
76-
return Object.assign(CONSOLE_FEED_CONTRAST_STYLES, CONSOLE_FEED_CONTRAST_ICONS, CONSOLE_FEED_SIZES, style);
77-
default:
78-
return '';
79-
}
80-
}
80+
const {
81+
collapseConsole, expandConsole, clearConsole, dispatchConsoleEvent
82+
} = bindActionCreators({ ...IDEActions, ...ConsoleActions }, useDispatch());
8183

82-
render() {
83-
const consoleClass = classNames({
84-
'preview-console': true,
85-
'preview-console--collapsed': !this.props.isExpanded
86-
});
84+
useDidUpdate(() => {
85+
clearConsole();
86+
dispatchConsoleEvent(consoleEvents);
87+
}, [theme, fontSize]);
8788

88-
return (
89-
<section className={consoleClass} >
90-
<header className="preview-console__header">
91-
<h2 className="preview-console__header-title">Console</h2>
92-
<div className="preview-console__header-buttons">
93-
<button className="preview-console__clear" onClick={this.props.clearConsole} aria-label="Clear console">
94-
Clear
95-
</button>
96-
<button
97-
className="preview-console__collapse"
98-
onClick={this.props.collapseConsole}
99-
aria-label="Close console"
100-
>
101-
<DownArrowIcon focusable="false" aria-hidden="true" />
102-
</button>
103-
<button className="preview-console__expand" onClick={this.props.expandConsole} aria-label="Open console" >
104-
<UpArrowIcon focusable="false" aria-hidden="true" />
105-
</button>
106-
</div>
107-
</header>
108-
<div ref={(element) => { this.consoleMessages = element; }} className="preview-console__messages">
109-
{this.props.consoleEvents.map((consoleEvent) => {
110-
const { method, times } = consoleEvent;
111-
const { theme } = this.props;
112-
return (
113-
<div key={consoleEvent.id} className={`preview-console__message preview-console__message--${method}`}>
114-
{ times > 1 &&
115-
<div
116-
className="preview-console__logged-times"
117-
style={{ fontSize: this.props.fontSize, borderRadius: this.props.fontSize / 2 }}
118-
>
119-
{times}
120-
</div>
121-
}
122-
<ConsoleFeed
123-
styles={this.getConsoleFeedStyle(theme, times)}
124-
logs={[consoleEvent]}
125-
/>
126-
</div>
127-
);
128-
})}
129-
</div>
130-
</section>
131-
);
132-
}
133-
}
89+
const cm = useRef({});
13490

135-
Console.propTypes = {
136-
consoleEvents: PropTypes.arrayOf(PropTypes.shape({
137-
method: PropTypes.string.isRequired,
138-
args: PropTypes.arrayOf(PropTypes.string)
139-
})),
140-
isExpanded: PropTypes.bool.isRequired,
141-
collapseConsole: PropTypes.func.isRequired,
142-
expandConsole: PropTypes.func.isRequired,
143-
clearConsole: PropTypes.func.isRequired,
144-
dispatchConsoleEvent: PropTypes.func.isRequired,
145-
theme: PropTypes.string.isRequired,
146-
fontSize: PropTypes.number.isRequired
147-
};
91+
useDidUpdate(() => { cm.current.scrollTop = cm.current.scrollHeight; });
92+
93+
const consoleClass = classNames({
94+
'preview-console': true,
95+
'preview-console--collapsed': !isExpanded
96+
});
14897

149-
Console.defaultProps = {
150-
consoleEvents: []
98+
return (
99+
<section className={consoleClass} >
100+
<header className="preview-console__header">
101+
<h2 className="preview-console__header-title">Console</h2>
102+
<div className="preview-console__header-buttons">
103+
<button className="preview-console__clear" onClick={clearConsole} aria-label="Clear console">
104+
Clear
105+
</button>
106+
<button
107+
className="preview-console__collapse"
108+
onClick={collapseConsole}
109+
aria-label="Close console"
110+
>
111+
<DownArrowIcon focusable="false" aria-hidden="true" />
112+
</button>
113+
<button className="preview-console__expand" onClick={expandConsole} aria-label="Open console" >
114+
<UpArrowIcon focusable="false" aria-hidden="true" />
115+
</button>
116+
</div>
117+
</header>
118+
<div ref={cm} className="preview-console__messages">
119+
{consoleEvents.map((consoleEvent) => {
120+
const { method, times } = consoleEvent;
121+
return (
122+
<div key={consoleEvent.id} className={`preview-console__message preview-console__message--${method}`}>
123+
{ times > 1 &&
124+
<div
125+
className="preview-console__logged-times"
126+
style={{ fontSize, borderRadius: fontSize / 2 }}
127+
>
128+
{times}
129+
</div>
130+
}
131+
<ConsoleFeed
132+
styles={getConsoleFeedStyle(theme, times, fontSize)}
133+
logs={[consoleEvent]}
134+
/>
135+
</div>
136+
);
137+
})}
138+
</div>
139+
</section>
140+
);
151141
};
152142

143+
153144
export default Console;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export const optionsOnOff = (name, onLabel = 'On', offLabel = 'Off') => [
2+
{
3+
value: true, label: onLabel, ariaLabel: `${name} on`, name: `${name}`, id: `${name}-on`.replace(' ', '-')
4+
},
5+
{
6+
value: false, label: offLabel, ariaLabel: `${name} off`, name: `${name}`, id: `${name}-off`.replace(' ', '-')
7+
},
8+
];
9+
10+
export const optionsPickOne = (name, ...options) => options.map(option => ({
11+
value: option,
12+
label: option,
13+
ariaLabel: `${option} ${name} on`,
14+
name: `${option} ${name}`,
15+
id: `${option}-${name}-on`.replace(' ', '-')
16+
}));
17+
18+
const nameToValueName = x => (x && x.toLowerCase().replace(/#|_|-/g, ' '));
19+
20+
// preferenceOnOff: name, value and onSelect are mandatory. propname is optional
21+
export const preferenceOnOff = (name, value, onSelect, propname) => ({
22+
title: name,
23+
value,
24+
options: optionsOnOff(propname || nameToValueName(name)),
25+
onSelect
26+
});

client/modules/IDE/components/Preferences.jsx renamed to client/modules/IDE/components/Preferences/index.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import { withTranslation } from 'react-i18next';
77
// import { connect } from 'react-redux';
88
// import * as PreferencesActions from '../actions/preferences';
99

10-
import PlusIcon from '../../../images/plus.svg';
11-
import MinusIcon from '../../../images/minus.svg';
12-
import beepUrl from '../../../sounds/audioAlert.mp3';
10+
import PlusIcon from '../../../../images/plus.svg';
11+
import MinusIcon from '../../../../images/minus.svg';
12+
import beepUrl from '../../../../sounds/audioAlert.mp3';
1313

1414
class Preferences extends React.Component {
1515
constructor(props) {

0 commit comments

Comments
 (0)