Skip to content

Commit 53d2af2

Browse files
committed
Merge branch 'develop-1.4'
2 parents 8cfd7ec + 8a4c9bb commit 53d2af2

29 files changed

Lines changed: 18972 additions & 3086 deletions

.flowconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
[ignore]
22

3+
.*/node_modules/eslint-plugin-import/node_modules/resolve/test/resolver/malformed_package_json/package\.json
4+
35
[include]
46

57
[libs]

docs/keyboard.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Those key bindings are as follows.
3737
| Move the current step to right | Ctrl + Alt + ] | ![Move to next icon](board-imgs/MoveNext.png) |
3838
| Play or pause the program | Ctrl + Alt + p | ![Play or pause program icon](board-imgs/PlayPause.png) |
3939
| Refresh the scene | Ctrl + Alt + r | ![Refresh scene icon](board-imgs/RefreshScene.png) |
40+
| Replace the current program step | Ctrl + Alt + c | Not available |
4041
| Show the keyboard shortcuts menu | ? | ![Show keyboard shortcuts menu icon](board-imgs/ShowKeyboardShortcuts.png) |
4142
| Stop the program from playing | Ctrl + Alt + s | ![Stop program icon](board-imgs/StopProgram.png) |
4243
| Change the current visual theme to the dark theme | Ctrl + Alt + x, t, 3 | ![Change to dark theme icon](board-imgs/ChangeToDarkTheme.png) |
@@ -52,9 +53,11 @@ Those key bindings are as follows.
5253
| Move focus to the character position controls | Ctrl + Alt + x, f, c | ![Focus character position controls icon](board-imgs/FocusCharacterPositionControls.png) |
5354
| Move focus to the character position row input field | Ctrl + Alt + x, f, y | ![Focus character position row input icon](board-imgs/FocusCharacterPositionRow.png) |
5455
| Move focus to the loop iterations input field | Ctrl + Alt + x, f, l | ![Focus loop iterations input icon](board-imgs/FocusLoopIterationsInput.png) |
56+
| Move focus to the next program block | Ctrl + Alt + x, f, ] | Not available |
5557
| Move focus to the page title | Ctrl + Alt + x, f, h | ![Focus Weavly logo icon](board-imgs/FocusLogo.png) |
5658
| Move focus to the pen toggle | Ctrl + Alt + x, f, s | ![Focus pen toggle icon](board-imgs/FocusPenToggle.png) |
5759
| Move focus to the play button | Ctrl + Alt + x, f, p | ![Focus play button icon](board-imgs/FocusPlay.png) |
60+
| Move focus to the previous program block | Ctrl + Alt + x, f, [ | Not available |
5861
| Move focus to the world selector | Ctrl + Alt + x, f, w | ![Focus world selector icon](board-imgs/FocusWorldSelector.png) |
5962
| Move the character down | Ctrl + Alt + x, p, m, d | ![Move character down icon](board-imgs/MoveCharacterDown.png) |
6063
| Move the character left | Ctrl + Alt + x, p, m, l | ![Move character left icon](board-imgs/MoveCharacterLeft.png) |
@@ -91,6 +94,7 @@ Those key bindings are as follows:
9194
| Move the current step to right | Alt + ] | ![Move to next icon](board-imgs/MoveNext.png) |
9295
| Play or pause the program | Alt + p | ![Play or pause program icon](board-imgs/PlayPause.png) |
9396
| Refresh the scene | Alt + r | ![Refresh scene icon](board-imgs/RefreshScene.png) |
97+
| Replace the current program step | Alt + c | Not available |
9498
| Show the keyboard shortcuts menu | ? | ![Show keyboard shortcuts menu icon](board-imgs/ShowKeyboardShortcuts.png) |
9599
| Stop the program from playing | Alt + s | ![Stop program icon](board-imgs/StopProgram.png) |
96100
| Change the current visual theme to the dark theme | Alt + x, t, 3 | ![Change to dark theme icon](board-imgs/ChangeToDarkTheme.png) |
@@ -106,9 +110,11 @@ Those key bindings are as follows:
106110
| Move focus to the character position controls | Alt + x, f, c | ![Focus character position controls icon](board-imgs/FocusCharacterPositionControls.png) |
107111
| Move focus to the character position row input field | Alt + x, f, y | ![Focus character position row input icon](board-imgs/FocusCharacterPositionRow.png) |
108112
| Move focus to the loop iterations input field | Alt + x, f, l | ![Focus loop iterations input icon](board-imgs/FocusLoopIterationsInput.png) |
113+
| Move focus to the next program block | Alt + x, f, ] | Not available |
109114
| Move focus to the page title | Alt + x, f, h | ![Focus Weavly logo icon](board-imgs/FocusLogo.png) |
110115
| Move focus to the pen toggle | Alt + x, f, s | ![Focus pen toggle icon](board-imgs/FocusPenToggle.png) |
111116
| Move focus to the play button | Alt + x, f, p | ![Focus play button icon](board-imgs/FocusPlay.png) |
117+
| Move focus to the previous program block | Alt + x, f, [ | Not available |
112118
| Move focus to the world selector | Alt + x, f, w | ![Focus world selector icon](board-imgs/FocusWorldSelector.png) |
113119
| Move the character down | Alt + x, p, m, d | ![Move character down icon](board-imgs/MoveCharacterDown.png) |
114120
| Move the character left | Alt + x, p, m, l | ![Move character left icon](board-imgs/MoveCharacterLeft.png) |

package-lock.json

Lines changed: 17498 additions & 2614 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "c2lc-coding-environment",
3-
"version": "1.3.2",
3+
"version": "1.4.0",
44
"private": true,
55
"dependencies": {
66
"bootstrap": "4.6.1",
@@ -12,7 +12,7 @@
1212
"react-bootstrap": "1.6.4",
1313
"react-dom": "16.14.0",
1414
"react-intl": "5.22.0",
15-
"react-scripts": "5.0.0",
15+
"react-scripts": "5.0.1",
1616
"tone": "14.7.77",
1717
"typeface-roboto": "1.1.13"
1818
},

src/ActionPanel.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ReactComponent as MovePreviousIcon } from './svg/MovePrevious.svg';
1010
import { ReactComponent as MoveNextIcon } from './svg/MoveNext.svg';
1111
import { ReactComponent as DeleteIcon } from './svg/Delete.svg';
1212
import { ReactComponent as ReplaceIcon } from './svg/replace.svg';
13-
import { focusByQuerySelector, moveToNextStepDisabled, moveToPreviousStepDisabled } from './Utils';
13+
import { focusByQuerySelector, isLoopBlock, moveToNextStepDisabled, moveToPreviousStepDisabled } from './Utils';
1414
import './ActionPanel.scss';
1515

