Skip to content

Commit df3b742

Browse files
committed
Merge branch 'master' of github.com:isaacphysics/isaac-react-app
2 parents 7d451d0 + 9da5311 commit df3b742

File tree

11 files changed

+8945
-3163
lines changed

11 files changed

+8945
-3163
lines changed

package-lock.json

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

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@types/enzyme": "^3.9.1",
2121
"@types/he": "^1.1.0",
2222
"@types/history": "^4.7.2",
23-
"@types/jest": "24.0.11",
23+
"@types/jest": "^26.0.8",
2424
"@types/js-cookie": "^2.2.2",
2525
"@types/node": "11.11.0",
2626
"@types/react": "16.8.7",
@@ -39,7 +39,7 @@
3939
"axios": "^0.19.0",
4040
"babel-core": "7.0.0-bridge.0",
4141
"babel-eslint": "9.0.0",
42-
"babel-jest": "^24.7.1",
42+
"babel-jest": "^26.2.2",
4343
"babel-loader": "8.0.5",
4444
"babel-plugin-named-asset-import": "^0.3.1",
4545
"babel-preset-react-app": "^7.0.2",
@@ -71,10 +71,10 @@
7171
"inequality": "^0.9.22",
7272
"inequality-grammar": "^0.9.10",
7373
"isaac-graph-sketcher": "^0.9.2",
74-
"jest": "^24.7.1",
75-
"jest-pnp-resolver": "1.0.2",
76-
"jest-resolve": "23.6.0",
77-
"jest-watch-typeahead": "^0.2.1",
74+
"jest": "^26.2.2",
75+
"jest-pnp-resolver": "^1.2.2",
76+
"jest-resolve": "^26.2.2",
77+
"jest-watch-typeahead": "^0.6.0",
7878
"js-cookie": "^2.2.0",
7979
"katex": "^0.11.1",
8080
"lodash": "^4.17.19",
@@ -113,7 +113,7 @@
113113
"style-loader": "0.23.1",
114114
"terser-webpack-plugin": "^2.3.1",
115115
"transfer-webpack-plugin": "^0.1.4",
116-
"ts-jest": "24.3.0",
116+
"ts-jest": "^26.1.4",
117117
"ts-loader": "6.2.0",
118118
"typescript": "^3.8.3",
119119
"url-loader": "1.1.2",

