@khanacademy/perseus-editor 2.14.2 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/es/index.js +99 -24
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +99 -22
- package/dist/index.js.map +1 -1
- package/dist/item-extras-editor.d.ts +12 -12
- package/package.json +2 -2
- package/src/__stories__/item-editor.stories.tsx +41 -0
- package/src/__tests__/{items-extras-editors.test.tsx → item-extras-editor.test.tsx} +17 -0
- package/src/item-extras-editor.tsx +184 -132
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
periodicTable: boolean;
|
|
6
|
-
periodicTableWithKey: boolean;
|
|
7
|
-
tTable: boolean;
|
|
8
|
-
zTable: boolean;
|
|
2
|
+
import type { PerseusAnswerArea } from "@khanacademy/perseus";
|
|
3
|
+
type Props = PerseusAnswerArea & {
|
|
4
|
+
onChange: (props: Partial<PerseusAnswerArea>) => void;
|
|
9
5
|
};
|
|
10
|
-
type
|
|
11
|
-
|
|
6
|
+
type State = {
|
|
7
|
+
financialCalculatorOptionsExpanded: boolean;
|
|
12
8
|
};
|
|
13
|
-
declare class ItemExtrasEditor extends React.Component<Props> {
|
|
14
|
-
static defaultProps:
|
|
15
|
-
|
|
9
|
+
declare class ItemExtrasEditor extends React.Component<Props, State> {
|
|
10
|
+
static defaultProps: PerseusAnswerArea;
|
|
11
|
+
state: {
|
|
12
|
+
financialCalculatorOptionsExpanded: boolean;
|
|
13
|
+
};
|
|
14
|
+
componentDidUpdate(prevProps: Readonly<Props>): void;
|
|
15
|
+
serialize: () => PerseusAnswerArea;
|
|
16
16
|
render(): React.ReactNode;
|
|
17
17
|
}
|
|
18
18
|
export default ItemExtrasEditor;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Perseus editors",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.15.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@khanacademy/kas": "^0.3.7",
|
|
26
26
|
"@khanacademy/kmath": "^0.1.8",
|
|
27
|
-
"@khanacademy/perseus": "^17.
|
|
27
|
+
"@khanacademy/perseus": "^17.3.0",
|
|
28
28
|
"@khanacademy/perseus-core": "1.4.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable react/prop-types */
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import ItemExtrasEditor from "../item-extras-editor";
|
|
5
|
+
|
|
6
|
+
import type {Meta, StoryObj} from "@storybook/react";
|
|
7
|
+
|
|
8
|
+
import "../styles/perseus-editor.less";
|
|
9
|
+
|
|
10
|
+
type Props = React.ComponentProps<typeof ItemExtrasEditor>;
|
|
11
|
+
|
|
12
|
+
const Wrapper = (props: Props) => {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
14
|
+
const {onChange, ...rest} = props;
|
|
15
|
+
const [extras, setExtras] =
|
|
16
|
+
React.useState<Partial<typeof ItemExtrasEditor.defaultProps>>(rest);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<ItemExtrasEditor
|
|
20
|
+
{...extras}
|
|
21
|
+
onChange={(e) => {
|
|
22
|
+
props.onChange?.(e); // to register action in storybook
|
|
23
|
+
setExtras((prevExtras) => ({...prevExtras, ...e}));
|
|
24
|
+
}}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const story: Meta<Props> = {
|
|
30
|
+
title: "Perseus/Editor/Item Extras Editor",
|
|
31
|
+
component: ItemExtrasEditor,
|
|
32
|
+
render: (args) => <Wrapper {...args} />,
|
|
33
|
+
argTypes: {onChange: {action: "changed"}},
|
|
34
|
+
};
|
|
35
|
+
export default story;
|
|
36
|
+
|
|
37
|
+
type Story = StoryObj<typeof ItemExtrasEditor>;
|
|
38
|
+
|
|
39
|
+
export const Default: Story = {
|
|
40
|
+
args: {...ItemExtrasEditor.defaultProps},
|
|
41
|
+
};
|
|
@@ -65,4 +65,21 @@ describe("ItemExtrasEditor", () => {
|
|
|
65
65
|
periodicTableWithKey: false,
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
it("should call onChange on dependent values when financialCalculator is checked", () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
const onChangeMock = jest.fn();
|
|
72
|
+
render(<ItemExtrasEditor onChange={onChangeMock} />);
|
|
73
|
+
const checkbox = screen.getByLabelText("Show financial calculator:");
|
|
74
|
+
|
|
75
|
+
// Act
|
|
76
|
+
userEvent.click(checkbox);
|
|
77
|
+
|
|
78
|
+
// Assert
|
|
79
|
+
expect(onChangeMock).toHaveBeenCalledWith({
|
|
80
|
+
financialCalculatorMonthlyPayment: true,
|
|
81
|
+
financialCalculatorTotalAmount: true,
|
|
82
|
+
financialCalculatorTimeToPayOff: true,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
68
85
|
});
|
|
@@ -1,178 +1,230 @@
|
|
|
1
|
-
import {components} from "@khanacademy/perseus";
|
|
1
|
+
import {components, ItemExtras} from "@khanacademy/perseus";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
|
|
4
|
+
import type {PerseusAnswerArea} from "@khanacademy/perseus";
|
|
5
|
+
|
|
4
6
|
const {InfoTip} = components;
|
|
5
7
|
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
chi2Table: boolean;
|
|
9
|
-
periodicTable: boolean;
|
|
10
|
-
periodicTableWithKey: boolean;
|
|
11
|
-
tTable: boolean;
|
|
12
|
-
zTable: boolean;
|
|
8
|
+
type Props = PerseusAnswerArea & {
|
|
9
|
+
onChange: (props: Partial<PerseusAnswerArea>) => void;
|
|
13
10
|
};
|
|
14
11
|
|
|
15
|
-
type
|
|
16
|
-
|
|
12
|
+
type State = {
|
|
13
|
+
financialCalculatorOptionsExpanded: boolean;
|
|
17
14
|
};
|
|
18
15
|
|
|
19
|
-
class ItemExtrasEditor extends React.Component<Props> {
|
|
20
|
-
static defaultProps:
|
|
16
|
+
class ItemExtrasEditor extends React.Component<Props, State> {
|
|
17
|
+
static defaultProps: PerseusAnswerArea = {
|
|
21
18
|
calculator: false,
|
|
22
19
|
chi2Table: false,
|
|
20
|
+
financialCalculatorMonthlyPayment: false,
|
|
21
|
+
financialCalculatorTotalAmount: false,
|
|
22
|
+
financialCalculatorTimeToPayOff: false,
|
|
23
23
|
periodicTable: false,
|
|
24
24
|
periodicTableWithKey: false,
|
|
25
25
|
tTable: false,
|
|
26
26
|
zTable: false,
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
state = {
|
|
30
|
+
financialCalculatorOptionsExpanded: false,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
componentDidUpdate(prevProps: Readonly<Props>): void {
|
|
34
|
+
// If no financial calculator options are checked, uncheck the
|
|
35
|
+
// financial calculator option.
|
|
36
|
+
if (
|
|
37
|
+
this.state.financialCalculatorOptionsExpanded &&
|
|
38
|
+
!this.props.financialCalculatorMonthlyPayment &&
|
|
39
|
+
!this.props.financialCalculatorTotalAmount &&
|
|
40
|
+
!this.props.financialCalculatorTimeToPayOff
|
|
41
|
+
) {
|
|
42
|
+
this.setState({financialCalculatorOptionsExpanded: false});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
serialize: () => PerseusAnswerArea = () => {
|
|
47
|
+
const data = {...ItemExtrasEditor.defaultProps};
|
|
48
|
+
for (const key of ItemExtras) {
|
|
49
|
+
data[key] = !!this.props[key];
|
|
50
|
+
}
|
|
51
|
+
return data;
|
|
38
52
|
};
|
|
39
53
|
|
|
40
54
|
render(): React.ReactNode {
|
|
41
55
|
return (
|
|
42
56
|
<div className="perseus-answer-editor">
|
|
43
57
|
<div className="perseus-answer-options">
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}}
|
|
55
|
-
/>
|
|
56
|
-
</label>
|
|
57
|
-
<InfoTip>
|
|
58
|
-
Use the calculator when completing difficult
|
|
59
|
-
calculations is NOT the intent of the question.
|
|
60
|
-
DON’T use the calculator when testing the student’s
|
|
61
|
-
ability to complete different types of computations.
|
|
62
|
-
</InfoTip>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div>
|
|
66
|
-
<label>
|
|
67
|
-
Show periodic table:{" "}
|
|
68
|
-
<input
|
|
69
|
-
type="checkbox"
|
|
70
|
-
checked={this.props.periodicTable}
|
|
71
|
-
onChange={(e) => {
|
|
72
|
-
this.props.onChange({
|
|
73
|
-
periodicTable: e.target.checked,
|
|
74
|
-
// If the periodic table is unchecked,
|
|
75
|
-
// this needs to be reset. If table is
|
|
76
|
-
// checked, it should already be false.
|
|
77
|
-
periodicTableWithKey: false,
|
|
78
|
-
});
|
|
79
|
-
}}
|
|
80
|
-
/>
|
|
81
|
-
</label>
|
|
82
|
-
<InfoTip>
|
|
83
|
-
This provides the student with the ability to view a
|
|
84
|
-
periodic table of the elements, e.g., for answering
|
|
85
|
-
chemistry questions.
|
|
86
|
-
</InfoTip>
|
|
87
|
-
</div>
|
|
58
|
+
<ItemExtraCheckbox
|
|
59
|
+
label="Show calculator:"
|
|
60
|
+
infoTip="Use the calculator when completing difficult calculations is NOT the intent of the question. DON’T use the calculator when testing the student’s ability to complete different types of computations."
|
|
61
|
+
checked={this.props.calculator}
|
|
62
|
+
onChange={(e) => {
|
|
63
|
+
this.props.onChange({
|
|
64
|
+
calculator: e.target.checked,
|
|
65
|
+
});
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
88
68
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
69
|
+
<ItemExtraCheckbox
|
|
70
|
+
label="Show financial calculator:"
|
|
71
|
+
infoTip="This provides the student with the ability to view a financial calculator, e.g., for answering financial questions. Once checked, requires at least one of the three options below to be checked."
|
|
72
|
+
checked={this.state.financialCalculatorOptionsExpanded}
|
|
73
|
+
onChange={(e) => {
|
|
74
|
+
this.setState({
|
|
75
|
+
financialCalculatorOptionsExpanded:
|
|
76
|
+
e.target.checked,
|
|
77
|
+
});
|
|
78
|
+
// If the financial calculator is unchecked,
|
|
79
|
+
// this needs to be reset. All checked by
|
|
80
|
+
// default.
|
|
81
|
+
this.props.onChange({
|
|
82
|
+
financialCalculatorMonthlyPayment:
|
|
83
|
+
e.target.checked,
|
|
84
|
+
financialCalculatorTotalAmount:
|
|
85
|
+
e.target.checked,
|
|
86
|
+
financialCalculatorTimeToPayOff:
|
|
87
|
+
e.target.checked,
|
|
88
|
+
});
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
110
91
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
checked={
|
|
92
|
+
{this.state.financialCalculatorOptionsExpanded && (
|
|
93
|
+
<>
|
|
94
|
+
<ItemExtraCheckbox
|
|
95
|
+
label="Include monthly payment:"
|
|
96
|
+
infoTip="This provides the student with the ability to view a monthly payment calculator; e.g., given a loan amount, interest rate, and term, what is the monthly payment?"
|
|
97
|
+
checked={
|
|
98
|
+
this.props.financialCalculatorMonthlyPayment
|
|
99
|
+
}
|
|
117
100
|
onChange={(e) => {
|
|
118
101
|
this.props.onChange({
|
|
119
|
-
|
|
102
|
+
financialCalculatorMonthlyPayment:
|
|
103
|
+
e.target.checked,
|
|
120
104
|
});
|
|
121
105
|
}}
|
|
106
|
+
indent
|
|
122
107
|
/>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
<div>
|
|
132
|
-
<label>
|
|
133
|
-
Show t table (statistics):{" "}
|
|
134
|
-
<input
|
|
135
|
-
type="checkbox"
|
|
136
|
-
checked={this.props.tTable}
|
|
108
|
+
<ItemExtraCheckbox
|
|
109
|
+
label="Include total amount:"
|
|
110
|
+
infoTip="This provides the student with the ability to view a total amount calculator; e.g., given a monthly payment, interest rate, and term, what is the total amount to be paid?"
|
|
111
|
+
checked={
|
|
112
|
+
this.props.financialCalculatorTotalAmount
|
|
113
|
+
}
|
|
137
114
|
onChange={(e) => {
|
|
138
115
|
this.props.onChange({
|
|
139
|
-
|
|
116
|
+
financialCalculatorTotalAmount:
|
|
117
|
+
e.target.checked,
|
|
140
118
|
});
|
|
141
119
|
}}
|
|
120
|
+
indent
|
|
142
121
|
/>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
</InfoTip>
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
<div>
|
|
153
|
-
<label>
|
|
154
|
-
Show chi-squared table (statistics):{" "}
|
|
155
|
-
<input
|
|
156
|
-
type="checkbox"
|
|
157
|
-
checked={this.props.chi2Table}
|
|
122
|
+
<ItemExtraCheckbox
|
|
123
|
+
label="Include time-to-pay-off:"
|
|
124
|
+
infoTip="This provides the student with the ability to view a time to pay off calculator; e.g., given a loan amount, interest rate, and monthly payment, how long will it take to pay off the loan?"
|
|
125
|
+
checked={
|
|
126
|
+
this.props.financialCalculatorTimeToPayOff
|
|
127
|
+
}
|
|
158
128
|
onChange={(e) => {
|
|
159
129
|
this.props.onChange({
|
|
160
|
-
|
|
130
|
+
financialCalculatorTimeToPayOff:
|
|
131
|
+
e.target.checked,
|
|
161
132
|
});
|
|
162
133
|
}}
|
|
134
|
+
indent
|
|
163
135
|
/>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
136
|
+
</>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
<ItemExtraCheckbox
|
|
140
|
+
label="Show periodic table:"
|
|
141
|
+
infoTip="This provides the student with the ability to view a periodic table of the elements, e.g., for answering chemistry questions."
|
|
142
|
+
checked={this.props.periodicTable}
|
|
143
|
+
onChange={(e) => {
|
|
144
|
+
this.props.onChange({
|
|
145
|
+
periodicTable: e.target.checked,
|
|
146
|
+
// If the periodic table is unchecked,
|
|
147
|
+
// this needs to be reset. If table is
|
|
148
|
+
// checked, it should already be false.
|
|
149
|
+
periodicTableWithKey: false,
|
|
150
|
+
});
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
153
|
+
|
|
154
|
+
{this.props.periodicTable && (
|
|
155
|
+
<ItemExtraCheckbox
|
|
156
|
+
label="Include key/legend with periodic table:"
|
|
157
|
+
infoTip="Include a key for HS courses; omit for AP chemistry."
|
|
158
|
+
checked={this.props.periodicTableWithKey}
|
|
159
|
+
onChange={(e) => {
|
|
160
|
+
this.props.onChange({
|
|
161
|
+
periodicTableWithKey: e.target.checked,
|
|
162
|
+
});
|
|
163
|
+
}}
|
|
164
|
+
indent
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
<ItemExtraCheckbox
|
|
169
|
+
label="Show z table (statistics):"
|
|
170
|
+
infoTip="This provides the student with the ability to view a table of critical values for the z distribution, e.g. for answering statistics questions."
|
|
171
|
+
checked={this.props.zTable}
|
|
172
|
+
onChange={(e) => {
|
|
173
|
+
this.props.onChange({
|
|
174
|
+
zTable: e.target.checked,
|
|
175
|
+
});
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
<ItemExtraCheckbox
|
|
180
|
+
label="Show t table (statistics):"
|
|
181
|
+
infoTip="This provides the student with the ability to view a table of critical values for the Student's t distribution, e.g. for answering statistics questions."
|
|
182
|
+
checked={this.props.tTable}
|
|
183
|
+
onChange={(e) => {
|
|
184
|
+
this.props.onChange({
|
|
185
|
+
tTable: e.target.checked,
|
|
186
|
+
});
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
|
|
190
|
+
<ItemExtraCheckbox
|
|
191
|
+
label="Show chi-squared table (statistics):"
|
|
192
|
+
infoTip="This provides the student with the ability to view a table of critical values for the chi-squared distribution, e.g. for answering statistics questions."
|
|
193
|
+
checked={this.props.chi2Table}
|
|
194
|
+
onChange={(e) => {
|
|
195
|
+
this.props.onChange({
|
|
196
|
+
chi2Table: e.target.checked,
|
|
197
|
+
});
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
172
200
|
</div>
|
|
173
201
|
</div>
|
|
174
202
|
);
|
|
175
203
|
}
|
|
176
204
|
}
|
|
177
205
|
|
|
206
|
+
const ItemExtraCheckbox = (props: {
|
|
207
|
+
label: string;
|
|
208
|
+
infoTip: string;
|
|
209
|
+
checked: boolean;
|
|
210
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
211
|
+
indent?: boolean;
|
|
212
|
+
}) => (
|
|
213
|
+
<div
|
|
214
|
+
style={{
|
|
215
|
+
marginLeft: props.indent ? "10px" : "0px",
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
<label>
|
|
219
|
+
{props.label}
|
|
220
|
+
<input
|
|
221
|
+
type="checkbox"
|
|
222
|
+
checked={props.checked}
|
|
223
|
+
onChange={props.onChange}
|
|
224
|
+
/>
|
|
225
|
+
</label>
|
|
226
|
+
<InfoTip>{props.infoTip}</InfoTip>
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
|
|
178
230
|
export default ItemExtrasEditor;
|