Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
33b5619
show different error if loading class libraries fails
ajpal May 5, 2020
33e22aa
refactor for two error states
ajpal May 6, 2020
d1885dc
Set theme to default if handleChange event === null
May 7, 2020
658cce4
Unit test handleChange event
May 7, 2020
c5cde2b
levelbuilder content changes (-robo-commit)
deploy-code-org May 7, 2020
9f3b307
Merge pull request #34698 from code-dot-org/dtl_candidate_c979873b
deploy-code-org May 7, 2020
957fba2
fix tests
ajpal May 8, 2020
024d929
Restore missing entries in schema.rb
breville May 8, 2020
3d85f43
update trusted proxy list for cloudfront servers
deploy-code-org May 8, 2020
f308405
Merge pull request #34712 from code-dot-org/staging
deploy-code-org May 8, 2020
a46d8ac
Virtual and suppress_email fields on the workshop form
islemaster May 7, 2020
35718de
HelpTip component with story
islemaster May 7, 2020
2414e6b
Add HelpTips to new workshop form options
islemaster May 7, 2020
a2e6795
Hide new UI behind virtual-workshops experiment
islemaster May 7, 2020
24e0546
Absolute imports
islemaster May 8, 2020
1be0986
code break homepage promo, episode 8
Erin007 May 8, 2020
99ea66a
Merge pull request #34684 from code-dot-org/clear-theme-fix
May 8, 2020
978e994
Merge pull request #34714 from code-dot-org/staging
deploy-code-org May 8, 2020
3ae41b1
fix other field
molly-moen May 8, 2020
d0f0cc1
Merge pull request #34633 from code-dot-org/may5-library-error
ajpal May 8, 2020
ca41c6b
Merge pull request #34719 from code-dot-org/staging
deploy-code-org May 8, 2020
acfb81f
Merge pull request #34678 from code-dot-org/virtual-workshop-form
islemaster May 8, 2020
e454f5e
levelbuilder content changes (-Brendan)
deploy-code-org May 8, 2020
b2f300b
Merge pull request #34720 from code-dot-org/levelbuilder
breville May 8, 2020
8f418e9
Merge pull request #34711 from code-dot-org/fix-db-schema
breville May 8, 2020
093d2d5
staging content changes (-)
deploy-code-org May 8, 2020
b5f2c67
Merge pull request #34713 from code-dot-org/code-break-promo-ep-8
Erin007 May 8, 2020
e322a6c
Merge pull request #34716 from code-dot-org/molly/other-text
molly-moen May 8, 2020
ba6f37b
Merge pull request #34721 from code-dot-org/staging
deploy-code-org May 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 apps/i18n/common/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,7 @@
"enrollmentDescription": "Join your teacher's classroom by entering their section code below. Teachers will be able to see your course progress, projects, and reset your password in case you forget it.",
"errorEmptyFunctionBlockModal": "There need to be blocks inside your function definition. Click \"edit\" and drag blocks inside the green block.",
"errorExceededLimitedBlocks": "You did it! Now go find the pattern in your code and take out the extra blocks. You can only use {limit} of these blocks:",
"errorFindingClassLibraries": "Unable to load your class libraries at this time. Please check your internet connection and try again.",
"errorIncompleteBlockInFunction": "Click \"edit\" to make sure you don't have any blocks missing inside your function definition.",
"errorGenericLintError": "Your program contains an editor warning that needs to be corrected. Hover over the icons near the line numbers in the editor to learn more.",
"errorLoadingRosteredSections": "Oops, there was a problem grabbing your sections from {type}.",
Expand Down
11 changes: 8 additions & 3 deletions 11 apps/src/applab/designElements/ThemeDropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// each theme will look like
import PropTypes from 'prop-types';
import React from 'react';
import {themeOptionsForSelect} from '../constants';
import {themeOptionsForSelect, DEFAULT_THEME_INDEX} from '../constants';
import Select from 'react-select';
import 'react-select/dist/react-select.css';

