@instructure/quiz-taking
Version:
420 lines (419 loc) • 19.2 kB
JavaScript
/** @jsx jsx */ function _assert_this_initialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _call_super(_this, derived, args) {
derived = _get_prototype_of(derived);
return _possible_constructor_return(_this, _is_native_reflect_construct() ? Reflect.construct(derived, args || [], _get_prototype_of(_this).constructor) : derived.apply(_this, args));
}
function _class_call_check(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for(var i = 0; i < props.length; i++){
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _create_class(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _get_prototype_of(o) {
_get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _get_prototype_of(o);
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _set_prototype_of(subClass, superClass);
}
function _object_spread(target) {
for(var i = 1; i < arguments.length; i++){
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === "function") {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function(key) {
_define_property(target, key, source[key]);
});
}
return target;
}
function _possible_constructor_return(self, call) {
if (call && (_type_of(call) === "object" || typeof call === "function")) {
return call;
}
return _assert_this_initialized(self);
}
function _set_prototype_of(o, p) {
_set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _set_prototype_of(o, p);
}
function _type_of(obj) {
"@swc/helpers - typeof";
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
}
function _is_native_reflect_construct() {
try {
var result = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
} catch (_) {}
return (_is_native_reflect_construct = function() {
return !!result;
})();
}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { scoreToLetterGrade } from '@instructure/grading-utils';
import { jsx } from '@instructure/emotion';
import { Heading } from '@instructure/ui-heading';
import { Text } from '@instructure/ui-text';
import { Link } from '@instructure/ui-link';
import { AccessibleContent } from '@instructure/ui-a11y-content';
import { Table } from '@instructure/ui-table';
import { ToggleDetails } from '@instructure/ui-toggle-details';
import { Tooltip } from '@instructure/ui-tooltip';
import { Focusable } from '@instructure/ui-focusable';
import { View } from '@instructure/ui-view';
import { Responsive } from '@instructure/ui-responsive';
import { IconOffLine } from '@instructure/ui-icons';
import { CustomPropTypes } from '@instructure/quiz-core/common/util/CustomPropTypes';
import generateStyle from './styles';
import generateComponentTheme from './theme';
import t from '@instructure/quiz-i18n/format-message';
import { formatPercentMax2FractionDigits } from '@instructure/quiz-i18n/util/numberFormats';
import { withI18nSupport } from '@instructure/quiz-common/with-i18n-support';
import { withStyleOverrides } from '@instructure/quiz-common/util/withStyleOverrides';
var _withI18nSupport;
var BaseAttemptHistory = /*#__PURE__*/ function(_superClass) {
"use strict";
_inherits(BaseAttemptHistory, _superClass);
function BaseAttemptHistory() {
_class_call_check(this, BaseAttemptHistory);
return _call_super(this, BaseAttemptHistory, arguments);
}
_create_class(BaseAttemptHistory, [
{
// =============
// LIFECYCLE
// =============
key: "componentDidMount",
value: function componentDidMount() {
if (this.props.attemptHistory) {
this.fetchData();
}
}
},
{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (this.props.attemptHistory && (!prevProps.attemptHistory || this.props.attemptHistory.some(function(attempt, index) {
return attempt.quizSessionId !== prevProps.attemptHistory[index].quizSessionId;
}))) {
this.fetchData();
}
}
},
{
key: "fetchData",
value: function fetchData() {
var _this_props = this.props, getQuizSessions = _this_props.getQuizSessions, quizId = _this_props.quizId, attemptHistory = _this_props.attemptHistory;
var quizSessionIds = attemptHistory.map(function(attempt) {
return attempt.quizSessionId;
}).filter(Boolean);
if (!quizSessionIds.length) return;
getQuizSessions(quizId, {
ids: quizSessionIds
});
}
},
{
key: "scoreToKeepString",
get: function get() {
return ({
highest: t('Highest'),
latest: t('Latest'),
average: t('Average'),
first: t('First')
})[this.props.scoreToKeep];
}
},
{
// =============
// Callbacks
// =============
key: "onAttemptClick",
value: function onAttemptClick(quizSessionId) {
var _this = this;
return function() {
_this.props.onAttemptClick(quizSessionId);
};
}
},
{
// =============
// RENDERING
// =============
key: "renderAttemptLink",
value: function renderAttemptLink(attempt, i) {
var displayName = attempt.displayName, quizSessionId = attempt.quizSessionId;
var attemptName = displayName || t('Attempt {n}', {
n: i + 1
});
var fontStyles = this.props.selectedSessionId === quizSessionId ? this.props.styles.bold : this.props.styles.normal;
if (this.props.onAttemptClick && quizSessionId) {
return /*#__PURE__*/ jsx(Link, {
onClick: this.onAttemptClick(quizSessionId)
}, /*#__PURE__*/ jsx("span", {
css: fontStyles
}, attemptName));
} else {
return attemptName;
}
}
},
{
key: "renderHiddenAttemptInfo",
value: function renderHiddenAttemptInfo() {
var _this = this;
var text = t('Not displayed per instructor settings.');
return /*#__PURE__*/ jsx(AccessibleContent, {
alt: text
}, /*#__PURE__*/ jsx(Tooltip, {
renderTip: text,
placement: "top",
mountNode: this.getMountNode
}, /*#__PURE__*/ jsx("span", null, /*#__PURE__*/ jsx(Focusable, null, function(param) {
var focusVisible = param.focusVisible;
return /*#__PURE__*/ jsx(View, {
as: "div",
withFocusOutline: focusVisible
}, /*#__PURE__*/ jsx("span", {
css: _this.props.styles.hiddenIcon,
tabIndex: "0"
}, /*#__PURE__*/ jsx(IconOffLine, {
size: "x-small"
})));
}))));
}
},
{
key: "getMountNode",
value: function getMountNode() {
return document.getElementById('main');
}
},
{
key: "renderAttemptHistory",
value: function renderAttemptHistory() {
var _this = this;
var _this_props = this.props, isSurvey = _this_props.isSurvey, restrictQuantitativeData = _this_props.restrictQuantitativeData;
return this.props.attemptHistory.map(function(attempt, i) {
var points = null;
var score = null;
var letterGrade = null;
if (attempt.score != null) {
if (attempt.pointsPossible) {
points = t('{score, number} of {total, number}', {
score: attempt.score,
total: attempt.pointsPossible
});
if (attempt.percentage == null) {
score = _this.formatPercentMax2FractionDigits(attempt.score / attempt.pointsPossible);
if (restrictQuantitativeData) {
letterGrade = scoreToLetterGrade(attempt.score / attempt.pointsPossible * 100, _this.props.gradingScheme);
}
}
} else {
points = t.number(attempt.score);
}
}
if (attempt.percentage != null) {
score = _this.formatPercentMax2FractionDigits(attempt.percentage);
if (restrictQuantitativeData) {
letterGrade = scoreToLetterGrade(attempt.percentage * 100, _this.props.gradingScheme);
}
}
var fontStyles = _this.props.selectedSessionId === attempt.quizSessionId ? _this.props.styles.bold : _this.props.styles.normal;
return /*#__PURE__*/ jsx(Table.Row, {
key: JSON.stringify(attempt)
}, /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: fontStyles
}, _this.renderAttemptLink(attempt, i))), !isSurvey && !restrictQuantitativeData && /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: fontStyles
}, points || _this.renderHiddenAttemptInfo())), !isSurvey && !restrictQuantitativeData && /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: fontStyles
}, score || _this.renderHiddenAttemptInfo())), !isSurvey && restrictQuantitativeData && /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: fontStyles
}, letterGrade || _this.renderHiddenAttemptInfo())), !isSurvey && /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: fontStyles
}, _this.renderScoreBody(attempt))));
});
}
},
{
key: "renderScoreBody",
value: function renderScoreBody(attempt) {
if (typeof attempt.score !== 'undefined' && attempt.authoritative && this.props.scoreToKeep !== 'average') {
return t('({scoreType} score)', {
scoreType: this.scoreToKeepString
});
}
}
},
{
key: "renderAverageScore",
value: function renderAverageScore() {
var _this_props = this.props, averageScore = _this_props.averageScore, isSurvey = _this_props.isSurvey;
if (isSurvey || !averageScore) return null;
var average = this.props.restrictQuantitativeData ? scoreToLetterGrade(averageScore * 100, this.props.gradingScheme) : formatPercentMax2FractionDigits(averageScore);
return /*#__PURE__*/ jsx(Table.Row, null, /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: this.props.styles.bold
}, t('Average Score'))), !this.props.restrictQuantitativeData && /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: this.props.styles.bold
}, '--' /* eslint-disable-line react/jsx-no-literals */ )), /*#__PURE__*/ jsx(Table.Cell, null, /*#__PURE__*/ jsx("span", {
css: this.props.styles.bold
}, average)), /*#__PURE__*/ jsx(Table.Cell, null));
}
},
{
key: "renderTable",
value: function renderTable() {
var _this = this;
var _this_props = this.props, restrictQuantitativeData = _this_props.restrictQuantitativeData, isSurvey = _this_props.isSurvey;
return /*#__PURE__*/ jsx("div", {
css: this.props.styles.sectionWrapper
}, /*#__PURE__*/ jsx(ToggleDetails, {
defaultExpanded: true,
summary: /*#__PURE__*/ jsx(Heading, {
level: "h2",
color: "primary"
}, t('Attempt History'))
}, /*#__PURE__*/ jsx(Responsive, {
query: {
small: {
maxWidth: '20rem'
},
large: {
minWidth: '21rem'
}
},
props: {
small: {
layout: 'stacked'
},
large: {
layout: 'auto'
}
}
}, function(props) {
return(// Column header fallbacks are React Fragments because InstUI doesn't filter out falsy
// values from the list of Children and the app crashes
// Related ticket: https://instructure.atlassian.net/browse/INSTUI-4534
/*#__PURE__*/ jsx(Table, _object_spread({
caption: t('Attempt History')
}, props), /*#__PURE__*/ jsx(Table.Head, null, /*#__PURE__*/ jsx(Table.Row, null, /*#__PURE__*/ jsx(Table.ColHeader, {
id: "attempt-history-results"
}, t('Results')), !isSurvey && !restrictQuantitativeData ? /*#__PURE__*/ jsx(Table.ColHeader, {
id: "attempt-history-points"
}, t('Points')) : /*#__PURE__*/ jsx(React.Fragment, null), !isSurvey && !restrictQuantitativeData ? /*#__PURE__*/ jsx(Table.ColHeader, {
id: "attempt-history-score"
}, t('Score')) : /*#__PURE__*/ jsx(React.Fragment, null), !isSurvey && restrictQuantitativeData ? /*#__PURE__*/ jsx(Table.ColHeader, {
id: "attempt-history-grade"
}, t('Grade')) : /*#__PURE__*/ jsx(React.Fragment, null), !isSurvey && /*#__PURE__*/ jsx(Table.ColHeader, {
id: "attempt-history-score-to-keep"
}, t('({scoreToKeep} score is kept)', {
scoreToKeep: _this.scoreToKeepString
})))), /*#__PURE__*/ jsx(Table.Body, null, _this.renderAttemptHistory(), _this.renderAverageScore())));
})));
}
},
{
key: "render",
value: function render() {
var attemptHistory = this.props.attemptHistory;
if (!attemptHistory) return null;
if (attemptHistory.length > 0) {
return this.renderTable();
} else {
return /*#__PURE__*/ jsx("div", {
css: this.props.styles.page
}, /*#__PURE__*/ jsx("span", {
css: this.props.styles.emptyText
}, /*#__PURE__*/ jsx(Text, {
color: "primary"
}, t('No attempt information to display yet.'))));
}
}
}
]);
return BaseAttemptHistory;
}(_withI18nSupport = withI18nSupport(Component));
_define_property(BaseAttemptHistory, "displayName", 'AttemptHistory');
_define_property(BaseAttemptHistory, "componentId", "Quizzes".concat(BaseAttemptHistory.displayName));
_define_property(BaseAttemptHistory, "propTypes", {
attemptHistory: CustomPropTypes.attemptHistory,
averageScore: CustomPropTypes.numberInRange(0, 1),
getQuizSessions: PropTypes.func.isRequired,
gradingScheme: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
value: PropTypes.number
})),
onAttemptClick: PropTypes.func,
quizId: CustomPropTypes.recordId,
restrictQuantitativeData: PropTypes.bool,
isSurvey: PropTypes.bool.isRequired,
scoreToKeep: PropTypes.oneOf([
'highest',
'latest',
'average',
'first'
]).isRequired,
selectedSessionId: PropTypes.string.isRequired,
/* eslint-disable-next-line react/forbid-prop-types */ styles: PropTypes.object
});
_define_property(BaseAttemptHistory, "defaultProps", {
attemptHistory: null,
gradingScheme: [],
/* v8 ignore start */ onAttemptClick: function() {},
/* v8 ignore end */ restrictQuantitativeData: false
});
export var AttemptHistory = withStyleOverrides(generateStyle, generateComponentTheme)(BaseAttemptHistory);
export default AttemptHistory;