1616
type ActionPanelProps = {
@@ -192,8 +192,7 @@ class ActionPanel extends React.Component<ActionPanelProps, {}> {
192192
}
193193

194194
getReplaceIsVisible(): boolean {
195-
const currentStepName = this.props.programSequence.getProgramStepAt(this.props.pressedStepIndex).block;
196-
return currentStepName !== 'startLoop' && currentStepName !== 'endLoop';
195+
return !isLoopBlock(this.props.programSequence.getProgramStepAt(this.props.pressedStepIndex).block);
197196
}
198197

199198
// handlers
@@ -219,6 +218,7 @@ class ActionPanel extends React.Component<ActionPanelProps, {}> {
219218
const moveToNextStepIsDisabled = moveToNextStepDisabled(this.props.programSequence, this.props.pressedStepIndex);
220219
const moveToPreviousStepIsDisabled = moveToPreviousStepDisabled(this.props.programSequence, this.props.pressedStepIndex);
221220
const replaceIsVisible = this.getReplaceIsVisible();
221+
const replaceIsDisabled = this.props.selectedCommandName == null;
222222
return (
223223
<React.Fragment>
224224
<div className="ActionPanel__background">
@@ -240,9 +240,10 @@ class ActionPanel extends React.Component<ActionPanelProps, {}> {
240240
{replaceIsVisible &&
241241
<AriaDisablingButton
242242
name='replaceCurrentStep'
243-
disabled={false}
243+
disabled={replaceIsDisabled}
244+
disabledClassName='ActionPanel__action-buttons--disabled'
244245
aria-label={this.props.intl.formatMessage({id:'ActionPanel.action.replace'}, stepMessageData)}
245-
className='ActionPanel__action-buttons focus-trap-action-panel__action-panel-button focus-trap-action-panel-replace__replace_button'
246+
className='ActionPanel__action-buttons focus-trap-action-panel__action-panel-button'
246247
onClick={this.handleClickReplace}>
247248
<ReplaceIcon className='ActionPanel__action-button-svg' />
248249
</AriaDisablingButton>

src/ActionPanel.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function createMountActionPanel(props) {
2020
React.createElement(
2121
ActionPanel,
2222
Object.assign(
23+
{},
2324
{
2425
focusedOptionName: null,
2526
selectedCommandName: 'right45',
@@ -96,6 +97,17 @@ describe('ActionPanel options', () => {
9697
expect(mockReplaceHandler.mock.calls[0][0]).toBe(pressedStepIndex);
9798
});
9899

100+
test('Given that there is no selected action, then the Replace button should be disabled', () => {
101+
const { wrapper } = createMountActionPanel({
102+
pressedStepIndex: 1,
103+
selectedCommandName: null
104+
});
105+
const replaceButton = getActionPanelOptionButtons(wrapper, 'replaceCurrentStep');
106+
const expectedAriaLabel = 'Replace Step 2 turn left 45 degrees ';
107+
expect(replaceButton.get(0).props['aria-label']).toBe(expectedAriaLabel);
108+
expect(replaceButton.get(0).props['disabled']).toBe(true);
109+
});
110+
99111
test('When the moveToPreviousStep option is selected on second step turn left 45 of the program', () => {
100112
const pressedStepIndex = 1;
101113
const { wrapper, mockMoveToPreviousStep } = createMountActionPanel({

src/AnnouncementBuilder.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// @flow
2+
3+
import type {IntlShape} from 'react-intl';
4+
import type {ProgramBlock} from './types';
5+
6+
type AnnouncementData = {|
7+
messageIdSuffix: string,
8+
values: any
9+
|};
10+
11+
export default class AnnouncementBuilder {
12+
intl: IntlShape;
13+
14+
constructor(intl: IntlShape) {
15+
this.intl = intl;
16+
}
17+
18+
buildSelectActionAnnouncement(action: string): AnnouncementData {
19+
let commandType = null;
20+
if (action === 'loop') {
21+
commandType = this.intl.formatMessage({
22+
id: 'Announcement.control'
23+
});
24+
} else {
25+
commandType = this.intl.formatMessage({
26+
id: 'Announcement.movement'
27+
});
28+
}
29+
return {
30+
messageIdSuffix: 'actionSelected',
31+
values: {
32+
commandType: commandType,
33+
command: this.intl.formatMessage({
34+
id: `Announcement.${action}`
35+
}),
36+
}
37+
};
38+
}
39+
40+
buildAddStepAnnouncement(action: string): AnnouncementData {
41+
let commandType = null;
42+
if (action === 'loop') {
43+
commandType = this.intl.formatMessage({
44+
id: 'Announcement.control'
45+
});
46+
} else {
47+
commandType = this.intl.formatMessage({
48+
id: 'Announcement.movement'
49+
});
50+
}
51+
return {
52+
messageIdSuffix: 'add',
53+
values: {
54+
commandType: commandType,
55+
command: this.intl.formatMessage({
56+
id: `Announcement.${action}`
57+
}),
58+
}
59+
};
60+
}
61+
62+
buildDeleteStepAnnouncement(programBlock: ProgramBlock): AnnouncementData {
63+
let commandType = null;
64+
if (programBlock.block === 'startLoop' || programBlock.block === 'endLoop') {
65+
commandType = this.intl.formatMessage({
66+
id: "Announcement.control"
67+
});
68+
} else {
69+
commandType = this.intl.formatMessage({
70+
id: "Announcement.movement"
71+
});
72+
}
73+
return {
74+
messageIdSuffix: 'delete',
75+
values: {
76+
commandType: commandType,
77+
command: this.intl.formatMessage(
78+
{
79+
id: `Announcement.${programBlock.block}`
80+
},
81+
{
82+
loopLabel: programBlock.label
83+
}
84+
)
85+
}
86+
};
87+
}
88+
89+
buildReplaceStepAnnouncement(programBlock: ProgramBlock,
90+
selectedAction: string): AnnouncementData {
91+
92+
return {
93+
messageIdSuffix: 'replace',
94+
values: {
95+
oldCommand: this.intl.formatMessage({
96+
id: `Announcement.${programBlock.block}`
97+
}),
98+
newCommand: this.intl.formatMessage({
99+
id: `Announcement.${selectedAction}`
100+
})
101+
}
102+
};
103+
}
104+
};

src/AnnouncementBuilder.test.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// @flow
2+
3+
import AnnouncementBuilder from './AnnouncementBuilder';
4+
import {createIntl} from 'react-intl';
5+
import messages from './messages.json';
6+
7+
const intl = createIntl({
8+
locale: 'en',
9+
defaultLocale: 'en',
10+
messages: messages.en
11+
});
12+
13+
function createAnnouncementBuilder() {
14+
return new AnnouncementBuilder(intl);
15+
}
16+
17+
test('Test buildSelectActionAnnouncement()', () => {
18+
expect.assertions(2);
19+
20+
const announcementBuilder = createAnnouncementBuilder();
21+
22+
expect(announcementBuilder.buildSelectActionAnnouncement('loop')).toStrictEqual({
23+
messageIdSuffix: 'actionSelected',
24+
values: {
25+
commandType: 'control',
26+
command: 'loop'
27+
}
28+
});
29+
30+
expect(announcementBuilder.buildSelectActionAnnouncement('forward1')).toStrictEqual({
31+
messageIdSuffix: 'actionSelected',
32+
values: {
33+
commandType: 'movement',
34+
command: 'forward 1 square'
35+
}
36+
});
37+
});
38+
39+
test('Test buildAddStepAnnouncement()', () => {
40+
expect.assertions(2);
41+
42+
const announcementBuilder = createAnnouncementBuilder();
43+
44+
expect(announcementBuilder.buildAddStepAnnouncement('loop')).toStrictEqual({
45+
messageIdSuffix: 'add',
46+
values: {
47+
commandType: 'control',
48+
command: 'loop'
49+
}
50+
});
51+
52+
expect(announcementBuilder.buildAddStepAnnouncement('forward1')).toStrictEqual({
53+
messageIdSuffix: 'add',
54+
values: {
55+
commandType: 'movement',
56+
command: 'forward 1 square'
57+
}
58+
});
59+
});
60+
61+
describe('Test buildDeleteStepAnnouncement()', () => {
62+
test('startLoop', () => {
63+
const announcementBuilder = createAnnouncementBuilder();
64+
65+
const startLoopBlock = {
66+
block: 'startLoop',
67+
label: 'A'
68+
};
69+
70+
expect(announcementBuilder.buildDeleteStepAnnouncement(startLoopBlock)).toStrictEqual({
71+
messageIdSuffix: 'delete',
72+
values: {
73+
commandType: 'control',
74+
command: 'loop A'
75+
}
76+
});
77+
});
78+
79+
test('endLoop', () => {
80+
const announcementBuilder = createAnnouncementBuilder();
81+
82+
const endLoopBlock = {
83+
block: 'endLoop',
84+
label: 'A'
85+
};
86+
87+
expect(announcementBuilder.buildDeleteStepAnnouncement(endLoopBlock)).toStrictEqual({
88+
messageIdSuffix: 'delete',
89+
values: {
90+
commandType: 'control',
91+
command: 'loop A'
92+
}
93+
});
94+
});
95+
96+
test('forward1', () => {
97+
const announcementBuilder = createAnnouncementBuilder();
98+
99+
const forwardBlock = {
100+
block: 'forward1'
101+
};
102+
103+
expect(announcementBuilder.buildDeleteStepAnnouncement(forwardBlock)).toStrictEqual({
104+
messageIdSuffix: 'delete',
105+
values: {
106+
commandType: 'movement',
107+
command: 'forward 1 square'
108+
}
109+
});
110+
});
111+
});
112+
113+
test('Test buildReplaceStepAnnouncement()', () => {
114+
expect.assertions(1);
115+
116+
const announcementBuilder = createAnnouncementBuilder();
117+
118+
const forwardBlock = {
119+
block: 'forward1'
120+
};
121+
122+
expect(announcementBuilder.buildReplaceStepAnnouncement(forwardBlock, 'right45')).toStrictEqual({
123+
messageIdSuffix: 'replace',
124+
values: {
125+
oldCommand: 'forward 1 square',
126+
newCommand: 'turn right 45 degrees'
127+
}
128+
});
129+
});

0 commit comments

Comments
 (0)