Skip to content

Commit 1ef7c82

Browse files
committed
Merge branch 'develop-1.10'
2 parents a92d473 + ca04381 commit 1ef7c82

7 files changed

Lines changed: 266 additions & 150 deletions

File tree

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "c2lc-coding-environment",
3-
"version": "1.9.0",
3+
"version": "1.10.0",
44
"private": true,
55
"dependencies": {
66
"bootstrap": "4.6.1",

src/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export class App extends React.Component<AppProps, AppState> {
167167
constructor(props: any) {
168168
super(props);
169169

170-
this.version = '1.9';
170+
this.version = '1.10';
171171

172172
this.appContext = {
173173
bluetoothApiIsAvailable: FeatureDetection.bluetoothApiIsAvailable()

src/Scene.js

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { ReactComponent as StartIndicator } from './svg/StartIndicator.svg';
2626

2727
const startIndicatorWidth = 0.45;
2828

29-
type MousePosition = {
29+
type PointerPosition = {
3030
x: number,
3131
y: number
3232
};
@@ -173,7 +173,7 @@ class Scene extends React.Component<SceneProps, {}> {
173173
}
174174

175175
/* istanbul ignore next */
176-
getPositionFromSceneSvgMouseEvent(e: any): MousePosition {
176+
getPositionFromSceneSvgPointerEvent(e: any): PointerPosition {
177177
// $FlowFixMe: DOMPoint
178178
const clientPoint = new DOMPoint(e.clientX, e.clientY);
179179
const svgElem = e.currentTarget;
@@ -185,23 +185,36 @@ class Scene extends React.Component<SceneProps, {}> {
185185
}
186186

187187
/* istanbul ignore next */
188-
handleMouseDownSceneSvg = (e: any) => {
189-
const pos: MousePosition = this.getPositionFromSceneSvgMouseEvent(e);
190-
this.lastPaintX = pos.x;
191-
this.lastPaintY = pos.y;
192-
this.props.onPaintScene(pos.x, pos.y);
188+
handlePointerDownSceneSvg = (e: any) => {
189+
if (e.button === 0) {
190+
e.currentTarget.onpointermove = this.handlePaintMove;
191+
192+
// Capture the pointer events so that we can handle the case when
193+
// the pointerup event happens outside of the scene svg
194+
e.currentTarget.setPointerCapture(e.pointerId);
195+
196+
const pos: PointerPosition = this.getPositionFromSceneSvgPointerEvent(e);
197+
this.lastPaintX = pos.x;
198+
this.lastPaintY = pos.y;
199+
this.props.onPaintScene(pos.x, pos.y);
200+
}
193201
}
194202

195203
/* istanbul ignore next */
196-
handleMouseMoveSceneSvg = (e: any) => {
197-
const primaryButtonPressed = ((e.buttons % 2) === 1);
198-
if (primaryButtonPressed) {
199-
const pos: MousePosition = this.getPositionFromSceneSvgMouseEvent(e);
200-
if (pos.x !== this.lastPaintX || pos.y !== this.lastPaintY) {
201-
this.lastPaintX = pos.x;
202-
this.lastPaintY = pos.y;
203-
this.props.onPaintScene(pos.x, pos.y);
204-
}
204+
handlePointerUpSceneSvg = (e: any) => {
205+
e.currentTarget.onpointermove = null;
206+
e.currentTarget.releasePointerCapture(e.pointerId);
207+
}
208+
209+
/* istanbul ignore next */
210+
handlePaintMove = (e: any) => {
211+
const pos: PointerPosition = this.getPositionFromSceneSvgPointerEvent(e);
212+
if (this.props.dimensions.isXInRange(pos.x)
213+
&& this.props.dimensions.isYInRange(pos.y)
214+
&& (pos.x !== this.lastPaintX || pos.y !== this.lastPaintY)) {
215+
this.lastPaintX = pos.x;
216+
this.lastPaintY = pos.y;
217+
this.props.onPaintScene(pos.x, pos.y);
205218
}
206219
}
207220

@@ -252,8 +265,8 @@ class Scene extends React.Component<SceneProps, {}> {
252265
viewBox={`${minX} ${minY} ${width} ${height}`}
253266
ref={this.sceneSvgRef}
254267
aria-hidden={true}
255-
onMouseDown={this.handleMouseDownSceneSvg}
256-
onMouseMove={this.handleMouseMoveSceneSvg}
268+
onPointerDown={this.handlePointerDownSceneSvg}
269+
onPointerUp={this.handlePointerUpSceneSvg}
257270
>
258271
<defs>
259272
<clipPath id='Scene-clippath'>
@@ -290,19 +303,21 @@ class Scene extends React.Component<SceneProps, {}> {
290303
width={startIndicatorWidth}
291304
height={startIndicatorWidth}
292305
/>
293-
{this.props.theme === 'contrast' &&
306+
{(!this.props.customBackgroundDesignMode && this.props.theme === 'contrast') &&
294307
<circle
295308
className='Scene__characterOutline'
296309
cx={this.props.characterState.xPos}
297310
cy={this.props.characterState.yPos}
298311
r={0.51}
299312
/>
300313
}
301-
<SceneCharacter
302-
characterState={this.props.characterState}
303-
theme={this.props.theme}
304-
world={this.props.world}
305-
/>
314+
{!this.props.customBackgroundDesignMode &&
315+
<SceneCharacter
316+
characterState={this.props.characterState}
317+
theme={this.props.theme}
318+
world={this.props.world}
319+
/>
320+
}
306321
{this.props.customBackgroundDesignMode &&
307322
<PaintbrushCursor
308323
className='Scene__designModeCursor'

src/Scene.test.js

Lines changed: 82 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -231,104 +231,94 @@ describe('The ARIA label should tell there is a character with its position', ()
231231
});
232232
});
233233

234-
describe('When the Scene renders', () => {
235-
test('Should render the character component', () => {
236-
expect.assertions(5);
237-
const sceneDimensions = new SceneDimensions(1, 1, 1, 1);
238-
const sceneWrapper = createMountScene({
239-
dimensions: sceneDimensions,
240-
customBackground: new CustomBackground(sceneDimensions)
241-
});
234+
test.each([
235+
// Theme, Design mode, Character, Outline, Design mode cursor
236+
['default', false, true, false, false],
237+
['light', false, true, false, false],
238+
['dark', false, true, false, false],
239+
['gray', false, true, false, false],
240+
['contrast', false, true, true, false],
241+
['default', true, false, false, true],
242+
['light', true, false, false, true],
243+
['dark', true, false, false, true],
244+
['gray', true, false, false, true],
245+
['contrast', true, false, false, true]
246+
])('Start indicator, character, and design mode cursor (theme=%s, customBackgroundDesignMode=%p)', (theme, designMode, expectedCharacter, expectedOutline, expectedDesignModeCursor) => {
247+
const sceneDimensions = new SceneDimensions(1, 12, 1, 8);
248+
const startingX = 3;
249+
const startingY = 2;
250+
const characterX = 5;
251+
const characterY = 4;
252+
const designModeCursorX = 7;
253+
const designModeCursorY = 6;
254+
255+
const sceneWrapper = createMountScene({
256+
dimensions: sceneDimensions,
257+
characterState: new CharacterState(
258+
characterX,
259+
characterY,
260+
2,
261+
[],
262+
sceneDimensions
263+
),
264+
designModeCursorState: new DesignModeCursorState(
265+
designModeCursorX,
266+
designModeCursorY,
267+
sceneDimensions
268+
),
269+
theme: theme,
270+
customBackground: new CustomBackground(sceneDimensions),
271+
customBackgroundDesignMode: designMode,
272+
startingX,
273+
startingY
274+
});
275+
276+
// Start indicator
277+
const startIndicator = findStartIndicator(sceneWrapper);
278+
expect(startIndicator.length).toBe(1);
279+
expect(startIndicator.get(0).props.x)
280+
.toBe(startingX - startIndicator.get(0).props.width/2);
281+
expect(startIndicator.get(0).props.y)
282+
.toBe(startingY - startIndicator.get(0).props.height/2);
283+
284+
// Character
285+
const character = findSceneCharacter(sceneWrapper);
286+
expect(character.exists()).toBe(expectedCharacter);
287+
if (expectedCharacter) {
288+
expect(character.get(0).props.transform)
289+
.toBe(`translate(${characterX} ${characterY}) rotate(0 0 0)`);
290+
const characterIcon = findSceneCharacterIcon(sceneWrapper);
291+
expect(characterIcon.hostNodes().length).toBe(1);
242292
const expectedCharacterDimensions = calculateCharacterDimensions();
243-
expect(findSceneCharacterIcon(sceneWrapper).hostNodes().length).toBe(1);
244-
expect(findSceneCharacterIcon(sceneWrapper).get(0).props.x)
293+
expect(characterIcon.get(0).props.x)
245294
.toBeCloseTo(expectedCharacterDimensions.x, 5);
246-
expect(findSceneCharacterIcon(sceneWrapper).get(0).props.y)
295+
expect(characterIcon.get(0).props.y)
247296
.toBeCloseTo(expectedCharacterDimensions.y, 5);
248-
expect(findSceneCharacterIcon(sceneWrapper).get(0).props.width)
297+
expect(characterIcon.get(0).props.width)
249298
.toBeCloseTo(expectedCharacterDimensions.width, 5);
250-
expect(findSceneCharacterIcon(sceneWrapper).get(0).props.height)
299+
expect(characterIcon.get(0).props.height)
251300
.toBeCloseTo(expectedCharacterDimensions.height, 5);
252-
});
253-
test('Should render the design mode cursor in customBackgroundDesignMode', () => {
254-
const sceneDimensions = new SceneDimensions(1, 12, 1, 8);
255-
const designModeCursorState = new DesignModeCursorState(3, 2, sceneDimensions);
256-
257-
const sceneWrapper = createMountScene({
258-
dimensions: sceneDimensions,
259-
designModeCursorState: designModeCursorState,
260-
customBackground: new CustomBackground(sceneDimensions),
261-
customBackgroundDesignMode: true
262-
});
263-
264-
const designModeCursor = findDesignModeCursor(sceneWrapper);
265-
301+
}
302+
303+
// Character outline
304+
const characterOutline = findCharacterOutline(sceneWrapper);
305+
expect(characterOutline.exists()).toBe(expectedOutline);
306+
if (expectedOutline) {
307+
expect(characterOutline.length).toBe(1);
308+
expect(characterOutline.get(0).props.cx).toBe(characterX);
309+
expect(characterOutline.get(0).props.cy).toBe(characterY);
310+
}
311+
312+
// Design mode cursor
313+
const designModeCursor = findDesignModeCursor(sceneWrapper);
314+
expect(designModeCursor.exists()).toBe(expectedDesignModeCursor);
315+
if (expectedDesignModeCursor) {
266316
expect(designModeCursor.length).toBe(1);
267-
268-
const expectedX = designModeCursorState.x
269-
- designModeCursor.get(0).props.width/2;
270-
const expectedY = designModeCursorState.y
271-
- designModeCursor.get(0).props.height/2;
272-
expect(designModeCursor.get(0).props.x).toBe(expectedX);
273-
expect(designModeCursor.get(0).props.y).toBe(expectedY);
274-
});
275-
test('Should not render the design mode cursor outside customBackgroundDesignMode', () => {
276-
const sceneDimensions = new SceneDimensions(1, 12, 1, 8);
277-
const sceneWrapper = createMountScene({
278-
dimensions: sceneDimensions,
279-
designModeCursorState: new DesignModeCursorState(3, 2, sceneDimensions),
280-
customBackground: new CustomBackground(sceneDimensions),
281-
customBackgroundDesignMode: false
282-
});
283-
expect(findDesignModeCursor(sceneWrapper).length).toBe(0);
284-
});
285-
test('Should render the start indicator', () => {
286-
const sceneDimensions = new SceneDimensions(1, 8, 1, 9);
287-
const startingX = 3;
288-
const startingY = 3;
289-
const sceneWrapper = createMountScene({
290-
dimensions: sceneDimensions,
291-
customBackground: new CustomBackground(sceneDimensions),
292-
startingX,
293-
startingY
294-
});
295-
296-
const startIndicator = findStartIndicator(sceneWrapper);
297-
298-
expect(startIndicator.length).toBe(1);
299-
300-
const expectedX = startingX - startIndicator.get(0).props.width/2;
301-
const expectedY = startingY - startIndicator.get(0).props.height/2;
302-
expect(startIndicator.get(0).props.x).toBe(expectedX);
303-
expect(startIndicator.get(0).props.y).toBe(expectedY);
304-
});
305-
test.each([
306-
['default', false],
307-
['light', false],
308-
['dark', false],
309-
['gray', false],
310-
['contrast', true]
311-
])('Should add an outline to the character in high contrast only', (theme, expectedCharacterOutlineExists) => {
312-
const sceneDimensions = new SceneDimensions(1, 10, 1, 10);
313-
const characterX = 2;
314-
const characterY = 3;
315-
const sceneWrapper = createMountScene({
316-
dimensions: sceneDimensions,
317-
characterState: new CharacterState(characterX, characterY, 2, [], sceneDimensions),
318-
theme: theme,
319-
customBackground: new CustomBackground(sceneDimensions)
320-
});
321-
322-
const characterOutline = findCharacterOutline(sceneWrapper);
323-
324-
expect(characterOutline.exists()).toBe(expectedCharacterOutlineExists);
325-
326-
if (expectedCharacterOutlineExists) {
327-
expect(characterOutline.length).toBe(1);
328-
expect(characterOutline.get(0).props.cx).toBe(characterX);
329-
expect(characterOutline.get(0).props.cy).toBe(characterY);
330-
}
331-
});
317+
expect(designModeCursor.get(0).props.x)
318+
.toBe(designModeCursorX - designModeCursor.get(0).props.width/2);
319+
expect(designModeCursor.get(0).props.y)
320+
.toBe(designModeCursorY - designModeCursor.get(0).props.height/2);
321+
}
332322
});
333323

334324
describe('When the character renders, transform should apply', (sceneDimensions = new SceneDimensions(1, 100, 1, 100)) => {

0 commit comments

Comments
 (0)