Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Expression w/ Label POC #1378

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions packages/math-input/src/components/input/math-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,6 @@ const inputMaxWidth = 128;
const numeralHeightPx = 20;
const totalDesiredPadding = 12;
const minHeightPx = numeralHeightPx + totalDesiredPadding * 2;
const minWidthPx = 64;

const styles = StyleSheet.create({
input: {
Expand All @@ -1068,7 +1067,7 @@ const inlineStyles = {
innerContainer: {
backgroundColor: "white",
minHeight: minHeightPx,
minWidth: minWidthPx,
width: "100%",
maxWidth: inputMaxWidth,
boxSizing: "border-box",
position: "relative",
Expand Down
189 changes: 105 additions & 84 deletions packages/perseus/src/components/math-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,96 +291,117 @@ class InnerMathInput extends React.Component<InnerProps, State> {
className = className + " " + this.props.className;
}

const textareaId = `math-input_${Date.now()}`;
if (this.__mathFieldWrapperRef) {
const textarea =
this.__mathFieldWrapperRef.getElementsByTagName("textarea");
textarea[0].setAttribute("id", textareaId);
}

return (
<View
style={[
styles.outerWrapper,
this.state.focused && styles.wrapperFocused,
this.props.hasError && styles.wrapperError,
]}
>
<div
<View style={{padding: "15px 4px 0"}}>
<label
htmlFor={textareaId}
style={{
display: "flex",
padding: 1,
}}
onClick={(e) => {
// Prevent the click into the input from registering
// so that the keypad popover doesn't close when
// switching focus to the input.
e.stopPropagation();

const mathField = this.mathField();
if (!mathField) {
return;
}
this.setState({
cursorContext: getCursorContext(mathField),
});
fontSize: "12px",
lineHeight: "10px",
}}
>
<span
className={className}
ref={(ref) => (this.__mathFieldWrapperRef = ref)}
aria-label={this.props.labelText}
onFocus={() => this.focus()}
onBlur={() => this.blur()}
/>
<Popover
opened={this.state.keypadOpen}
onClose={() => this.closeKeypad()}
dismissEnabled
content={() => (
<PopoverContentCore
closeButtonVisible
style={styles.popoverContent}
>
<DesktopKeypad
onAnalyticsEvent={
this.props.analytics.onAnalyticsEvent
}
extraKeys={this.props.extraKeys}
onClickKey={this.handleKeypadPress}
cursorContext={this.state.cursorContext}
convertDotToTimes={
this.props.convertDotToTimes
}
{...(this.props.keypadButtonSets ??
mapButtonSets(this.props?.buttonSets))}
/>
</PopoverContentCore>
)}
Hello world
</label>
<View
style={[
styles.outerWrapper,
this.state.focused && styles.wrapperFocused,
this.props.hasError && styles.wrapperError,
]}
>
<div
style={{
display: "flex",
padding: 1,
}}
onClick={(e) => {
// Prevent the click into the input from registering
// so that the keypad popover doesn't close when
// switching focus to the input.
e.stopPropagation();

const mathField = this.mathField();
if (!mathField) {
return;
}
this.setState({
cursorContext: getCursorContext(mathField),
});
}}
>
{this.props.buttonsVisible === "never" ? (
<MathInputIcon
hovered={false}
focused={false}
active={false}
/>
) : (
<Clickable
aria-label={
this.state.keypadOpen
? this.context.strings.closeKeypad
: this.context.strings.openKeypad
}
role="button"
onClick={() =>
this.state.keypadOpen
? this.closeKeypad()
: this.openKeypad()
}
>
{(props) => (
<MathInputIcon
active={this.state.keypadOpen}
{...props}
<span
className={className}
ref={(ref) => (this.__mathFieldWrapperRef = ref)}
aria-label={this.props.labelText}
onFocus={() => this.focus()}
onBlur={() => this.blur()}
/>
<Popover
opened={this.state.keypadOpen}
onClose={() => this.closeKeypad()}
dismissEnabled
content={() => (
<PopoverContentCore
closeButtonVisible
style={styles.popoverContent}
>
<DesktopKeypad
onAnalyticsEvent={
this.props.analytics
.onAnalyticsEvent
}
extraKeys={this.props.extraKeys}
onClickKey={this.handleKeypadPress}
cursorContext={this.state.cursorContext}
convertDotToTimes={
this.props.convertDotToTimes
}
{...(this.props.keypadButtonSets ??
mapButtonSets(
this.props?.buttonSets,
))}
/>
)}
</Clickable>
)}
</Popover>
</div>
</PopoverContentCore>
)}
>
{this.props.buttonsVisible === "never" ? (
<MathInputIcon
hovered={false}
focused={false}
active={false}
/>
) : (
<Clickable
aria-label={
this.state.keypadOpen
? this.context.strings.closeKeypad
: this.context.strings.openKeypad
}
role="button"
onClick={() =>
this.state.keypadOpen
? this.closeKeypad()
: this.openKeypad()
}
>
{(props) => (
<MathInputIcon
active={this.state.keypadOpen}
{...props}
/>
)}
</Clickable>
)}
</Popover>
</div>
</View>
</View>
);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/perseus/src/styles/perseus-renderer.less
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@
font-size: 18px;
line-height: 22px;
margin: 22px 0px;
display: inline-flex;
flex-wrap: wrap;
align-items: end;
}

// HACK(yejia): Override the font size and line height for blurbs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from "react";

import {ServerItemRendererWithDebugUI} from "../../../../../testing/server-item-renderer-with-debug-ui";
import {
complexExpressionWidget,
expressionItem2,
expressionItem3,
} from "../__testdata__/expression.testdata";
Expand Down Expand Up @@ -113,7 +114,7 @@ export const Mobile = (args: StoryArgs): React.ReactElement => {
to use the custom keypad.
</p>
<WrappedKeypadContext
item={expressionItem3}
item={complexExpressionWidget}
customKeypad
isMobile
/>
Expand Down
114 changes: 114 additions & 0 deletions packages/perseus/src/widgets/__testdata__/expression.testdata.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General question: where is the best place to get test data like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll DM you (I'm always a little hesitant to post links to internal tools on GH).

Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,117 @@ export const randomExpressionGenerator = (): PerseusRenderer => {
},
};
};

export const complexExpressionWidget: PerseusItem = {
answerArea: Object.fromEntries(
ItemExtras.map((extra) => [extra, false]),
) as PerseusAnswerArea,
_multi: null,
answer: null,
hints: [
{
content:
"$f(x, y, z) = (f_0, f_1, f_2)$\n\nThe curl of $f$:\n\n$\\begin{align}\n\\text{curl}(f) &= \\det \\begin{bmatrix}\n{\\hat{\\imath}} & \\hat{\\jmath} & \\hat{k} \\\\ \\\\\n\\dfrac{\\partial}{\\partial x} & \\dfrac{\\partial}{\\partial y} & \\dfrac{\\partial}{\\partial z} \\\\ \\\\\nf_0 & f_1 & f_2\n\\end{bmatrix} \\\\ \\\\\n&= \\left( \\dfrac{\\partial f_2}{\\partial y} - \\dfrac{\\partial f_1}{\\partial z} \\right) \\hat{\\imath} \\\\ \\\\\n&+ \\left( \\dfrac{\\partial f_0}{\\partial z} - \\dfrac{\\partial f_2}{\\partial x} \\right) \\hat{\\jmath} \\\\ \\\\\n&+ \\left( \\dfrac{\\partial f_1}{\\partial x} - \\dfrac{\\partial f_0}{\\partial y} \\right) \\hat{k}\n\\end{align}$",
images: {},
replace: false,
widgets: {},
},
{
content:
"$\\begin{align}\nf_0(x, y, z) &= \\tan(y) \\\\ \\\\\nf_1(x, y, z) &= z\\sin(x) \\\\ \\\\\nf_2(x, y, z) &= 2\\cos(x)\n\\end{align}$\n\nLet's calculate all the partial derivatives we'll need.\n\n | $f_0$ | $f_1$ | $f_2$\n:-: | :-: | :-: | :-:\n$\\dfrac{\\partial}{\\partial x}$ | | $z\\cos(x)$ | $-2\\sin(x)$\n$\\dfrac{\\partial}{\\partial y}$| $\\sec^2(y)$ | | $0$\n$\\dfrac{\\partial}{\\partial z}$ | $0$ | $\\sin(x)$ | \n\nNow we can put it all together.\n\n$\\begin{align}\n\\text{curl}(f) &= \\left( \\dfrac{\\partial f_2}{\\partial y} - \\dfrac{\\partial f_1}{\\partial z} \\right) \\hat{\\imath} \\\\ \\\\\n&+ \\left( \\dfrac{\\partial f_0}{\\partial z} - \\dfrac{\\partial f_2}{\\partial x} \\right) \\hat{\\jmath} \\\\ \\\\\n&+ \\left( \\dfrac{\\partial f_1}{\\partial x} - \\dfrac{\\partial f_0}{\\partial y} \\right) \\hat{k} \\\\ \\\\\n&= (0 - \\sin(x)) \\hat{\\imath} + (0 + 2\\sin(x)) \\hat{\\jmath} \\\\ \\\\\n&+ (z\\cos(x) - \\sec^2(y)) \\hat{k} \\\\ \\\\\n&= -\\sin(x) \\hat{\\imath} + 2\\sin(x) \\hat{\\jmath} + (z\\cos(x) - \\sec^2(y)) \\hat{k}\n\\end{align}$ \n\nWe could also write the $\\hat{k}$-component as $z\\cos(x) - \\dfrac{1}{\\cos^2(y)}$ because $\\sec(y) = \\dfrac{1}{\\cos(y)}$.",
images: {},
replace: false,
widgets: {},
},
{
content:
"In conclusion:\n\n$\\text{curl}(f) = -\\sin(x) \\hat{\\imath} + 2\\sin(x) \\hat{\\jmath} + (z\\cos(x) - \\sec^2(y)) \\hat{k}$",
images: {},
replace: false,
widgets: {},
},
],
itemDataVersion: {
major: 0,
minor: 1,
},
question: {
content:
"$f(x, y, z) = (\\tan(y), z\\sin(x), 2\\cos(x))$\n\n$\\text{curl}(f) = $ [[☃ expression 1]] $\\hat{\\imath} + $ [[☃ expression 2]] $\\hat{\\jmath} + $ [[☃ expression 3]] $\\hat{k}$",
images: {},
widgets: {
"expression 1": {
alignment: "default",
graded: true,
options: {
answerForms: [
{
considered: "correct",
form: false,
key: "0",
simplify: false,
value: "-\\sin\\left(x\\right)",
},
],
buttonSets: ["basic", "trig", "prealgebra"],
functions: ["f", "g", "h"],
times: false,
},
static: false,
type: "expression",
version: {
major: 1,
minor: 0,
},
},
"expression 2": {
alignment: "default",
graded: true,
options: {
answerForms: [
{
considered: "correct",
form: false,
key: "0",
simplify: false,
value: "2\\sin\\left(x\\right)",
},
],
buttonSets: ["basic", "trig", "prealgebra"],
functions: ["f", "g", "h"],
times: false,
},
static: false,
type: "expression",
version: {
major: 1,
minor: 0,
},
},
"expression 3": {
alignment: "default",
graded: true,
options: {
answerForms: [
{
considered: "correct",
form: false,
key: "0",
simplify: false,
value: "z\\cos\\left(x\\right)-\\sec^{2}\\left(y\\right)",
},
],
buttonSets: ["basic", "trig", "prealgebra"],
functions: ["f", "g", "h"],
times: false,
},
static: false,
type: "expression",
version: {
major: 1,
minor: 0,
},
},
},
},
};
Loading
Loading