@nualang/nualang-ui-components 0.1.1400 → 0.1.1402
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/dist/Exercises/Roleplay/Games/TopBarActions.js +10 -15
- package/dist/Exercises/Roleplay/OriginalRoleplay.js +11 -16
- package/dist/Forms/Settings/Tabs/General/General.js +67 -27
- package/dist/Lists/ExerciseSelection/ExerciseSelection.js +7 -3
- package/dist/Lists/Exercises/Exercises.js +4 -7
- package/dist/Lists/Members/Members.js +57 -12
- package/dist/Lists/Members/Members.test.js +70 -9
- package/dist/Lists/PhraseListSelection/PhraseListSelection.js +8 -4
- package/dist/Lists/RoleplaySelection/RoleplaySelection.js +8 -4
- package/dist/Misc/AchievementsBanner/AchievementsBanner.js +62 -0
- package/dist/Misc/ColorLinearProgress/ColorLinearProgress.js +14 -17
- package/dist/Misc/ExerciseProgressBar/ExerciseProgressBar.js +10 -16
- package/dist/Misc/MedalCard/MedalCard.js +98 -0
- package/dist/Misc/MedalSelector/MedalSelector.js +4 -0
- package/dist/Misc/MedalSelector/PlaceholderMedal.js +96 -0
- package/dist/Misc/MedalSelector/PlaceholderMedal.svg +27 -0
- package/dist/Misc/NualaMascot/NualaMascot.js +68 -0
- package/dist/Misc/NualaMascot/NualaMascotGreen.js +212 -0
- package/dist/Misc/NualaMascot/NualaMascotPink.js +131 -0
- package/dist/Misc/NualaMascot/NualaMascotRed.js +148 -0
- package/dist/Misc/NualaMascot/NualaMascotYellow.js +140 -0
- package/dist/Navigation/ResponsiveDrawer/ResponsiveDrawer.js +5 -4
- package/dist/Tables/Progress/Progress.js +17 -3
- package/dist/Tables/Progress/ProgressTable.js +4 -1
- package/dist/Tables/Progress/ProgressTable.test.js +24 -0
- package/dist/Tables/Progress/TableToolbar.js +25 -1
- package/dist/Tables/Progress/TableToolbar.test.js +62 -0
- package/dist/Tables/Progress/sorting.js +3 -2
- package/dist/Tables/Progress/sorting.test.js +36 -0
- package/dist/index.js +4 -1
- package/dist/utils/studentSort.js +35 -0
- package/package.json +4 -4
|
@@ -7,23 +7,10 @@ import EditIcon from "@mui/icons-material/Create";
|
|
|
7
7
|
import Share from "@mui/icons-material/Share";
|
|
8
8
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
|
9
9
|
import { makeStyles } from "tss-react/mui";
|
|
10
|
-
import { withStyles } from "tss-react/mui";
|
|
11
10
|
import { alpha } from "@mui/material/styles";
|
|
12
11
|
import VoiceSpeedSelector from "../../../Misc/VoiceSpeed/VoiceSpeed";
|
|
13
12
|
import ReportProblem from "../../../Misc/ReportProblem/ReportProblem";
|
|
14
13
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
15
|
-
const RoundedLinearProgress = withStyles(LinearProgress, theme => ({
|
|
16
|
-
root: {
|
|
17
|
-
height: 12,
|
|
18
|
-
borderRadius: 4
|
|
19
|
-
},
|
|
20
|
-
colorPrimary: {
|
|
21
|
-
backgroundColor: alpha(theme.palette.success.light, 0.25)
|
|
22
|
-
},
|
|
23
|
-
barColorPrimary: {
|
|
24
|
-
backgroundColor: theme.palette.success.main
|
|
25
|
-
}
|
|
26
|
-
}));
|
|
27
14
|
const useStyles = makeStyles()((theme, _params, classes) => ({
|
|
28
15
|
label: {
|
|
29
16
|
color: "#fff"
|
|
@@ -238,13 +225,21 @@ const TopBarActions = ({
|
|
|
238
225
|
children: roleplayHasStarted || isRoleplayPreview ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
239
226
|
children: [!isRoleplayPreview && /*#__PURE__*/_jsx("div", {
|
|
240
227
|
className: classes.progress,
|
|
241
|
-
children: /*#__PURE__*/_jsx(
|
|
228
|
+
children: /*#__PURE__*/_jsx(LinearProgress, {
|
|
242
229
|
variant: "determinate",
|
|
243
230
|
value: currentMessages.length / roleplay.script.length * 100,
|
|
244
231
|
"aria-label": t("exercise"),
|
|
245
232
|
"aria-valuemin": 0,
|
|
246
233
|
"aria-valuemax": 100,
|
|
247
|
-
"aria-valuenow": currentMessages.length / roleplay.script.length * 100
|
|
234
|
+
"aria-valuenow": currentMessages.length / roleplay.script.length * 100,
|
|
235
|
+
sx: {
|
|
236
|
+
height: 12,
|
|
237
|
+
borderRadius: 4,
|
|
238
|
+
backgroundColor: theme => alpha(theme.palette.success.light, 0.25),
|
|
239
|
+
"& .MuiLinearProgress-bar": {
|
|
240
|
+
backgroundColor: theme => theme.palette.success.main
|
|
241
|
+
}
|
|
242
|
+
}
|
|
248
243
|
})
|
|
249
244
|
}), hiddenDown ? null : /*#__PURE__*/_jsxs(_Fragment, {
|
|
250
245
|
children: [/*#__PURE__*/_jsx(VoiceSpeedSelector, {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { useEffect, useState, memo } from "react";
|
|
2
2
|
import { Joyride, STATUS } from "react-joyride";
|
|
3
3
|
import { useTheme, alpha } from "@mui/material/styles";
|
|
4
|
-
import { withStyles } from "tss-react/mui";
|
|
5
4
|
import { makeStyles } from "tss-react/mui";
|
|
6
|
-
import { Grid, Slide, IconButton, Button, ButtonBase, Avatar, Dialog, Typography, Box, Divider, Tooltip,
|
|
5
|
+
import { Grid, Slide, IconButton, Button, ButtonBase, Avatar, Dialog, Typography, Box, Divider, Tooltip, useMediaQuery, LinearProgress } from "@mui/material";
|
|
7
6
|
import Alert from "@mui/material/Alert";
|
|
8
7
|
import Menu from "@mui/material/Menu";
|
|
9
8
|
import MenuItem from "@mui/material/MenuItem";
|
|
@@ -31,18 +30,6 @@ import ReportProblem from "../../Misc/ReportProblem/ReportProblem";
|
|
|
31
30
|
import { removeExtraWhiteSpaces } from "../../utils";
|
|
32
31
|
import DefaultButton from "../../Misc/DefaultColourButton/DefaultColourButton";
|
|
33
32
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
34
|
-
const RoundedLinearProgress = withStyles(LinearProgress, theme => ({
|
|
35
|
-
root: {
|
|
36
|
-
height: 12,
|
|
37
|
-
borderRadius: 4
|
|
38
|
-
},
|
|
39
|
-
colorPrimary: {
|
|
40
|
-
backgroundColor: alpha(theme.palette.success.light, 0.25)
|
|
41
|
-
},
|
|
42
|
-
barColorPrimary: {
|
|
43
|
-
backgroundColor: theme.palette.success.main
|
|
44
|
-
}
|
|
45
|
-
}));
|
|
46
33
|
const useStyles = makeStyles()((theme, {
|
|
47
34
|
isActorOne
|
|
48
35
|
}, classes) => ({
|
|
@@ -629,9 +616,17 @@ function Roleplay({
|
|
|
629
616
|
children: roleplayHasStarted ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
630
617
|
children: [/*#__PURE__*/_jsx("div", {
|
|
631
618
|
className: classes.progress,
|
|
632
|
-
children: /*#__PURE__*/_jsx(
|
|
619
|
+
children: /*#__PURE__*/_jsx(LinearProgress, {
|
|
633
620
|
variant: "determinate",
|
|
634
|
-
value: currentMessages.length / roleplay.script.length * 100
|
|
621
|
+
value: currentMessages.length / roleplay.script.length * 100,
|
|
622
|
+
sx: {
|
|
623
|
+
height: 12,
|
|
624
|
+
borderRadius: 4,
|
|
625
|
+
backgroundColor: theme => alpha(theme.palette.success.light, 0.25),
|
|
626
|
+
"& .MuiLinearProgress-bar": {
|
|
627
|
+
backgroundColor: theme => theme.palette.success.main
|
|
628
|
+
}
|
|
629
|
+
}
|
|
635
630
|
})
|
|
636
631
|
}), hiddenDown ? null : /*#__PURE__*/_jsxs(_Fragment, {
|
|
637
632
|
children: [/*#__PURE__*/_jsxs(Button, {
|
|
@@ -56,7 +56,8 @@ function GeneralForm({
|
|
|
56
56
|
}) {
|
|
57
57
|
const {
|
|
58
58
|
email,
|
|
59
|
-
|
|
59
|
+
given_name,
|
|
60
|
+
family_name
|
|
60
61
|
} = values;
|
|
61
62
|
const learnLang = values["custom:learnLang"] || "";
|
|
62
63
|
const forLang = values["custom:forLang"] || "";
|
|
@@ -85,7 +86,7 @@ function GeneralForm({
|
|
|
85
86
|
};
|
|
86
87
|
getLearnLangLabel();
|
|
87
88
|
}, [role, t]);
|
|
88
|
-
const
|
|
89
|
+
const isNameDisabled = username && (username.toLowerCase().includes("wayside") || username.toLowerCase().includes("google"));
|
|
89
90
|
return /*#__PURE__*/_jsxs(Grid, {
|
|
90
91
|
container: true,
|
|
91
92
|
sx: {
|
|
@@ -102,46 +103,54 @@ function GeneralForm({
|
|
|
102
103
|
},
|
|
103
104
|
children: t("required_field")
|
|
104
105
|
})
|
|
105
|
-
}),
|
|
106
|
+
}), /*#__PURE__*/_jsx(Grid, {
|
|
106
107
|
size: {
|
|
107
108
|
xs: 12,
|
|
108
109
|
md: 6
|
|
109
110
|
},
|
|
110
111
|
children: /*#__PURE__*/_jsx(TextField, {
|
|
111
|
-
id: "
|
|
112
|
-
name: "
|
|
113
|
-
label: t("
|
|
114
|
-
|
|
112
|
+
id: "given_name",
|
|
113
|
+
name: "given_name",
|
|
114
|
+
label: t("first_name"),
|
|
115
|
+
title: t("first_name"),
|
|
116
|
+
value: given_name,
|
|
115
117
|
onChange: handleChange,
|
|
116
118
|
onBlur: handleBlur,
|
|
117
|
-
helperText: touched.
|
|
118
|
-
error: touched.
|
|
119
|
-
type: "
|
|
119
|
+
helperText: touched.given_name ? t(errors.given_name) : "",
|
|
120
|
+
error: touched.given_name && Boolean(errors.given_name),
|
|
121
|
+
type: "text",
|
|
120
122
|
variant: "outlined",
|
|
121
|
-
disabled: true,
|
|
122
123
|
fullWidth: true,
|
|
123
|
-
required: true
|
|
124
|
+
required: true,
|
|
125
|
+
disabled: isNameDisabled,
|
|
126
|
+
slotProps: {
|
|
127
|
+
htmlInput: {
|
|
128
|
+
"data-lpignore": "true",
|
|
129
|
+
autoComplete: "off",
|
|
130
|
+
maxLength: 32
|
|
131
|
+
}
|
|
132
|
+
}
|
|
124
133
|
})
|
|
125
134
|
}), /*#__PURE__*/_jsx(Grid, {
|
|
126
135
|
size: {
|
|
127
136
|
xs: 12,
|
|
128
|
-
md:
|
|
137
|
+
md: 6
|
|
129
138
|
},
|
|
130
139
|
children: /*#__PURE__*/_jsx(TextField, {
|
|
131
|
-
id: "
|
|
132
|
-
name: "
|
|
133
|
-
label: t("
|
|
134
|
-
title: t("
|
|
135
|
-
value:
|
|
140
|
+
id: "family_name",
|
|
141
|
+
name: "family_name",
|
|
142
|
+
label: t("last_name"),
|
|
143
|
+
title: t("last_name"),
|
|
144
|
+
value: family_name,
|
|
136
145
|
onChange: handleChange,
|
|
137
146
|
onBlur: handleBlur,
|
|
138
|
-
helperText: touched.
|
|
139
|
-
error: touched.
|
|
147
|
+
helperText: touched.family_name ? t(errors.family_name) : "",
|
|
148
|
+
error: touched.family_name && Boolean(errors.family_name),
|
|
140
149
|
type: "text",
|
|
141
150
|
variant: "outlined",
|
|
142
151
|
fullWidth: true,
|
|
143
152
|
required: true,
|
|
144
|
-
disabled:
|
|
153
|
+
disabled: isNameDisabled,
|
|
145
154
|
slotProps: {
|
|
146
155
|
htmlInput: {
|
|
147
156
|
"data-lpignore": "true",
|
|
@@ -150,6 +159,26 @@ function GeneralForm({
|
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
161
|
})
|
|
162
|
+
}), !isGeneratedEmail && /*#__PURE__*/_jsx(Grid, {
|
|
163
|
+
size: {
|
|
164
|
+
xs: 12,
|
|
165
|
+
md: 6
|
|
166
|
+
},
|
|
167
|
+
children: /*#__PURE__*/_jsx(TextField, {
|
|
168
|
+
id: "email",
|
|
169
|
+
name: "email",
|
|
170
|
+
label: t("email"),
|
|
171
|
+
value: email,
|
|
172
|
+
onChange: handleChange,
|
|
173
|
+
onBlur: handleBlur,
|
|
174
|
+
helperText: touched.email ? t(errors.email) : "",
|
|
175
|
+
error: touched.email && Boolean(errors.email),
|
|
176
|
+
type: "email",
|
|
177
|
+
variant: "outlined",
|
|
178
|
+
disabled: true,
|
|
179
|
+
fullWidth: true,
|
|
180
|
+
required: true
|
|
181
|
+
})
|
|
153
182
|
}), /*#__PURE__*/_jsx(Grid, {
|
|
154
183
|
size: {
|
|
155
184
|
xs: 12,
|
|
@@ -241,7 +270,8 @@ export default function General({
|
|
|
241
270
|
t = text => text,
|
|
242
271
|
initialValues = {
|
|
243
272
|
picture: null,
|
|
244
|
-
|
|
273
|
+
given_name: "",
|
|
274
|
+
family_name: "",
|
|
245
275
|
email: "Unknown",
|
|
246
276
|
city: "",
|
|
247
277
|
country: "",
|
|
@@ -261,8 +291,11 @@ export default function General({
|
|
|
261
291
|
}) {
|
|
262
292
|
const {
|
|
263
293
|
email,
|
|
294
|
+
given_name,
|
|
295
|
+
family_name,
|
|
264
296
|
preferred_username
|
|
265
297
|
} = initialValues;
|
|
298
|
+
const displayName = [given_name, family_name].filter(Boolean).join(" ");
|
|
266
299
|
const {
|
|
267
300
|
classes
|
|
268
301
|
} = useStyles();
|
|
@@ -271,7 +304,8 @@ export default function General({
|
|
|
271
304
|
const [avatarOptionClicked, setAvatarOptionClicked] = useState(false);
|
|
272
305
|
const [confirm] = useConfirm(t);
|
|
273
306
|
const generalFormInitialValues = {
|
|
274
|
-
|
|
307
|
+
given_name,
|
|
308
|
+
family_name,
|
|
275
309
|
email,
|
|
276
310
|
"custom:learnLang": initialValues["custom:learnLang"],
|
|
277
311
|
"custom:forLang": initialValues["custom:forLang"],
|
|
@@ -316,12 +350,12 @@ export default function General({
|
|
|
316
350
|
display: "inline-block"
|
|
317
351
|
},
|
|
318
352
|
children: [values.picture ? /*#__PURE__*/_jsx(Avatar, {
|
|
319
|
-
"aria-label":
|
|
353
|
+
"aria-label": displayName,
|
|
320
354
|
src: values.picture,
|
|
321
355
|
className: classes.avatar,
|
|
322
356
|
alt: ""
|
|
323
357
|
}) : /*#__PURE__*/_jsx(Avatar, {
|
|
324
|
-
"aria-label":
|
|
358
|
+
"aria-label": displayName,
|
|
325
359
|
sx: theme => ({
|
|
326
360
|
backgroundColor: theme.palette.mode === "dark" ? "white" : "",
|
|
327
361
|
color: theme.palette.mode === "dark" ? theme.palette.background.default : ""
|
|
@@ -378,7 +412,12 @@ export default function General({
|
|
|
378
412
|
variant: "h5",
|
|
379
413
|
component: "h5",
|
|
380
414
|
gutterBottom: true,
|
|
381
|
-
|
|
415
|
+
title: displayName || preferred_username,
|
|
416
|
+
noWrap: true,
|
|
417
|
+
sx: {
|
|
418
|
+
maxWidth: "100%"
|
|
419
|
+
},
|
|
420
|
+
children: displayName || preferred_username
|
|
382
421
|
}), !isGeneratedEmail && /*#__PURE__*/_jsx(Typography, {
|
|
383
422
|
color: "textSecondary",
|
|
384
423
|
variant: "body1",
|
|
@@ -527,7 +566,8 @@ const isEmail = value => {
|
|
|
527
566
|
return !emailRegex.test(value);
|
|
528
567
|
};
|
|
529
568
|
General.validationSchema = Yup.object().shape({
|
|
530
|
-
|
|
569
|
+
given_name: Yup.string().required("required").test("noSpecialChars", "no_special_characters", value => !containsInvalidSymbols(value)).test("is-not-email", "Email addresses are not allowed", value => isEmail(value)),
|
|
570
|
+
family_name: Yup.string().required("required").test("noSpecialChars", "no_special_characters", value => !containsInvalidSymbols(value)).test("is-not-email", "Email addresses are not allowed", value => isEmail(value)),
|
|
531
571
|
name: Yup.string().test("noSpecialChars", "no_special_characters", value => !containsInvalidSymbols(value)).nullable(),
|
|
532
572
|
"custom:forLang": Yup.string().nullable(),
|
|
533
573
|
"custom:learnLang": Yup.string().nullable()
|
|
@@ -27,7 +27,8 @@ function Exercise({
|
|
|
27
27
|
sectionId,
|
|
28
28
|
topicId,
|
|
29
29
|
setIsExerciseSelected,
|
|
30
|
-
phrases
|
|
30
|
+
phrases,
|
|
31
|
+
topicName
|
|
31
32
|
}) {
|
|
32
33
|
const noRoleplays = name.toLowerCase() === "roleplays" && roleplays.length === 0;
|
|
33
34
|
const noPhrases = name.toLowerCase() === "phrases" && !phrases?.length && !getPhraseLists;
|
|
@@ -96,7 +97,8 @@ function Exercise({
|
|
|
96
97
|
isExerciseSelected: isExerciseSelected,
|
|
97
98
|
setIsExerciseSelected: setIsExerciseSelected,
|
|
98
99
|
isExpanded: phraseListsOpen,
|
|
99
|
-
onPhraseListsLoaded: handlePhraseListsLoaded
|
|
100
|
+
onPhraseListsLoaded: handlePhraseListsLoaded,
|
|
101
|
+
topicName: topicName
|
|
100
102
|
})
|
|
101
103
|
})
|
|
102
104
|
}), name.toLowerCase() === "roleplays" && /*#__PURE__*/_jsx(Collapse, {
|
|
@@ -114,7 +116,8 @@ function Exercise({
|
|
|
114
116
|
t: t,
|
|
115
117
|
isExerciseSelected: isExerciseSelected,
|
|
116
118
|
handleSelectExercise: handleSelectExercise,
|
|
117
|
-
useCase: useCase
|
|
119
|
+
useCase: useCase,
|
|
120
|
+
topicName: topicName
|
|
118
121
|
})
|
|
119
122
|
})
|
|
120
123
|
})]
|
|
@@ -340,6 +343,7 @@ function Topic({
|
|
|
340
343
|
handleStartExercise: handleStartExercise,
|
|
341
344
|
isCreator: isCreator,
|
|
342
345
|
getPhraseLists: getPhraseLists,
|
|
346
|
+
topicName: topicName,
|
|
343
347
|
...otherProps
|
|
344
348
|
})
|
|
345
349
|
})]
|
|
@@ -15,7 +15,8 @@ import { searchStringToObj, objToSearchString } from "../../utils";
|
|
|
15
15
|
import FlashOnIcon from "@mui/icons-material/FlashOn";
|
|
16
16
|
import FlashOffIcon from "@mui/icons-material/FlashOff";
|
|
17
17
|
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
|
18
|
-
import { Collapse, ListItemText, ListItemIcon, List, ListItemAvatar, IconButton, Badge, Tooltip, Box, Chip
|
|
18
|
+
import { Collapse, ListItemText, ListItemIcon, List, ListItemAvatar, IconButton, Badge, Tooltip, Box, Chip } from "@mui/material";
|
|
19
|
+
import ColorLinearProgress from "../../Misc/ColorLinearProgress/ColorLinearProgress";
|
|
19
20
|
import Add from "@mui/icons-material/Add";
|
|
20
21
|
import Face from "@mui/icons-material/Face";
|
|
21
22
|
import ForumIcon from "@mui/icons-material/Forum";
|
|
@@ -181,13 +182,9 @@ function ItemProgressBar({
|
|
|
181
182
|
sx: {
|
|
182
183
|
width: 160
|
|
183
184
|
},
|
|
184
|
-
children: /*#__PURE__*/_jsx(
|
|
185
|
+
children: /*#__PURE__*/_jsx(ColorLinearProgress, {
|
|
185
186
|
variant: "determinate",
|
|
186
|
-
value: pct
|
|
187
|
-
sx: {
|
|
188
|
-
height: 6,
|
|
189
|
-
borderRadius: 3
|
|
190
|
-
}
|
|
187
|
+
value: pct
|
|
191
188
|
})
|
|
192
189
|
}), /*#__PURE__*/_jsx(Typography, {
|
|
193
190
|
variant: "caption",
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
-
import { Avatar, Box, Button, IconButton, List, ListItem, ListItemAvatar, ListItemText, ListSubheader, Typography, Tooltip, Menu, MenuItem, Chip } from "@mui/material";
|
|
3
|
+
import { Avatar, Box, Button, IconButton, List, ListItem, ListItemAvatar, ListItemText, ListSubheader, TextField, Typography, Tooltip, Menu, MenuItem, Chip } from "@mui/material";
|
|
4
4
|
import { makeStyles } from "tss-react/mui";
|
|
5
5
|
import Person from "@mui/icons-material/Person";
|
|
6
6
|
import CommentIcon from "@mui/icons-material/Comment";
|
|
7
7
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
|
8
|
+
import SortIcon from "@mui/icons-material/Sort";
|
|
8
9
|
import Skeleton from "@mui/material/Skeleton";
|
|
9
10
|
import MembersNotFound from "../../Misc/NotFound/Members";
|
|
10
11
|
import { DateTime } from "luxon";
|
|
11
12
|
import useConfirm from "../../hooks/useConfirm";
|
|
12
13
|
import HtmlTooltipComponent from "../../Misc/HtmlTooltipComponent/HtmlTooltipComponent";
|
|
14
|
+
import { getStudentSortOrder, setStudentSortOrder, compareMembersBySortOrder, MEMBERS_SORT_ORDER_KEY } from "../../utils/studentSort";
|
|
13
15
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
14
16
|
const useStyles = makeStyles()(theme => ({
|
|
15
17
|
root: {
|
|
@@ -116,7 +118,7 @@ function Member(props) {
|
|
|
116
118
|
})
|
|
117
119
|
})
|
|
118
120
|
}), /*#__PURE__*/_jsx(ListItemText, {
|
|
119
|
-
primary: featureFlags.classroomModes
|
|
121
|
+
primary: featureFlags.classroomModes?.readWrite && featureFlags.classroomModes?.challenge && member.assignedLabels?.isReadWriteModeStudent && member.assignedLabels?.isChallengeModeStudent ? /*#__PURE__*/_jsxs(Box, {
|
|
120
122
|
sx: {
|
|
121
123
|
display: "flex",
|
|
122
124
|
alignItems: "center",
|
|
@@ -143,7 +145,7 @@ function Member(props) {
|
|
|
143
145
|
color: "primary"
|
|
144
146
|
})
|
|
145
147
|
})]
|
|
146
|
-
}) : featureFlags.classroomModes
|
|
148
|
+
}) : featureFlags.classroomModes?.readWrite && member.assignedLabels?.isReadWriteModeStudent ? /*#__PURE__*/_jsxs(Box, {
|
|
147
149
|
sx: {
|
|
148
150
|
display: "flex",
|
|
149
151
|
alignItems: "center",
|
|
@@ -160,7 +162,7 @@ function Member(props) {
|
|
|
160
162
|
color: "primary"
|
|
161
163
|
})
|
|
162
164
|
})]
|
|
163
|
-
}) : featureFlags.classroomModes
|
|
165
|
+
}) : featureFlags.classroomModes?.challenge && member.assignedLabels?.isChallengeModeStudent ? /*#__PURE__*/_jsxs(Box, {
|
|
164
166
|
sx: {
|
|
165
167
|
display: "flex",
|
|
166
168
|
alignItems: "center",
|
|
@@ -193,7 +195,7 @@ function Member(props) {
|
|
|
193
195
|
}), /*#__PURE__*/_jsxs(OverflowMenu, {
|
|
194
196
|
anchorEl: anchorEl,
|
|
195
197
|
handleClose: handleCloseMenu,
|
|
196
|
-
children: [featureFlags.classroomModes
|
|
198
|
+
children: [featureFlags.classroomModes?.readWrite && (member.assignedLabels?.isReadWriteModeStudent ? /*#__PURE__*/_jsx(HtmlTooltipComponent, {
|
|
197
199
|
titleBold: t("heritage_speaker"),
|
|
198
200
|
titleDesc: t("heritage_speaker_desc"),
|
|
199
201
|
placement: "right",
|
|
@@ -229,7 +231,7 @@ function Member(props) {
|
|
|
229
231
|
primary: t("mark_as_heritage_speaker")
|
|
230
232
|
})
|
|
231
233
|
})
|
|
232
|
-
})), featureFlags.classroomModes
|
|
234
|
+
})), featureFlags.classroomModes?.challenge && (member.assignedLabels?.isChallengeModeStudent ? /*#__PURE__*/_jsx(HtmlTooltipComponent, {
|
|
233
235
|
titleBold: t("challenge_student"),
|
|
234
236
|
titleDesc: t("challenge_student_desc"),
|
|
235
237
|
placement: "right",
|
|
@@ -405,13 +407,56 @@ function Members({
|
|
|
405
407
|
handleDeleteClassroomMember,
|
|
406
408
|
handleUpdateClassroomMember,
|
|
407
409
|
isClassroomArchived,
|
|
408
|
-
featureFlags = {}
|
|
410
|
+
featureFlags = {},
|
|
411
|
+
showSortControl = false
|
|
409
412
|
}) {
|
|
410
|
-
|
|
411
|
-
|
|
413
|
+
const [sortOrder, setSortOrder] = useState(() => getStudentSortOrder(MEMBERS_SORT_ORDER_KEY, "joinDate"));
|
|
414
|
+
const handleSortOrderChange = value => {
|
|
415
|
+
setSortOrder(value);
|
|
416
|
+
setStudentSortOrder(MEMBERS_SORT_ORDER_KEY, value);
|
|
417
|
+
};
|
|
418
|
+
const sortedMembers = useMemo(() => [...(members || [])].sort((a, b) => compareMembersBySortOrder(a, b, sortOrder)), [members, sortOrder]);
|
|
419
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
420
|
+
children: [showSortControl && t && /*#__PURE__*/_jsxs(Box, {
|
|
421
|
+
sx: theme => ({
|
|
422
|
+
display: "flex",
|
|
423
|
+
alignItems: "center",
|
|
424
|
+
justifyContent: "flex-end",
|
|
425
|
+
gap: 1,
|
|
426
|
+
px: 2,
|
|
427
|
+
py: 1,
|
|
428
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
429
|
+
backgroundColor: theme.palette.mode === "dark" ? theme.palette.background.default : theme.palette.grey[50]
|
|
430
|
+
}),
|
|
431
|
+
children: [/*#__PURE__*/_jsx(SortIcon, {
|
|
432
|
+
fontSize: "small",
|
|
433
|
+
color: "action"
|
|
434
|
+
}), /*#__PURE__*/_jsxs(TextField, {
|
|
435
|
+
select: true,
|
|
436
|
+
size: "small",
|
|
437
|
+
id: "members_sort_by",
|
|
438
|
+
"data-cy": "members_sort_by",
|
|
439
|
+
label: t("sort_by"),
|
|
440
|
+
value: sortOrder,
|
|
441
|
+
onChange: e => handleSortOrderChange(e.target.value),
|
|
442
|
+
sx: {
|
|
443
|
+
minWidth: 170
|
|
444
|
+
},
|
|
445
|
+
children: [/*#__PURE__*/_jsx(MenuItem, {
|
|
446
|
+
value: "joinDate",
|
|
447
|
+
children: t("join_date")
|
|
448
|
+
}), /*#__PURE__*/_jsx(MenuItem, {
|
|
449
|
+
value: "firstName",
|
|
450
|
+
children: t("first_name")
|
|
451
|
+
}), /*#__PURE__*/_jsx(MenuItem, {
|
|
452
|
+
value: "lastName",
|
|
453
|
+
children: t("last_name")
|
|
454
|
+
})]
|
|
455
|
+
})]
|
|
456
|
+
}), /*#__PURE__*/_jsx(MemberList, {
|
|
412
457
|
t: t,
|
|
413
458
|
placeholderImageUrl: placeholderImageUrl,
|
|
414
|
-
members:
|
|
459
|
+
members: sortedMembers,
|
|
415
460
|
handleMessage: handleMessage,
|
|
416
461
|
disableSubheader: disableSubheader,
|
|
417
462
|
getMemberDetails: getMemberDetails,
|
|
@@ -420,7 +465,7 @@ function Members({
|
|
|
420
465
|
handleUpdateClassroomMember: handleUpdateClassroomMember,
|
|
421
466
|
isClassroomArchived: isClassroomArchived,
|
|
422
467
|
featureFlags: featureFlags
|
|
423
|
-
})
|
|
468
|
+
})]
|
|
424
469
|
});
|
|
425
470
|
}
|
|
426
471
|
Members.propTypes = {
|
|
@@ -1,20 +1,81 @@
|
|
|
1
|
-
import { render } from "@testing-library/react";
|
|
2
|
-
import { describe, it } from "vitest";
|
|
1
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
2
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
3
3
|
import { ThemeProvider, createTheme } from "@mui/material/styles";
|
|
4
4
|
import Members from "./Members";
|
|
5
5
|
import { props } from "./Members.stories";
|
|
6
|
+
import { MEMBERS_SORT_ORDER_KEY } from "../../utils/studentSort";
|
|
6
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const renderMembers = (extraProps = {}) => {
|
|
9
|
+
const theme = createTheme();
|
|
10
|
+
return render(/*#__PURE__*/_jsx(ThemeProvider, {
|
|
11
|
+
theme: theme,
|
|
12
|
+
children: /*#__PURE__*/_jsx(Members, {
|
|
13
|
+
...props,
|
|
14
|
+
featureFlags: {
|
|
15
|
+
classroomModes: {}
|
|
16
|
+
},
|
|
17
|
+
...extraProps
|
|
18
|
+
})
|
|
19
|
+
}));
|
|
20
|
+
};
|
|
21
|
+
const renderedUsernames = async () => {
|
|
22
|
+
await screen.findByText("Peter Parker");
|
|
23
|
+
const knownUsernames = new Set(props.members.map(m => m.username));
|
|
24
|
+
return Array.from(document.querySelectorAll(".MuiListItemText-primary")).map(el => el.textContent).filter(text => knownUsernames.has(text));
|
|
25
|
+
};
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
cleanup();
|
|
28
|
+
localStorage.removeItem(MEMBERS_SORT_ORDER_KEY);
|
|
29
|
+
});
|
|
7
30
|
describe("Members", () => {
|
|
8
31
|
it("renders without crashing", () => {
|
|
9
|
-
const theme = createTheme();
|
|
10
32
|
const {
|
|
11
33
|
unmount
|
|
12
|
-
} =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
34
|
+
} = renderMembers();
|
|
35
|
+
unmount();
|
|
36
|
+
});
|
|
37
|
+
it("defaults to join date order (newest first)", async () => {
|
|
38
|
+
const {
|
|
39
|
+
unmount
|
|
40
|
+
} = renderMembers({
|
|
41
|
+
showSortControl: true
|
|
42
|
+
});
|
|
43
|
+
const usernames = await renderedUsernames();
|
|
44
|
+
expect(usernames).toEqual(["Indigo Young", "Indigo Garland", "Cellophane", "Spider", "Evelyn Tool", "Oran Tool", "Peter Parker", "Mary Jane", "Giles Posture", "Indigo Violet", "Norman Gordon", "Penny Tool"]);
|
|
45
|
+
unmount();
|
|
46
|
+
});
|
|
47
|
+
it("sorts by last name when the stored preference is lastName", async () => {
|
|
48
|
+
localStorage.setItem(MEMBERS_SORT_ORDER_KEY, "lastName");
|
|
49
|
+
const {
|
|
50
|
+
unmount
|
|
51
|
+
} = renderMembers({
|
|
52
|
+
showSortControl: true
|
|
53
|
+
});
|
|
54
|
+
const usernames = await renderedUsernames();
|
|
55
|
+
expect(usernames).toEqual(["Cellophane", "Indigo Garland", "Norman Gordon", "Mary Jane", "Peter Parker", "Giles Posture", "Spider", "Evelyn Tool", "Oran Tool", "Penny Tool", "Indigo Violet", "Indigo Young"]);
|
|
56
|
+
unmount();
|
|
57
|
+
});
|
|
58
|
+
it("sorts by first name when the stored preference is firstName", async () => {
|
|
59
|
+
localStorage.setItem(MEMBERS_SORT_ORDER_KEY, "firstName");
|
|
60
|
+
const {
|
|
61
|
+
unmount
|
|
62
|
+
} = renderMembers({
|
|
63
|
+
showSortControl: true
|
|
64
|
+
});
|
|
65
|
+
const usernames = await renderedUsernames();
|
|
66
|
+
expect(usernames).toEqual(["Cellophane", "Evelyn Tool", "Giles Posture", "Indigo Garland", "Indigo Violet", "Indigo Young", "Mary Jane", "Norman Gordon", "Oran Tool", "Penny Tool", "Peter Parker", "Spider"]);
|
|
67
|
+
unmount();
|
|
68
|
+
});
|
|
69
|
+
it("does not mutate the members prop", async () => {
|
|
70
|
+
localStorage.setItem(MEMBERS_SORT_ORDER_KEY, "lastName");
|
|
71
|
+
const originalOrder = props.members.map(m => m.memberId);
|
|
72
|
+
const {
|
|
73
|
+
unmount
|
|
74
|
+
} = renderMembers({
|
|
75
|
+
showSortControl: true
|
|
76
|
+
});
|
|
77
|
+
await screen.findByText("Peter Parker");
|
|
78
|
+
expect(props.members.map(m => m.memberId)).toEqual(originalOrder);
|
|
18
79
|
unmount();
|
|
19
80
|
});
|
|
20
81
|
});
|
|
@@ -18,7 +18,8 @@ function PhraseList({
|
|
|
18
18
|
setIsPhraseListSelected,
|
|
19
19
|
t,
|
|
20
20
|
isExerciseSelected,
|
|
21
|
-
setIsExerciseSelected
|
|
21
|
+
setIsExerciseSelected,
|
|
22
|
+
topicName
|
|
22
23
|
}) {
|
|
23
24
|
const [open, setOpen] = useState(false);
|
|
24
25
|
const isTranslationHidden = phraseList?.hiddenPhraseGroupGames?.translation;
|
|
@@ -37,7 +38,8 @@ function PhraseList({
|
|
|
37
38
|
timer: 30,
|
|
38
39
|
answerOption: 0,
|
|
39
40
|
phraseListId: phraseList.phraseListId,
|
|
40
|
-
phrases: phraseList.phrases || []
|
|
41
|
+
phrases: phraseList.phrases || [],
|
|
42
|
+
topicName: topicName ?? ""
|
|
41
43
|
});
|
|
42
44
|
};
|
|
43
45
|
return /*#__PURE__*/_jsxs("div", {
|
|
@@ -162,7 +164,8 @@ function PhraseListSelection({
|
|
|
162
164
|
isExerciseSelected,
|
|
163
165
|
isExpanded,
|
|
164
166
|
setIsExerciseSelected,
|
|
165
|
-
onPhraseListsLoaded
|
|
167
|
+
onPhraseListsLoaded,
|
|
168
|
+
topicName
|
|
166
169
|
}) {
|
|
167
170
|
const [isPhraseListSelected, setIsPhraseListSelected] = useState(false);
|
|
168
171
|
const phraseListsQuery = courseQuerys.useCourseSectionTopicPhraseLists(async (cId, sId, tId, filters) => getPhraseLists(cId, sId, tId, filters), {
|
|
@@ -232,7 +235,8 @@ function PhraseListSelection({
|
|
|
232
235
|
setIsPhraseListSelected: setIsPhraseListSelected,
|
|
233
236
|
t: t,
|
|
234
237
|
isExerciseSelected: isExerciseSelected,
|
|
235
|
-
setIsExerciseSelected: setIsExerciseSelected
|
|
238
|
+
setIsExerciseSelected: setIsExerciseSelected,
|
|
239
|
+
topicName: topicName
|
|
236
240
|
}, phraseList.phraseListId || index));
|
|
237
241
|
}
|
|
238
242
|
export default PhraseListSelection;
|
|
@@ -15,7 +15,8 @@ function Roleplay({
|
|
|
15
15
|
setIsRoleplaySelected,
|
|
16
16
|
t,
|
|
17
17
|
isExerciseSelected,
|
|
18
|
-
handleSelectExercise = null
|
|
18
|
+
handleSelectExercise = null,
|
|
19
|
+
topicName
|
|
19
20
|
}) {
|
|
20
21
|
const [open, setOpen] = useState(false);
|
|
21
22
|
const games = ["roleplay-story", "roleplay-fill-in-the-blanks", "roleplay-act-it-out", "roleplay-act-it-out-listening", "roleplay-act-it-out-speaking", "roleplay-act-it-out-listening-speaking"];
|
|
@@ -26,7 +27,8 @@ function Roleplay({
|
|
|
26
27
|
exercise: exerciseName,
|
|
27
28
|
timer: 30,
|
|
28
29
|
answerOption: 0,
|
|
29
|
-
roleplayId: roleplay.roleplayId
|
|
30
|
+
roleplayId: roleplay.roleplayId,
|
|
31
|
+
topicName: topicName ?? ""
|
|
30
32
|
});
|
|
31
33
|
};
|
|
32
34
|
const isRoleplayEmpty = roleplay?.script?.length === 0 || !roleplay?.script?.some(line => line.text !== undefined);
|
|
@@ -174,7 +176,8 @@ function RoleplaySelection({
|
|
|
174
176
|
t,
|
|
175
177
|
isExerciseSelected,
|
|
176
178
|
handleSelectExercise,
|
|
177
|
-
useCase
|
|
179
|
+
useCase,
|
|
180
|
+
topicName
|
|
178
181
|
}) {
|
|
179
182
|
const [isRoleplaySelected, setIsRoleplaySelected] = useState(false);
|
|
180
183
|
return roleplays.map((roleplay, index) => /*#__PURE__*/_jsx(Roleplay, {
|
|
@@ -186,7 +189,8 @@ function RoleplaySelection({
|
|
|
186
189
|
t: t,
|
|
187
190
|
isExerciseSelected: isExerciseSelected,
|
|
188
191
|
handleSelectExercise: handleSelectExercise,
|
|
189
|
-
useCase: useCase
|
|
192
|
+
useCase: useCase,
|
|
193
|
+
topicName: topicName
|
|
190
194
|
}, index));
|
|
191
195
|
}
|
|
192
196
|
export default RoleplaySelection;
|