src/app/components/content/IsaacSymbolicChemistryQuestion.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useEffect, useState} from "react";
1+
import React, {useEffect, useRef, useState} from "react";
22
import {connect} from "react-redux";
33
import {setCurrentAttempt} from "../../state/actions";
44
import {IsaacContentValueOrChildren} from "./IsaacContentValueOrChildren";
@@ -32,7 +32,7 @@ interface IsaacSymbolicChemistryQuestionProps {
3232
const IsaacSymbolicChemistryQuestionComponent = (props: IsaacSymbolicChemistryQuestionProps) => {
3333
const {doc, questionId, currentAttempt, setCurrentAttempt} = props;
3434
const [modalVisible, setModalVisible] = useState(false);
35-
const [initialEditorSymbols, setInitialEditorSymbols] = useState([]);
35+
const initialEditorSymbols = useRef(JSON.parse(doc.formulaSeed || '[]'));
3636

3737
let currentAttemptValue: any | undefined;
3838
if (currentAttempt && currentAttempt.value) {
@@ -46,9 +46,9 @@ const IsaacSymbolicChemistryQuestionComponent = (props: IsaacSymbolicChemistryQu
4646
useEffect(() => {
4747
if (!currentAttempt || !currentAttemptValue || !currentAttemptValue.symbols) return;
4848

49-
setInitialEditorSymbols(_flattenDeep(currentAttemptValue.symbols));
49+
initialEditorSymbols.current = _flattenDeep(currentAttemptValue.symbols);
5050
}, [currentAttempt, currentAttemptValue]);
51-
51+
5252
const closeModal = (previousYPosition: number) => () => {
5353
document.body.style.overflow = "initial";
5454
setModalVisible(false);
@@ -74,12 +74,13 @@ const IsaacSymbolicChemistryQuestionComponent = (props: IsaacSymbolicChemistryQu
7474
close={closeModal(window.scrollY)}
7575
onEditorStateChange={(state: any) => {
7676
setCurrentAttempt(questionId, { type: 'chemicalFormula', value: JSON.stringify(state), mhchemExpression: (state && state.result && state.result.mhchem) || "" })
77-
setInitialEditorSymbols(state.symbols);
77+
initialEditorSymbols.current = state.symbols;
7878
}}
7979
availableSymbols={doc.availableSymbols}
80-
initialEditorSymbols={initialEditorSymbols}
80+
initialEditorSymbols={initialEditorSymbols.current}
8181
visible={modalVisible}
8282
editorMode='chemistry'
83+
questionDoc={doc}
8384
/>}
8485
</div>
8586
);

src/app/components/content/IsaacSymbolicLogicQuestion.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useEffect, useState} from "react";
1+
import React, {useEffect, useRef, useState} from "react";
22
import {connect} from "react-redux";
33
import {setCurrentAttempt} from "../../state/actions";
44
import {IsaacContentValueOrChildren} from "./IsaacContentValueOrChildren";
@@ -35,7 +35,7 @@ interface IsaacSymbolicLogicQuestionProps {
3535
const IsaacSymbolicLogicQuestionComponent = (props: IsaacSymbolicLogicQuestionProps) => {
3636
const {doc, questionId, currentAttempt, setCurrentAttempt} = props;
3737
const [modalVisible, setModalVisible] = useState(false);
38-
const [initialEditorSymbols, setInitialEditorSymbols] = useState([]);
38+
const initialEditorSymbols = useRef(JSON.parse(doc.formulaSeed || '[]'));
3939
const examBoard = useCurrentExamBoard();
4040

4141
let currentAttemptValue: any | undefined;
@@ -50,7 +50,7 @@ const IsaacSymbolicLogicQuestionComponent = (props: IsaacSymbolicLogicQuestionPr
5050
useEffect(() => {
5151
if (!currentAttempt || !currentAttemptValue || !currentAttemptValue.symbols) return;
5252

53-
setInitialEditorSymbols(_flattenDeep(currentAttemptValue.symbols));
53+
initialEditorSymbols.current = _flattenDeep(currentAttemptValue.symbols);
5454
}, [currentAttempt, currentAttemptValue]);
5555

5656
const closeModal = (previousYPosition: number) => () => {
@@ -78,13 +78,14 @@ const IsaacSymbolicLogicQuestionComponent = (props: IsaacSymbolicLogicQuestionPr
7878
close={closeModal(window.scrollY)}
7979
onEditorStateChange={(state: any) => {
8080
setCurrentAttempt(questionId, { type: 'logicFormula', value: JSON.stringify(state), pythonExpression: (state && state.result && state.result.python)||"" })
81-
setInitialEditorSymbols(state.symbols);
81+
initialEditorSymbols.current = state.symbols;
8282
}}
8383
availableSymbols={doc.availableSymbols}
84-
initialEditorSymbols={initialEditorSymbols}
84+
initialEditorSymbols={initialEditorSymbols.current}
8585
visible={modalVisible}
8686
editorMode='logic'
8787
logicSyntax={examBoard == EXAM_BOARD.OCR ? 'logic' : 'binary'}
88+
questionDoc={doc}
8889
/>}
8990
</div>
9091
);

src/app/components/content/IsaacSymbolicQuestion.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ interface IsaacSymbolicQuestionProps {
6060
const IsaacSymbolicQuestionComponent = (props: IsaacSymbolicQuestionProps) => {
6161
const {doc, questionId, currentAttempt, setCurrentAttempt} = props;
6262
const [modalVisible, setModalVisible] = useState(false);
63-
const [initialEditorSymbols, setInitialEditorSymbols] = useState(JSON.parse(doc.formulaSeed || '[]'));
63+
const initialEditorSymbols = useRef(JSON.parse(doc.formulaSeed || '[]'));
6464
const [textInput, setTextInput] = useState('');
6565

6666
let currentAttemptValue: any | undefined;
@@ -79,7 +79,7 @@ const IsaacSymbolicQuestionComponent = (props: IsaacSymbolicQuestionProps) => {
7979
if (!previousPythonExpression || previousPythonExpression !== pythonExpression) {
8080
setCurrentAttempt(questionId, {type: 'formula', value: JSON.stringify(newState), pythonExpression});
8181
}
82-
setInitialEditorSymbols(state.symbols);
82+
initialEditorSymbols.current = state.symbols;
8383
};
8484

8585
const closeModal = (previousYPosition: number) => () => {
@@ -183,7 +183,7 @@ const IsaacSymbolicQuestionComponent = (props: IsaacSymbolicQuestionProps) => {
183183
if (pycode === '') {
184184
const state = {result: {tex: "", python: "", mathml: ""}};
185185
setCurrentAttempt(questionId, { type: 'formula', value: JSON.stringify(sanitiseInequalityState(state)), pythonExpression: ""});
186-
setInitialEditorSymbols([]);
186+
initialEditorSymbols.current = [];
187187
} else if (parsedExpression.length === 1) {
188188
// This and the next one are using pycode instead of textInput because React will update the state whenever it sees fit
189189
// so textInput will almost certainly be out of sync with pycode which is the current content of the text box.
@@ -218,9 +218,10 @@ const IsaacSymbolicQuestionComponent = (props: IsaacSymbolicQuestionProps) => {
218218
close={closeModal(window.scrollY)}
219219
onEditorStateChange={updateState}
220220
availableSymbols={doc.availableSymbols}
221-
initialEditorSymbols={initialEditorSymbols}
221+
initialEditorSymbols={initialEditorSymbols.current}
222222
visible={modalVisible}
223223
editorMode='maths'
224+
questionDoc={doc}
224225
/>}
225226
<div className="eqn-editor-input">
226227
<div ref={hiddenEditorRef} className="equation-editor-text-entry" style={{height: 0, overflow: "hidden", visibility: "hidden"}} />

src/app/components/elements/modals/InequalityModal.tsx

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import _uniqWith from 'lodash/uniqWith';
66
import _isEqual from 'lodash/isEqual';
77
import {parsePseudoSymbolicAvailableSymbols, sanitiseInequalityState} from "../../../services/questions";
88
import {GREEK_LETTERS_MAP} from '../../../services/constants';
9+
import { IsaacContentValueOrChildren } from '../../content/IsaacContentValueOrChildren';
10+
import { ContentDTO } from '../../../../IsaacApiTypes';
911

1012
class MenuItem {
1113
public type: string;
@@ -36,6 +38,7 @@ interface InequalityModalProps {
3638
editorMode?: string;
3739
logicSyntax?: string;
3840
visible: boolean;
41+
questionDoc?: ContentDTO;
3942
}
4043

4144
interface InequalityModalState {
@@ -98,6 +101,48 @@ export class InequalityModal extends React.Component<InequalityModalProps> {
98101
private _logFunctionNames = ["ln", "log"];
99102

100103
private _chemicalElements = ["H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og"];
104+
private _chemicalParticles: {[key: string]: { type: string; menu: { label: string; texLabel: boolean; className?: string; fontSize?: string }; properties: object } } = {
105+
alpha: {
106+
type: 'Particle',
107+
menu: { label: '\\alpha', texLabel: true },
108+
properties: { particle: 'α', type: 'alpha' }
109+
},
110+
beta: {
111+
type: 'Particle',
112+
menu: { label: '\\beta', texLabel: true },
113+
properties: { particle: 'β', type: 'beta' }
114+
},
115+
gamma: {
116+
type: 'Particle',
117+
menu: { label: '\\gamma', texLabel: true },
118+
properties: { particle: 'γ', type: 'gamma' }
119+
},
120+
neutrino: {
121+
type: 'Particle',
122+
menu: { label: '\\nu', texLabel: true },
123+
properties: { particle: 'ν', type: 'neutrino' }
124+
},
125+
antineutrino: {
126+
type: 'Particle',
127+
menu: { label: '\\bar{\\nu}', texLabel: true },
128+
properties: { particle: 'ν̅', type: 'antineutrino' }
129+
},
130+
proton: {
131+
type: 'Particle',
132+
menu: { label: '\\text{p}', texLabel: true },
133+
properties: { particle: 'p', type: 'proton' }
134+
},
135+
neutron: {
136+
type: 'Particle',
137+
menu: { label: '\\text{n}', texLabel: true },
138+
properties: { particle: 'n', type: 'neutron' }
139+
},
140+
electron: {
141+
type: 'Particle',
142+
menu: { label: '\\text{e}', texLabel: true },
143+
properties: { particle: 'e', type: 'electron' }
144+
}
145+
};
101146

102147
private _differentialRegex = /^(Delta|delta|d)\s*(?:\^([0-9]+))?\s*([a-zA-Z]+(?:(?:_|\^).+)?)/;
103148
private _availableSymbols?: string[];
@@ -283,9 +328,12 @@ export class InequalityModal extends React.Component<InequalityModalProps> {
283328
}
284329
} else {
285330
// Everything else is a letter, unless we are doing chemistry
286-
if (this.props.editorMode === 'chemistry' && /^[A-Z]/.test(availableSymbol)) {
331+
if (this.props.editorMode === 'chemistry') {
287332
// Available chemical elements
288-
customMenuItems.chemicalElements.push(this.makeChemicalElementMenuItem(availableSymbol));
333+
const item = this.makeChemicalElementMenuItem(availableSymbol);
334+
if (item) {
335+
customMenuItems.chemicalElements.push(item);
336+
}
289337
} else {
290338
const item = this.makeLetterMenuItem(availableSymbol);
291339
if (item) {
@@ -333,7 +381,7 @@ export class InequalityModal extends React.Component<InequalityModalProps> {
333381
this.setState((prevState: InequalityModalState) => ({
334382
menuItems: {
335383
...prevState.menuItems,
336-
chemicalElements: this._chemicalElements.map( element => this.makeChemicalElementMenuItem(element) ),
384+
chemicalElements: [ ...this._chemicalElements, ...Object.keys(this._chemicalParticles) ].map( element => this.makeChemicalElementMenuItem(element) ),
337385
}
338386
}));
339387
} else {
@@ -478,7 +526,7 @@ export class InequalityModal extends React.Component<InequalityModalProps> {
478526
new MenuItem("AbsoluteValue", {}, { label: '\\small{|x|}', texLabel: true, className: 'abs' }),
479527
new MenuItem("Radix", {}, { label: '\\small{\\sqrt{x}}', texLabel: true, className: 'radix sqrt' }),
480528
new MenuItem("Relation", { relation: '=' }, { label: '=', texLabel: true, className: 'relation equal' }),
481-
new MenuItem("Relation", { relation: '<' }, { label: '>', texLabel: true, className: 'relation less' }),
529+
new MenuItem("Relation", { relation: '<' }, { label: '<', texLabel: true, className: 'relation less' }),
482530
new MenuItem("Relation", { relation: '>' }, { label: '>', texLabel: true, className: 'relation greater' }),
483531
new MenuItem("Relation", { relation: '<=' }, { label: '\\leq', texLabel: true, className: 'relation less-or-equal' }),
484532
new MenuItem("Relation", { relation: '>=' }, { label: '\\geq', texLabel: true, className: 'relation greater-or-equal' }),
@@ -672,7 +720,11 @@ export class InequalityModal extends React.Component<InequalityModalProps> {
672720
}
673721

674722
private makeChemicalElementMenuItem(symbol: string) {
675-
return new MenuItem('ChemicalElement', { element: symbol }, { label: `\\text{${symbol}}`, texLabel: true, className: `chemical-element ${symbol}` });
723+
if (this._chemicalElements.includes(symbol)) {
724+
return new MenuItem('ChemicalElement', { element: symbol }, { label: `\\text{${symbol}}`, texLabel: true, className: `chemical-element ${symbol}` });
725+
} else if (this._chemicalParticles.hasOwnProperty(symbol)) {
726+
return new MenuItem('Particle', this._chemicalParticles[symbol].properties, { ...this._chemicalParticles[symbol].menu, className: `chemical-particle ${symbol}` });
727+
}
676728
}
677729

678730
private makeChemicalStatesMenuItems() {
@@ -802,10 +854,14 @@ export class InequalityModal extends React.Component<InequalityModalProps> {
802854
const trashCanRect = trashCan.getBoundingClientRect();
803855
if (trashCanRect && x >= trashCanRect.left && x <= trashCanRect.right && y >= trashCanRect.top && y <= trashCanRect.bottom) {
804856
trashCan.classList.add('active');
805-
this.setState({ trashActive: true });
857+
if (!this.state.trashActive) {
858+
this.setState({ trashActive: true });
859+
}
806860
} else {
807861
trashCan.classList.remove('active');
808-
this.setState({ trashActive: false });
862+
if (this.state.trashActive) {
863+
this.setState({ trashActive: false });
864+
}
809865
}
810866
}
811867

@@ -1105,6 +1161,12 @@ export class InequalityModal extends React.Component<InequalityModalProps> {
11051161
>Centre</div>
11061162
<div id="inequality-trash" className="inequality-ui trash button">Trash</div>
11071163
<div className="beta-badge">beta</div>
1164+
{(this.props.questionDoc?.value || (this.props.questionDoc?.children && this.props.questionDoc?.children?.length > 0)) && <div className="question-reminder">
1165+
<IsaacContentValueOrChildren value={this.props.questionDoc.value} encoding={this.props.questionDoc.encoding}>
1166+
{this.props.questionDoc?.children}
1167+
</IsaacContentValueOrChildren>
1168+
</div>}
1169+
<div className="orientation-warning">The Isaac Equation Editor may only be used in landscape mode. Please rotate your device.</div>
11081170
{ menu }
11091171
</div>;
11101172
}

src/app/components/elements/panels/UserPassword.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,24 +155,19 @@ export const UserPassword = (
155155
<Row>
156156
<Col md={{size: 6, offset: 3}}>
157157
<FormGroup>
158-
{/* TODO:ENABLE_GOOGLE */}
159158
<h4>Linked Accounts</h4>
160159
<Col className="text-center">
161160
<div className="vertical-center ml-2">
162161
<input
163162
type="button"
164163
id="linked-accounts-no-password"
165164
className="linked-account-button google-button"
166-
disabled={SITE_SUBJECT === SITE.PHY}
167165
onClick={() => dispatch(authenticationProvidersUsed("GOOGLE") ? unlinkAccount("GOOGLE") : linkAccount("GOOGLE"))}
168166
/>
169-
<Label htmlFor="linked-accounts-no-passoword" className={`ml-2 mb-0 ${SITE_SUBJECT === SITE.PHY ? "text-muted" : ""}`}>
167+
<Label htmlFor="linked-accounts-no-passoword" className="ml-2 mb-0">
170168
{authenticationProvidersUsed("GOOGLE") ? " Remove linked Google account" : " Add linked Google account"}
171169
</Label>
172170
</div>
173-
{SITE_SUBJECT === SITE.PHY && <p className="mt-3 mb-0">
174-
Google authentication is <strong>temporarily disabled</strong><br />for the beta version of the site.
175-
</p>}
176171
</Col>
177172
</FormGroup>
178173
</Col>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from "react";
2+
import {Alert, Container} from "reactstrap";
3+
import {Link} from "react-router-dom";
4+
5+
export const PhysicsSiteRedesignBanner = () => (
6+
<Alert color="warning" className="mb-0 py-3">
7+
<Container className="text-center">
8+
<b>{"Welcome to the new-look Isaac Physics site! "}</b>
9+
<Link to="/pages/new_site_2020">Click here to find out what has changed<span className="sr-only"> in the site redesign</span></Link>.
10+
</Container>
11+
</Alert>
12+
);

src/app/components/pages/LogIn.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,9 @@ export const LogIn = () => {
188188

189189
<Row className="my-4">
190190
<Col className="text-center">
191-
{/* TODO:ENABLE_GOOGLE */}
192-
<Button block className="login-google" disabled={SITE_SUBJECT === SITE.PHY} color="link" onClick={logInWithGoogle}>
191+
<Button block className="login-google" color="link" onClick={logInWithGoogle}>
193192
Log in with Google
194193
</Button>
195-
{SITE_SUBJECT === SITE.PHY && <p className="mt-3 mb-0">
196-
Google authentication is <strong>temporarily disabled</strong><br />for the beta version of the site.
197-
</p>}
198194
</Col>
199195
</Row>
200196
</React.Fragment>

0 commit comments

Comments
 (0)