@khanacademy/perseus 0.4.3 → 1.0.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/.eslintrc.js +6 -1
- package/CHANGELOG.md +34 -0
- package/dist/es/index.js +1078 -1299
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +1077 -1304
- package/dist/index.js.map +1 -1
- package/dist/strings.js +0 -4
- package/package.json +9 -8
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +110 -110
- package/src/perseus-api.jsx +10 -36
- package/src/perseus-types.js +0 -138
- package/src/styles/constants.js +0 -2
- package/src/styles/variables.less +0 -2
- package/src/types.js +18 -35
- package/src/util.js +0 -6
- package/src/version.js +1 -1
- package/src/widgets/__testdata__/base-radio_testdata.js +22 -0
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +110 -110
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +1136 -1160
- package/src/widgets/__tests__/radio/base-radio_test.jsx +243 -41
- package/src/widgets/__tests__/radio/choice-icon_test.jsx +67 -0
- package/src/widgets/__tests__/radio/choice_test.jsx +201 -0
- package/src/widgets/__tests__/radio/focus-ring_test.jsx +23 -0
- package/src/widgets/__tests__/radio/option-status_test.jsx +65 -0
- package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -9
- package/src/widgets/passage/get-line-height-for-node.js +4 -5
- package/src/widgets/passage/passage-markdown.jsx +176 -120
- package/src/widgets/passage.jsx +48 -46
- package/src/widgets/radio/__stories__/base-radio.stories.jsx +164 -17
- package/src/widgets/radio/__stories__/choice-icon.stories.jsx +202 -111
- package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +21 -6
- package/src/widgets/radio/__stories__/choice.stories.jsx +138 -0
- package/src/widgets/radio/__stories__/focus-ring.stories.jsx +29 -23
- package/src/widgets/radio/__stories__/option-status.stories.jsx +93 -99
- package/src/widgets/radio/base-radio.jsx +483 -556
- package/src/widgets/radio/choice-icon/choice-icon.jsx +155 -0
- package/src/widgets/radio/choice-icon/cross-out-line.jsx +55 -0
- package/src/widgets/radio/choice-icon/library-choice-icon.jsx +219 -0
- package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +129 -0
- package/src/widgets/radio/choice-icon/shared-styles.js +17 -0
- package/src/widgets/radio/choice-none-above.jsx +49 -40
- package/src/widgets/radio/choice.jsx +282 -353
- package/src/widgets/radio/choice.jsx.fixture.js +0 -18
- package/src/widgets/radio/focus-ring.jsx +29 -22
- package/src/widgets/radio/option-status.jsx +58 -36
- package/src/widgets/radio/widget.jsx +181 -136
- package/src/widgets/radio.jsx +16 -17
- package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +0 -64
- package/src/widgets/radio/choice-icon.jsx +0 -469
- package/src/widgets/radio/cross-out-button.jsx +0 -134
|
@@ -6,72 +6,203 @@ import * as React from "react";
|
|
|
6
6
|
|
|
7
7
|
import "@testing-library/jest-dom"; // Imports custom mathers
|
|
8
8
|
|
|
9
|
+
import {testDependencies} from "../../../../../../testing/test-dependencies.js";
|
|
10
|
+
import {setDependencies} from "../../../dependencies.js";
|
|
11
|
+
import {generateChoice} from "../../__testdata__/base-radio_testdata.js";
|
|
9
12
|
import BaseRadio from "../../radio/base-radio.jsx";
|
|
10
13
|
|
|
11
14
|
import type {APIOptions} from "../../../types.js";
|
|
12
15
|
|
|
16
|
+
function renderBaseRadio(props) {
|
|
17
|
+
const apiOptions: APIOptions = Object.freeze({});
|
|
18
|
+
|
|
19
|
+
const baseProps = {
|
|
20
|
+
apiOptions,
|
|
21
|
+
choices: [],
|
|
22
|
+
deselectEnabled: false,
|
|
23
|
+
editMode: false,
|
|
24
|
+
labelWrap: false,
|
|
25
|
+
countChoices: false,
|
|
26
|
+
numCorrect: 1,
|
|
27
|
+
multipleSelect: false,
|
|
28
|
+
|
|
29
|
+
// A callback indicating that this choice has changed. Its argument is
|
|
30
|
+
// an object with two keys: `checked` and `crossedOut`. Each contains
|
|
31
|
+
// an array of boolean values, specifying the new checked and
|
|
32
|
+
// crossed-out value of each choice.
|
|
33
|
+
onChange: ({checked, crossedOut}) => {},
|
|
34
|
+
|
|
35
|
+
// Whether this widget was the most recently used widget in this
|
|
36
|
+
// Renderer. Determines whether we'll auto-scroll the page upon
|
|
37
|
+
// entering review mode.
|
|
38
|
+
isLastUsedWidget: false,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const overwrittenProps = {...baseProps, ...props};
|
|
42
|
+
|
|
43
|
+
return render(<BaseRadio {...overwrittenProps} />);
|
|
44
|
+
}
|
|
45
|
+
|
|
13
46
|
describe("base-radio", () => {
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
// because choice-none-above uses a Renderer
|
|
49
|
+
// we need to set dependencies when it's rendered
|
|
50
|
+
// (this probably needs to be fixed)
|
|
51
|
+
setDependencies(testDependencies);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("renders a none of the above choice", () => {
|
|
55
|
+
// Arrange / Act
|
|
56
|
+
renderBaseRadio({
|
|
57
|
+
editMode: true,
|
|
58
|
+
choices: [
|
|
59
|
+
generateChoice({content: "Option 1", correct: false}),
|
|
60
|
+
generateChoice({content: "Option B", correct: false}),
|
|
61
|
+
generateChoice({content: "Option Gamma", correct: false}),
|
|
62
|
+
generateChoice({content: "Option Delta", correct: false}),
|
|
63
|
+
generateChoice({
|
|
64
|
+
isNoneOfTheAbove: true,
|
|
65
|
+
correct: true,
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Assert
|
|
71
|
+
expect(
|
|
72
|
+
screen.getByRole("checkbox", {name: "Select Choice E"}),
|
|
73
|
+
).toBeInTheDocument();
|
|
74
|
+
expect(screen.getByText("None of the above")).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
|
|
14
77
|
describe("edit mode", () => {
|
|
15
78
|
it("should render <li>'s for each choice", () => {
|
|
79
|
+
// Arrange / Act
|
|
80
|
+
renderBaseRadio({
|
|
81
|
+
editMode: true,
|
|
82
|
+
choices: [
|
|
83
|
+
generateChoice({content: "Option 1", correct: false}),
|
|
84
|
+
generateChoice({content: "Option B", correct: false}),
|
|
85
|
+
generateChoice({content: "Option Gamma", correct: true}),
|
|
86
|
+
generateChoice({content: "Option Delta", correct: false}),
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Assert
|
|
91
|
+
expect(screen.getAllByRole("listitem")).toHaveLength(4);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should toggle choice when inner element clicked", () => {
|
|
16
95
|
// Arrange
|
|
17
|
-
|
|
18
|
-
|
|
96
|
+
let updatedValues = null;
|
|
97
|
+
const onChangeHandler = (newValues) => {
|
|
98
|
+
updatedValues = newValues;
|
|
19
99
|
};
|
|
20
|
-
|
|
100
|
+
|
|
101
|
+
renderBaseRadio({
|
|
102
|
+
editMode: true,
|
|
103
|
+
choices: [
|
|
104
|
+
generateChoice({content: "Option 1", correct: false}),
|
|
105
|
+
generateChoice({content: "Option B", correct: false}),
|
|
106
|
+
generateChoice({content: "Option Gamma", correct: true}),
|
|
107
|
+
generateChoice({content: "Option Delta", correct: false}),
|
|
108
|
+
],
|
|
109
|
+
onChange: onChangeHandler,
|
|
110
|
+
});
|
|
21
111
|
|
|
22
112
|
// Act
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
multipleSelect={false}
|
|
26
|
-
countChoices={false}
|
|
27
|
-
numCorrect={1}
|
|
28
|
-
editMode={true}
|
|
29
|
-
labelWrap={false}
|
|
30
|
-
apiOptions={apiOptions}
|
|
31
|
-
choices={[
|
|
32
|
-
{content: "Option 1", correct: false},
|
|
33
|
-
{content: "Option B", correct: false},
|
|
34
|
-
{content: "Option Gamma", correct: true},
|
|
35
|
-
{content: "Option Delta", correct: false},
|
|
36
|
-
]}
|
|
37
|
-
onChange={onChangeHandler}
|
|
38
|
-
/>,
|
|
113
|
+
userEvent.click(
|
|
114
|
+
screen.getByRole("checkbox", {name: "Select Choice C"}),
|
|
39
115
|
);
|
|
40
116
|
|
|
41
117
|
// Assert
|
|
42
|
-
expect(
|
|
118
|
+
expect(updatedValues).toMatchObject({
|
|
119
|
+
checked: [false, false, true, false],
|
|
120
|
+
});
|
|
43
121
|
});
|
|
122
|
+
});
|
|
44
123
|
|
|
45
|
-
|
|
124
|
+
describe("selecting and deselecting options", () => {
|
|
125
|
+
it("deselects multi-select choices", () => {
|
|
46
126
|
// Arrange
|
|
47
|
-
|
|
48
|
-
|
|
127
|
+
let updatedValues = null;
|
|
128
|
+
const onChangeHandler = (newValues) => {
|
|
129
|
+
updatedValues = newValues;
|
|
49
130
|
};
|
|
131
|
+
|
|
132
|
+
renderBaseRadio({
|
|
133
|
+
multipleSelect: true,
|
|
134
|
+
choices: [
|
|
135
|
+
generateChoice({
|
|
136
|
+
content: "Option 1",
|
|
137
|
+
correct: false,
|
|
138
|
+
checked: false,
|
|
139
|
+
}),
|
|
140
|
+
generateChoice({
|
|
141
|
+
content: "Option B",
|
|
142
|
+
correct: false,
|
|
143
|
+
checked: false,
|
|
144
|
+
}),
|
|
145
|
+
generateChoice({
|
|
146
|
+
content: "Option Gamma",
|
|
147
|
+
correct: true,
|
|
148
|
+
checked: true,
|
|
149
|
+
}),
|
|
150
|
+
generateChoice({
|
|
151
|
+
content: "Option Delta",
|
|
152
|
+
correct: false,
|
|
153
|
+
checked: false,
|
|
154
|
+
}),
|
|
155
|
+
],
|
|
156
|
+
onChange: onChangeHandler,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Act
|
|
160
|
+
const radioButton = screen.getByRole("checkbox", {
|
|
161
|
+
name: "Select Choice C",
|
|
162
|
+
});
|
|
163
|
+
userEvent.click(radioButton);
|
|
164
|
+
|
|
165
|
+
// Assert
|
|
166
|
+
expect(updatedValues).toMatchObject({
|
|
167
|
+
checked: [false, false, false, false],
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Equivalent to "should toggle choice when inner element clicked" but with editMode set to false
|
|
172
|
+
it("select single select choices", () => {
|
|
173
|
+
// Arrange
|
|
50
174
|
let updatedValues = null;
|
|
51
175
|
const onChangeHandler = (newValues) => {
|
|
52
176
|
updatedValues = newValues;
|
|
53
177
|
};
|
|
54
178
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
179
|
+
renderBaseRadio({
|
|
180
|
+
multipleSelect: true,
|
|
181
|
+
choices: [
|
|
182
|
+
generateChoice({
|
|
183
|
+
content: "Option 1",
|
|
184
|
+
correct: false,
|
|
185
|
+
}),
|
|
186
|
+
generateChoice({
|
|
187
|
+
content: "Option B",
|
|
188
|
+
correct: false,
|
|
189
|
+
}),
|
|
190
|
+
generateChoice({
|
|
191
|
+
content: "Option Gamma",
|
|
192
|
+
correct: true,
|
|
193
|
+
}),
|
|
194
|
+
generateChoice({
|
|
195
|
+
content: "Option Delta",
|
|
196
|
+
correct: false,
|
|
197
|
+
}),
|
|
198
|
+
],
|
|
199
|
+
onChange: onChangeHandler,
|
|
200
|
+
});
|
|
72
201
|
|
|
73
202
|
// Act
|
|
74
|
-
userEvent.click(
|
|
203
|
+
userEvent.click(
|
|
204
|
+
screen.getByRole("checkbox", {name: "Select Choice C"}),
|
|
205
|
+
);
|
|
75
206
|
|
|
76
207
|
// Assert
|
|
77
208
|
expect(updatedValues).toMatchObject({
|
|
@@ -79,4 +210,75 @@ describe("base-radio", () => {
|
|
|
79
210
|
});
|
|
80
211
|
});
|
|
81
212
|
});
|
|
213
|
+
|
|
214
|
+
describe("instruction text", () => {
|
|
215
|
+
it("displays correct text for single select", () => {
|
|
216
|
+
// Arrange / Act
|
|
217
|
+
renderBaseRadio({
|
|
218
|
+
multipleSelect: false,
|
|
219
|
+
choices: [
|
|
220
|
+
generateChoice({content: "Option 1", correct: false}),
|
|
221
|
+
generateChoice({content: "Option B", correct: false}),
|
|
222
|
+
generateChoice({content: "Option Gamma", correct: true}),
|
|
223
|
+
generateChoice({content: "Option Delta", correct: false}),
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Assert
|
|
228
|
+
// note(matthewc) role=group is for the fieldset/legend
|
|
229
|
+
// https://www.w3.org/TR/html-aria/
|
|
230
|
+
const group = screen.getByRole("group", {
|
|
231
|
+
name: "Choose 1 answer:",
|
|
232
|
+
});
|
|
233
|
+
expect(group).toBeInTheDocument();
|
|
234
|
+
expect(group).toBeVisible();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("displays correct text for multi select (count choices off)", () => {
|
|
238
|
+
// Arrange / Act
|
|
239
|
+
renderBaseRadio({
|
|
240
|
+
multipleSelect: true,
|
|
241
|
+
countChoices: false,
|
|
242
|
+
choices: [
|
|
243
|
+
generateChoice({content: "Option 1", correct: false}),
|
|
244
|
+
generateChoice({content: "Option B", correct: false}),
|
|
245
|
+
generateChoice({content: "Option Gamma", correct: true}),
|
|
246
|
+
generateChoice({content: "Option Delta", correct: true}),
|
|
247
|
+
],
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Assert
|
|
251
|
+
// note(matthewc) role=group is for the fieldset/legend
|
|
252
|
+
// https://www.w3.org/TR/html-aria/
|
|
253
|
+
const group = screen.getByRole("group", {
|
|
254
|
+
name: "Choose all answers that apply:",
|
|
255
|
+
});
|
|
256
|
+
expect(group).toBeInTheDocument();
|
|
257
|
+
expect(group).toBeVisible();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("displays correct text for multi select (count choices on)", () => {
|
|
261
|
+
// Arrange / Act
|
|
262
|
+
renderBaseRadio({
|
|
263
|
+
multipleSelect: true,
|
|
264
|
+
countChoices: true,
|
|
265
|
+
numCorrect: 2,
|
|
266
|
+
choices: [
|
|
267
|
+
generateChoice({content: "Option 1", correct: false}),
|
|
268
|
+
generateChoice({content: "Option B", correct: false}),
|
|
269
|
+
generateChoice({content: "Option Gamma", correct: true}),
|
|
270
|
+
generateChoice({content: "Option Delta", correct: true}),
|
|
271
|
+
],
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Assert
|
|
275
|
+
// note(matthewc) role=group is for the fieldset/legend
|
|
276
|
+
// https://www.w3.org/TR/html-aria/
|
|
277
|
+
const group = screen.getByRole("group", {
|
|
278
|
+
name: "Choose 2 answers:",
|
|
279
|
+
});
|
|
280
|
+
expect(group).toBeInTheDocument();
|
|
281
|
+
expect(group).toBeVisible();
|
|
282
|
+
});
|
|
283
|
+
});
|
|
82
284
|
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom"; // Imports custom mathers
|
|
7
|
+
|
|
8
|
+
import ChoiceIcon from "../../radio/choice-icon/choice-icon.jsx";
|
|
9
|
+
import {
|
|
10
|
+
SAT_ICON_SIZE,
|
|
11
|
+
LIBRARY_ICON_SIZE,
|
|
12
|
+
} from "../../radio/choice-icon/shared-styles.js";
|
|
13
|
+
|
|
14
|
+
function renderChoiceIcon(options) {
|
|
15
|
+
const defaultOptions = {
|
|
16
|
+
pos: 0,
|
|
17
|
+
checked: false,
|
|
18
|
+
crossedOut: false,
|
|
19
|
+
focused: false,
|
|
20
|
+
hovered: false,
|
|
21
|
+
pressed: false,
|
|
22
|
+
correct: false,
|
|
23
|
+
showCorrectness: false,
|
|
24
|
+
product: "library",
|
|
25
|
+
multipleSelect: false,
|
|
26
|
+
reviewMode: false,
|
|
27
|
+
previouslyAnswered: false,
|
|
28
|
+
transparentBackground: false,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const overwrittenOptions = {...defaultOptions, ...options};
|
|
32
|
+
|
|
33
|
+
return render(<ChoiceIcon {...overwrittenOptions} />);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("choice icon", () => {
|
|
37
|
+
describe.each([["sat"], ["library"]])("product: %s", (product) => {
|
|
38
|
+
describe.each([[true], [false]])(
|
|
39
|
+
"multipleSelect: %s",
|
|
40
|
+
(multipleSelect) => {
|
|
41
|
+
it("renders with the correct border radius", () => {
|
|
42
|
+
// Arrange
|
|
43
|
+
let expectedRadius;
|
|
44
|
+
if (multipleSelect) {
|
|
45
|
+
expectedRadius = 3;
|
|
46
|
+
} else if (product === "sat") {
|
|
47
|
+
expectedRadius = SAT_ICON_SIZE;
|
|
48
|
+
} else {
|
|
49
|
+
expectedRadius = LIBRARY_ICON_SIZE;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Act
|
|
53
|
+
renderChoiceIcon({product, multipleSelect});
|
|
54
|
+
|
|
55
|
+
const choiceWrapper = screen.getByTestId(
|
|
56
|
+
`choice-icon__${product}-choice-icon`,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Assert
|
|
60
|
+
expect(choiceWrapper).toHaveStyle(
|
|
61
|
+
`border-radius: ${expectedRadius}px`,
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
import "@testing-library/jest-dom"; // Imports custom mathers
|
|
8
|
+
|
|
9
|
+
import Choice from "../../radio/choice.jsx";
|
|
10
|
+
|
|
11
|
+
function renderChoice(options) {
|
|
12
|
+
const defaultOptions = {
|
|
13
|
+
checked: false,
|
|
14
|
+
rationale: "This is a good rationale",
|
|
15
|
+
content: "This is a possible choice",
|
|
16
|
+
correct: true,
|
|
17
|
+
disabled: false,
|
|
18
|
+
pos: 0,
|
|
19
|
+
reviewMode: false,
|
|
20
|
+
showRationale: false,
|
|
21
|
+
showCorrectness: false,
|
|
22
|
+
multipleSelect: false,
|
|
23
|
+
crossedOut: false,
|
|
24
|
+
previouslyAnswered: false,
|
|
25
|
+
ref: {current: null},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const overwrittenOptions = {...defaultOptions, ...options};
|
|
29
|
+
|
|
30
|
+
return render(<Choice {...overwrittenOptions} />);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("choice", () => {
|
|
34
|
+
it("renders choice content", () => {
|
|
35
|
+
// Arrange / Act
|
|
36
|
+
renderChoice();
|
|
37
|
+
|
|
38
|
+
// Assert
|
|
39
|
+
expect(screen.getByText("This is a possible choice")).toBeVisible();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("renders rationale", () => {
|
|
43
|
+
// Arrange / Act
|
|
44
|
+
renderChoice({showRationale: true});
|
|
45
|
+
|
|
46
|
+
// Assert
|
|
47
|
+
expect(screen.getByText("This is a good rationale")).toBeVisible();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("renders choice button", () => {
|
|
51
|
+
// Arrange / Act
|
|
52
|
+
renderChoice();
|
|
53
|
+
|
|
54
|
+
const button = screen.getByRole("checkbox", {
|
|
55
|
+
name: "Select Choice A",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Assert
|
|
59
|
+
expect(button).toBeVisible();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("has correct aria-checked when checked", () => {
|
|
63
|
+
// Arrange / Act
|
|
64
|
+
renderChoice({checked: true});
|
|
65
|
+
|
|
66
|
+
const button = screen.getByRole("checkbox", {
|
|
67
|
+
name: "Select Choice A",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Assert
|
|
71
|
+
expect(button).toBeChecked();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("has correct aria-checked when unchecked", () => {
|
|
75
|
+
// Arrange / Act
|
|
76
|
+
renderChoice({checked: false});
|
|
77
|
+
|
|
78
|
+
const button = screen.getByRole("checkbox", {
|
|
79
|
+
name: "Select Choice A",
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Assert
|
|
83
|
+
expect(button).not.toBeChecked();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("has correct aria-disabled when disabled", () => {
|
|
87
|
+
// Arrange / Act
|
|
88
|
+
renderChoice({disabled: true});
|
|
89
|
+
|
|
90
|
+
const button = screen.getByRole("checkbox", {
|
|
91
|
+
name: "Select Choice A",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Assert
|
|
95
|
+
expect(button.getAttribute("aria-disabled")).toBe("true");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("has correct aria-disabled when not disabled", () => {
|
|
99
|
+
// Arrange / Act
|
|
100
|
+
renderChoice({disabled: false});
|
|
101
|
+
|
|
102
|
+
const button = screen.getByRole("checkbox", {
|
|
103
|
+
name: "Select Choice A",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Assert
|
|
107
|
+
expect(button.getAttribute("aria-disabled")).toBe("false");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("shows correct when in review mode", () => {
|
|
111
|
+
// Arrange / Act
|
|
112
|
+
renderChoice({
|
|
113
|
+
checked: false,
|
|
114
|
+
correct: true,
|
|
115
|
+
showCorrectness: true,
|
|
116
|
+
reviewMode: true,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Assert
|
|
120
|
+
expect(screen.getByText("Correct")).toBeVisible();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("shows correct selected when in review mode", () => {
|
|
124
|
+
// Arrange / Act
|
|
125
|
+
renderChoice({
|
|
126
|
+
checked: true,
|
|
127
|
+
correct: true,
|
|
128
|
+
showCorrectness: true,
|
|
129
|
+
reviewMode: true,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Assert
|
|
133
|
+
expect(screen.getByText("Correct (selected)")).toBeVisible();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("shows incorrect when in review mode", () => {
|
|
137
|
+
// Arrange / Act
|
|
138
|
+
renderChoice({
|
|
139
|
+
checked: false,
|
|
140
|
+
correct: false,
|
|
141
|
+
showCorrectness: true,
|
|
142
|
+
reviewMode: true,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Assert
|
|
146
|
+
expect(screen.getByText("Incorrect")).toBeVisible();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("shows incorrect selected when in review mode", () => {
|
|
150
|
+
// Arrange / Act
|
|
151
|
+
renderChoice({
|
|
152
|
+
checked: true,
|
|
153
|
+
correct: false,
|
|
154
|
+
showCorrectness: true,
|
|
155
|
+
reviewMode: true,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Assert
|
|
159
|
+
expect(screen.getByText("Incorrect (selected)")).toBeVisible();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("can be checked", () => {
|
|
163
|
+
// Arrange
|
|
164
|
+
let checked = false;
|
|
165
|
+
renderChoice({
|
|
166
|
+
checked,
|
|
167
|
+
onChange: (next) => {
|
|
168
|
+
checked = next.checked;
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Act
|
|
173
|
+
const button = screen.getByRole("checkbox", {
|
|
174
|
+
name: "Select Choice A",
|
|
175
|
+
});
|
|
176
|
+
userEvent.click(button);
|
|
177
|
+
|
|
178
|
+
// Assert
|
|
179
|
+
expect(checked).toBeTrue();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("can be unchecked", () => {
|
|
183
|
+
// Arrange
|
|
184
|
+
let checked = true;
|
|
185
|
+
renderChoice({
|
|
186
|
+
checked,
|
|
187
|
+
onChange: (next) => {
|
|
188
|
+
checked = next.checked;
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Act
|
|
193
|
+
const button = screen.getByRole("checkbox", {
|
|
194
|
+
name: "Select Choice A",
|
|
195
|
+
});
|
|
196
|
+
userEvent.click(button);
|
|
197
|
+
|
|
198
|
+
// Assert
|
|
199
|
+
expect(checked).toBeFalse();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom"; // Imports custom mathers
|
|
7
|
+
|
|
8
|
+
import FocusRing from "../../radio/focus-ring.jsx";
|
|
9
|
+
|
|
10
|
+
describe("choice icon", () => {
|
|
11
|
+
describe.each([[true], [false]])("multipleSelect: %s", (multipleSelect) => {
|
|
12
|
+
it("renders with the correct border radius", () => {
|
|
13
|
+
// Arrange/Act
|
|
14
|
+
render(<FocusRing multipleSelect={multipleSelect} />);
|
|
15
|
+
|
|
16
|
+
const focusRing = screen.getByTestId("focus-ring");
|
|
17
|
+
|
|
18
|
+
// Assert
|
|
19
|
+
const expectedRadius = multipleSelect ? "5px" : "50%";
|
|
20
|
+
expect(focusRing).toHaveStyle(`border-radius: ${expectedRadius}`);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom"; // Imports custom mathers
|
|
7
|
+
|
|
8
|
+
import OptionStatus from "../../radio/option-status.jsx";
|
|
9
|
+
|
|
10
|
+
function renderOptionStatus(options) {
|
|
11
|
+
const defaultOptions = {
|
|
12
|
+
correct: false,
|
|
13
|
+
checked: false,
|
|
14
|
+
crossedOut: false,
|
|
15
|
+
previouslyAnswered: false,
|
|
16
|
+
reviewMode: true,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const overwrittenOptions = {...defaultOptions, ...options};
|
|
20
|
+
|
|
21
|
+
return render(<OptionStatus {...overwrittenOptions} />);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("answer text", () => {
|
|
25
|
+
it.each([
|
|
26
|
+
[true, "Correct"],
|
|
27
|
+
[false, "Incorrect"],
|
|
28
|
+
])(
|
|
29
|
+
"renders with the correct text based on 'correct' prop: %s => %s",
|
|
30
|
+
(correct, expectedText) => {
|
|
31
|
+
// Arrange/Act
|
|
32
|
+
renderOptionStatus({correct: correct});
|
|
33
|
+
const optionStatus = screen.getByText(expectedText);
|
|
34
|
+
|
|
35
|
+
// Assert
|
|
36
|
+
expect(optionStatus).toBeVisible();
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
it.each([
|
|
41
|
+
[true, "Incorrect (selected)"],
|
|
42
|
+
[false, "Incorrect"],
|
|
43
|
+
])(
|
|
44
|
+
"renders with the correct text based on 'checked' prop: %s => %s",
|
|
45
|
+
(checked, expectedText) => {
|
|
46
|
+
// Arrange/Act
|
|
47
|
+
renderOptionStatus({correct: false, checked: checked});
|
|
48
|
+
const optionStatus = screen.getByText(expectedText);
|
|
49
|
+
|
|
50
|
+
// Assert
|
|
51
|
+
expect(optionStatus).toBeVisible();
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
it("renders correctly based on 'crossedOut' and 'correct'", () => {
|
|
56
|
+
// Arrange/Act
|
|
57
|
+
renderOptionStatus({crossedOut: true, correct: true});
|
|
58
|
+
const optionStatus = screen.getByText(
|
|
59
|
+
"Correct (but you crossed it out)",
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Assert
|
|
63
|
+
expect(optionStatus).toBeVisible();
|
|
64
|
+
});
|
|
65
|
+
});
|