Expand Down Expand Up @@ -39,9 +39,14 @@ export default class ThemeDropdown extends React.Component {
state = {
selectedValue: this.props.initialValue
};

handleChange = event => {
this.props.handleChange(event.value);
this.setState({selectedValue: event.value});
const newValue = event
? event.value
: themeOptionsForSelect[DEFAULT_THEME_INDEX].option;

this.props.handleChange(newValue);
this.setState({selectedValue: newValue});
};

render() {
Expand Down
33 changes: 26 additions & 7 deletions 33 apps/src/code-studio/components/libraries/LibraryManagerDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class LibraryManagerDialog extends React.Component {
displayLibrary: null,
displayLibraryMode: DisplayLibraryMode.NONE,
isLoading: false,
error: null,
errorMessages: {},
updatedLibraryChannels: []
};

Expand All @@ -139,7 +139,12 @@ export class LibraryManagerDialog extends React.Component {
this.setState({classLibraries, projectLibraries});
},
error => {
console.log('error: ' + error);
this.setState({
errorMessages: {
...this.state.errorMessages,
loadClassLibraries: i18n.errorFindingClassLibraries()
}
});
}
);

Expand All @@ -163,7 +168,10 @@ export class LibraryManagerDialog extends React.Component {
};

setLibraryToImport = event => {
this.setState({importLibraryId: event.target.value, error: null});
this.setState({
importLibraryId: event.target.value,
errorMessages: {...this.state.errorMessages, importFromId: undefined}
});
};

addLibraryToProject = libraryJson => {
Expand Down Expand Up @@ -194,7 +202,10 @@ export class LibraryManagerDialog extends React.Component {
addLibraryById = (libraryJson, error) => {
if (error) {
this.setState({
error: i18n.libraryImportError(),
errorMessages: {
...this.state.errorMessages,
importFromId: i18n.libraryImportError()
},
isLoading: false
});
} else if (libraryJson) {
Expand Down Expand Up @@ -278,7 +289,10 @@ export class LibraryManagerDialog extends React.Component {
};

displayClassLibraries = () => {
const {classLibraries} = this.state;
const {classLibraries, errorMessages} = this.state;
if (errorMessages.loadClassLibraries) {
return <div style={styles.error}>{errorMessages.loadClassLibraries}</div>;
}
if (!Array.isArray(classLibraries) || !classLibraries.length) {
return <div style={styles.message}>{i18n.noLibrariesInClass()}</div>;
}
Expand Down Expand Up @@ -367,7 +381,12 @@ export class LibraryManagerDialog extends React.Component {

render() {
const {isOpen} = this.props;
const {importLibraryId, displayLibrary, isLoading, error} = this.state;
const {
importLibraryId,
displayLibrary,
isLoading,
errorMessages
} = this.state;

if (!isOpen) {
return null;
Expand Down Expand Up @@ -406,7 +425,7 @@ export class LibraryManagerDialog extends React.Component {
{!isLoading && i18n.add()}
</button>
</div>
<div style={styles.error}>{error}</div>
<div style={styles.error}>{errorMessages.importFromId}</div>
</BaseDialog>
{displayLibrary && this.renderDisplayLibrary()}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import {
Courses,
Subjects
} from '@cdo/apps/generated/pd/sharedWorkshopConstants';
import HelpTip from '@cdo/apps/lib/ui/HelpTip';
import experiments from '@cdo/apps/util/experiments';

const styles = {
readOnlyInput: {
Expand Down Expand Up @@ -90,6 +92,8 @@ export class WorkshopForm extends React.Component {
enrolled_teacher_count: PropTypes.number.isRequired,
regional_partner_name: PropTypes.string,
regional_partner_id: PropTypes.number,
virtual: PropTypes.bool,
suppress_email: PropTypes.bool,
organizer: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string
Expand Down Expand Up @@ -126,7 +130,9 @@ export class WorkshopForm extends React.Component {
availableFacilitators: [],
showSaveConfirmation: false,
showTypeOptionsHelpDisplay: false,
regional_partner_id: ''
regional_partner_id: '',
virtual: false,
suppress_email: false
};

if (props.workshop) {
Expand All @@ -145,7 +151,9 @@ export class WorkshopForm extends React.Component {
'fee',
'notes',
'regional_partner_id',
'organizer'
'organizer',
'virtual',
'suppress_email'
])
);
initialState.sessions = this.prepareSessionsForForm(
Expand Down Expand Up @@ -724,6 +732,22 @@ export class WorkshopForm extends React.Component {
return value;
};

handleVirtualChange = event => {
// This field gets its own handler both so we can coerce its value to
// boolean, and so we can enforce some business logic that says:
// Virtual workshops ALWAYS suppress email.
const virtual = event.target.value === 'true';
const suppress_email = virtual || this.state.suppress_email;
this.setState({virtual, suppress_email});
};

handleSuppressEmailChange = event => {
// This field gets its own handler so we can coerce its value to boolean
// before we save it to React state.
const suppress_email = event.target.value === 'true';
this.setState({suppress_email});
};

handleRegionalPartnerSelect = selection => {
this.setState({regional_partner_id: selection ? selection.value : null});
};
Expand Down Expand Up @@ -781,6 +805,8 @@ export class WorkshopForm extends React.Component {
subject: this.state.subject,
fee: this.state.fee ? this.state.fee : null,
notes: this.state.notes,
virtual: this.state.virtual,
suppress_email: this.state.suppress_email,
sessions_attributes: this.prepareSessionsForApi(
this.state.sessions,
this.state.destroyedSessions
Expand Down Expand Up @@ -965,6 +991,69 @@ export class WorkshopForm extends React.Component {
readOnly={this.props.readOnly}
/>
<br />
{experiments.isEnabled(experiments.VIRTUAL_WORKSHOPS) && (
<Row>
<Col sm={5}>
<FormGroup validationState={validation.style.virtual}>
<ControlLabel>
Is this a virtual workshop?
<HelpTip>
<p>When a workshop is virtual, our system:</p>
<ul>
<li>
Does not require you to enter a location address
</li>
<li>
Will not send email notifications, such as enrollment
receipts and workshop reminders
</li>
<li>
Will send a post-workshop survey designed for virtual
workshops
</li>
</ul>
</HelpTip>
</ControlLabel>
<SelectIsVirtual
value={this.state.virtual || false}
onChange={this.handleVirtualChange}
readOnly={this.props.readOnly}
/>
<HelpBlock>{validation.help.virtual}</HelpBlock>
</FormGroup>
</Col>
<Col sm={5}>
<FormGroup validationState={validation.style.suppress_email}>
<ControlLabel>
Enable email notifications?
<HelpTip>
<p>
Code.org can send email notifications about this
workshop to your attendees on your behalf. Notifications
may include:
</p>
<ul>
<li>Enrollment receipts</li>
<li>10-day and 3-day workshop reminders</li>
<li>Updates when workshop details change</li>
</ul>
<p>
Code.org will always email a post-workshop survey to
participants, even if you disable workshop notifications
here.
</p>
</HelpTip>
</ControlLabel>
<SelectSuppressEmail
onChange={this.handleSuppressEmailChange}
value={this.state.suppress_email || false}
readOnly={this.props.readOnly || this.state.virtual}
/>
<HelpBlock>{validation.help.suppress_email}</HelpBlock>
</FormGroup>
</Col>
</Row>
)}
<Row>
<Col sm={4}>
<FormGroup validationState={validation.style.location_name}>
Expand Down Expand Up @@ -1085,3 +1174,51 @@ export default connect(state => ({
permission: state.workshopDashboard.permission,
facilitatorCourses: state.workshopDashboard.facilitatorCourses
}))(WorkshopForm);

const SelectIsVirtual = ({value, readOnly, onChange}) => (
<FormControl
componentClass="select"
value={value}
id="virtual"
name="virtual"
onChange={onChange}
style={readOnly ? styles.readOnlyInput : undefined}
disabled={readOnly}
>
<option key={false} value={false}>
No, this is an in-person workshop.
</option>
<option key={true} value={true}>
Yes, this is a virtual workshop.
</option>
</FormControl>
);
SelectIsVirtual.propTypes = {
value: PropTypes.bool.isRequired,
readOnly: PropTypes.bool,
onChange: PropTypes.func.isRequired
};

const SelectSuppressEmail = ({value, readOnly, onChange}) => (
<FormControl
componentClass="select"
value={value}
id="suppress_email"
name="suppress_email"
onChange={onChange}
style={readOnly ? styles.readOnlyInput : undefined}
disabled={readOnly}
>
<option key={false} value={false}>
Yes, send notifications on my behalf.
</option>
<option key={true} value={true}>
No, I will handle communication with attendees myself.
</option>
</FormControl>
);
SelectSuppressEmail.propTypes = {
value: PropTypes.bool.isRequired,
readOnly: PropTypes.bool,
onChange: PropTypes.func.isRequired
};
4 changes: 3 additions & 1 deletion 4 apps/src/code-studio/pd/workshop_dashboard/workshop.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ export class Workshop extends React.Component {
'regional_partner_id',
'scholarship_workshop?',
'potential_organizers',
'created_at'
'created_at',
'virtual',
'suppress_email'
])
});
})
Expand Down
27 changes: 27 additions & 0 deletions 27 apps/src/lib/ui/HelpTip.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactTooltip from 'react-tooltip';
import Portal from 'react-portal';
import FontAwesome from '../../templates/FontAwesome';
import _ from 'lodash';

export default function HelpTip({children}) {
const id = _.uniqueId();

return (
<span data-for={id} data-tip>
<FontAwesome
icon="question-circle-o"
style={{cursor: 'pointer', marginLeft: '0.5em', marginRight: '0.5em'}}
/>
<Portal isOpened={true}>
<ReactTooltip id={id} role="tooltip" effect="solid">
<div style={{maxWidth: 400}}>{children}</div>
</ReactTooltip>
</Portal>
</span>
);
}
HelpTip.propTypes = {
children: PropTypes.node.isRequired
};
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.