Skip to content

Commit 930e131

Browse files
author
Madelyn Kasula
authored
Merge branch 'staging' into levelbuilder
2 parents bedadf6 + 8a985e8 commit 930e131

File tree

64 files changed

+630
-119
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+630
-119
lines changed

apps/Gruntfile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ describe('entry tests', () => {
451451
'levels/textMatch': './src/sites/studio/pages/levels/textMatch.js',
452452
'levels/widget': './src/sites/studio/pages/levels/widget.js',
453453
'levels/_external_link': './src/sites/studio/pages/levels/_external_link.js',
454+
'levels/show': './src/sites/studio/pages/levels/show.js',
454455
'projects/index': './src/sites/studio/pages/projects/index.js',
455456
'projects/public': './src/sites/studio/pages/projects/public.js',
456457
'projects/featured': './src/sites/studio/pages/projects/featured.js',

apps/i18n/common/en_us.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,7 @@
12411241
"startOver": "Start Over",
12421242
"startOverTitle": "Are you sure you want to start over?",
12431243
"startOverBody": "This will reset the puzzle to its start state and reset all the data you've added or changed.",
1244+
"statsTableFailure": "Sorry, something went wrong. Please reload the page to try again.",
12441245
"stayHere": "Stay here",
12451246
"stepIn": "Step in",
12461247
"stepOut": "Step out",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { PropTypes } from 'react';
2+
import i18n from '@cdo/locale';
3+
import { onDismissRedirectDialog, dismissedRedirectDialog } from '@cdo/apps/util/dismissVersionRedirect';
4+
import RedirectDialog from '@cdo/apps/code-studio/components/RedirectDialog';
5+
6+
export default class ScriptLevelRedirectDialog extends React.Component {
7+
static propTypes = {
8+
redirectUrl: PropTypes.string.isRequired,
9+
scriptName: PropTypes.string.isRequired,
10+
courseName: PropTypes.string,
11+
};
12+
13+
state = {
14+
isOpen: true,
15+
};
16+
17+
onCloseRedirectDialog = () => {
18+
const {courseName, scriptName} = this.props;
19+
// Use course name if available, and script name if not.
20+
onDismissRedirectDialog(courseName || scriptName);
21+
this.setState({
22+
isOpen: false,
23+
});
24+
};
25+
26+
render() {
27+
const {redirectUrl, courseName, scriptName} = this.props;
28+
29+
// Only render redirect dialog if isOpen and user hasn't already dismissed the dialog for this course or script.
30+
const displayRedirectDialog = this.state.isOpen && !dismissedRedirectDialog(courseName || scriptName);
31+
32+
return (
33+
<RedirectDialog
34+
isOpen={displayRedirectDialog}
35+
details={i18n.assignedToNewerVersion()}
36+
handleClose={this.onCloseRedirectDialog}
37+
redirectUrl={redirectUrl}
38+
redirectButtonText={i18n.goToAssignedVersion()}
39+
/>
40+
);
41+
}
42+
}

apps/src/gamelab/blocks.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const limitedColours = [
3232
// some "tertiary" colors
3333
'#ff8800', // ORANGE
3434
'#8800ff', // PURPLE
35-
'#0088ff', // LIGHT BLUE
35+
'#00ff88', // LIME
3636
];
3737

3838
const customInputTypes = {

apps/src/lib/kits/maker/CircuitPlaygroundBoard.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ process.hrtime = require('browser-process-hrtime');
2929
/** @const {number} serial port transfer rate */
3030
const SERIAL_BAUD = 57600;
3131

32+
/** Maps the Circuit Playground Express pins to Circuit Playground Classic*/
33+
const pinMapping = {"A0": 12, "A1": 6, "A2": 9, "A3": 10, "A4": 3, "A5": 2, "A6": 0, "A7": 1};
34+
35+
3236
/**
3337
* Controller interface for an Adafruit Circuit Playground board using
3438
* Circuit Playground Firmata firmware.
@@ -228,33 +232,39 @@ export default class CircuitPlaygroundBoard extends EventEmitter {
228232
.then(() => forEachLedInSequence(led => led.off(), 80));
229233
}
230234

235+
mappedPin(pin) {
236+
return pinMapping.hasOwnProperty(pin) ? pinMapping[pin] : pin;
237+
}
238+
231239
pinMode(pin, modeConstant) {
232-
this.fiveBoard_.pinMode(pin, modeConstant);
240+
this.fiveBoard_.pinMode(this.mappedPin(pin), modeConstant);
233241
}
234242

235243
digitalWrite(pin, value) {
236-
this.fiveBoard_.digitalWrite(pin, value);
244+
this.fiveBoard_.digitalWrite(this.mappedPin(pin), value);
237245
}
238246

239247
digitalRead(pin, callback) {
240-
this.fiveBoard_.digitalRead(pin, callback);
248+
this.fiveBoard_.digitalRead(this.mappedPin(pin), callback);
241249
}
242250

243251
analogWrite(pin, value) {
244-
this.fiveBoard_.analogWrite(pin, value);
252+
this.fiveBoard_.analogWrite(this.mappedPin(pin), value);
245253
}
246254

247255
analogRead(pin, callback) {
248-
this.fiveBoard_.analogRead(pin, callback);
256+
this.fiveBoard_.analogRead(this.mappedPin(pin), callback);
249257
}
250258

251259
createLed(pin) {
260+
pin = this.mappedPin(pin);
252261
const newLed = new Led({board: this.fiveBoard_, pin});
253262
this.dynamicComponents_.push(newLed);
254263
return newLed;
255264
}
256265

257266
createButton(pin) {
267+
pin = this.mappedPin(pin);
258268
const newButton = new Button({board: this.fiveBoard_, pin});
259269
this.dynamicComponents_.push(newButton);
260270
return newButton;

apps/src/redux/sectionDataRedux.js

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,48 @@
11
import { PropTypes } from 'react';
2+
import $ from 'jquery';
23

3-
// Reducer for section data in teacher dashboard.
4-
// Tab specific reducers can import actions from this file
5-
// if they need to respond to a section changing.
4+
/**
5+
* Reducer for section data in teacher dashboard.
6+
* Tab specific reducers can import actions from this file
7+
* if they need to respond to a section changing.
8+
*/
9+
10+
/**
11+
* Shape for the section
12+
* The section we get directly from angular right now. This gives us a
13+
* different shape than some other places we use sections. For now, I'm just
14+
* going to document the parts of section that we use here
15+
*/
16+
export const sectionDataPropType = PropTypes.shape({
17+
id: PropTypes.number.isRequired,
18+
script: PropTypes.object,
19+
students: PropTypes.arrayOf(PropTypes.shape({
20+
id: PropTypes.number.isRequired,
21+
name: PropTypes.string.isRequired,
22+
})).isRequired
23+
});
24+
25+
/**
26+
* Status for loading section data from server.
27+
*/
28+
export const LoadingStatus = {
29+
IN_PROGRESS: "inProgress",
30+
SUCCESS: "success",
31+
FAIL: "fail",
32+
};
633

7-
// Action type constants
34+
/**
35+
* Action type constants
36+
*/
837
export const SET_SECTION = 'sectionData/SET_SECTION';
38+
export const CLEAR_SECTION_DATA = 'sectionData/CLEAR_SECTION_DATA';
39+
export const START_LOADING_SECTION = 'sectionData/START_LOADING_SECTION';
40+
export const LOAD_SECTION_SUCCESS = 'sectionData/LOAD_SECTION_SUCCESS';
41+
export const LOAD_SECTION_FAIL = 'sectionData/LOAD_SECTION_FAIL';
942

10-
// Action creators
43+
/**
44+
* Action creators
45+
*/
1146
export const setSection = (section) => {
1247
// Sort section.students by name.
1348
const sortedStudents = section.students.sort((a, b) => a.name.localeCompare(b.name));
@@ -22,25 +57,16 @@ export const setSection = (section) => {
2257
};
2358

2459
/**
25-
* Shape for the section
26-
* The section we get directly from angular right now. This gives us a
27-
* different shape than some other places we use sections. For now, I'm just
28-
* going to document the parts of section that we use here
60+
* Initial state of sectionDataRedux
2961
*/
30-
export const sectionDataPropType = PropTypes.shape({
31-
id: PropTypes.number.isRequired,
32-
script: PropTypes.object,
33-
students: PropTypes.arrayOf(PropTypes.shape({
34-
id: PropTypes.number.isRequired,
35-
name: PropTypes.string.isRequired,
36-
})).isRequired
37-
});
38-
39-
// Initial state of sectionDataRedux
4062
const initialState = {
4163
section: {},
64+
loadingStatus: null,
4265
};
4366

67+
/**
68+
* Reducer
69+
*/
4470
export default function sectionData(state=initialState, action) {
4571
if (action.type === SET_SECTION) {
4672
// Setting the section is the first action to be called when switching
@@ -52,14 +78,61 @@ export default function sectionData(state=initialState, action) {
5278
};
5379
}
5480

81+
if (action.type === CLEAR_SECTION_DATA) {
82+
return initialState;
83+
}
84+
85+
if (action.type === START_LOADING_SECTION) {
86+
return {
87+
...state,
88+
loadingStatus: LoadingStatus.IN_PROGRESS,
89+
};
90+
}
91+
92+
if (action.type === LOAD_SECTION_SUCCESS) {
93+
return {
94+
...state,
95+
loadingStatus: LoadingStatus.SUCCESS,
96+
};
97+
}
98+
99+
if (action.type === LOAD_SECTION_FAIL) {
100+
return {
101+
...state,
102+
loadingStatus: LoadingStatus.FAIL,
103+
};
104+
}
105+
55106
return state;
56107
}
57108

58-
// Selector functions
109+
/**
110+
* Selector functions
111+
*/
59112
export const getTotalStudentCount = (state) => {
60113
return state.sectionData.section.students.length;
61114
};
62115

63116
export const getStudentList = (state) => {
64117
return state.sectionData.section.students;
65118
};
119+
120+
/**
121+
* Helper functions
122+
*/
123+
export const asyncSetSection = (sectionId) => (dispatch) => {
124+
// Clear any existing section data and begin loading.
125+
dispatch({ type: CLEAR_SECTION_DATA });
126+
dispatch({ type: START_LOADING_SECTION });
127+
128+
$.ajax({
129+
url: `/api/v1/sections/${sectionId}`,
130+
method: 'GET',
131+
dataType: 'json'
132+
}).done(section => {
133+
dispatch(setSection(section));
134+
dispatch({ type: LOAD_SECTION_SUCCESS });
135+
}).fail((jqXhr, status) => {
136+
dispatch({ type: LOAD_SECTION_FAIL });
137+
});
138+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import $ from 'jquery';
2+
import React from 'react';
3+
import ReactDOM from 'react-dom';
4+
import ScriptLevelRedirectDialog from '@cdo/apps/code-studio/components/ScriptLevelRedirectDialog';
5+
6+
$(document).ready(initPage);
7+
8+
function initPage() {
9+
const script = document.querySelector('script[data-level]');
10+
const config = JSON.parse(script.dataset.level);
11+
12+
const redirectDialogMountPoint = document.getElementById('redirect-dialog');
13+
if (redirectDialogMountPoint && config.redirect_script_url) {
14+
ReactDOM.render(
15+
<ScriptLevelRedirectDialog
16+
redirectUrl={config.redirect_script_url}
17+
scriptName={config.script_name}
18+
courseName={config.course_name}
19+
/>,
20+
redirectDialogMountPoint
21+
);
22+
}
23+
}

apps/src/sites/studio/pages/teacher_dashboard/index.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import $ from 'jquery';
22
import React from 'react';
33
import ReactDOM from 'react-dom';
44
import {BrowserRouter as Router, Route} from 'react-router-dom';
5+
import {Provider} from 'react-redux';
6+
import {getStore, registerReducers} from '@cdo/apps/redux';
7+
import sectionData, {asyncSetSection} from '@cdo/apps/redux/sectionDataRedux';
8+
import stats, {asyncSetCompletedLevelCount} from '@cdo/apps/templates/teacherDashboard/statsRedux';
59
import TeacherDashboard from '@cdo/apps/templates/teacherDashboard/TeacherDashboard';
610

711
const script = document.querySelector('script[data-dashboard]');
@@ -10,15 +14,20 @@ const sectionId = scriptData.section_id;
1014
const baseUrl = `/teacher_dashboard/sections/${sectionId}`;
1115

1216
$(document).ready(function () {
17+
registerReducers({sectionData, stats});
18+
const store = getStore();
19+
store.dispatch(asyncSetSection(sectionId));
20+
store.dispatch(asyncSetCompletedLevelCount(sectionId));
21+
1322
ReactDOM.render(
14-
<div>
23+
<Provider store={store}>
1524
<Router basename={baseUrl}>
1625
<Route
1726
path="/"
1827
component={props => <TeacherDashboard {...props} sectionId={sectionId} studioUrlPrefix=""/>}
1928
/>
2029
</Router>
21-
</div>
30+
</Provider>
2231
,
2332
document.getElementById('teacher-dashboard')
2433
);

apps/src/templates/RegionalPartnerSearch.jsx

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -237,33 +237,35 @@ class RegionalPartnerSearch extends Component {
237237
<div>In addition to attending a five-day summer workshop, the professional learning program includes up to 4 required one-day, in-person academic year workshops during the 2019-20 school year.</div>
238238
)}
239239

240-
{partnerInfo.cost_scholarship_information && (
241-
<div>
242-
<h3>Cost and scholarship information:</h3>
243-
<UnsafeRenderedMarkdown markdown={partnerInfo.cost_scholarship_information}/>
244-
</div>
245-
)}
246-
247-
{partnerInfo.additional_program_information && (
248-
<div>
249-
<h3>Additional program information:</h3>
250-
<UnsafeRenderedMarkdown markdown={partnerInfo.additional_program_information}/>
251-
</div>
252-
)}
240+
<div className="professional_learning_information" id={`id-${partnerInfo.id}`}>
241+
{partnerInfo.cost_scholarship_information && (
242+
<div>
243+
<h3>Cost and scholarship information:</h3>
244+
<UnsafeRenderedMarkdown markdown={partnerInfo.cost_scholarship_information}/>
245+
</div>
246+
)}
247+
248+
{partnerInfo.additional_program_information && (
249+
<div>
250+
<h3>Additional program information:</h3>
251+
<UnsafeRenderedMarkdown markdown={partnerInfo.additional_program_information}/>
252+
</div>
253+
)}
254+
</div>
253255

254256
{appState === WorkshopApplicationStates.now_closed && (
255257
<div>Applications are now closed.</div>
256258
)}
257259

258260
{appState === WorkshopApplicationStates.currently_open && !partnerInfo.link_to_partner_application && (
259-
<a href={studio("/pd/application/teacher")}>
261+
<a className="professional_learning_link" id={`id-${partnerInfo.id}`} href={studio("/pd/application/teacher")}>
260262
<button>Start application</button>
261263
</a>
262264
)}
263265

264266
{appState === WorkshopApplicationStates.currently_open && partnerInfo.link_to_partner_application && (
265-
<a href={partnerInfo.link_to_partner_application} target="_blank">
266-
<button>Apply on partners site</button>
267+
<a className="professional_learning_link" id={`id-${partnerInfo.id}`} href={partnerInfo.link_to_partner_application} target="_blank">
268+
<button>Apply on partner's site</button>
267269
</a>
268270
)}
269271

0 commit comments

Comments
 (0)