@instructure/quiz-taking
Version:
319 lines (316 loc) • 12.5 kB
JavaScript
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/esm/inherits";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
var _dec, _class, _AttemptHistory;
function _callSuper(_this, derived, args) {
function isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
return !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
} catch (e) {
return false;
}
}
derived = _getPrototypeOf(derived);
return _possibleConstructorReturn(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], _getPrototypeOf(_this).constructor) : derived.apply(_this, args));
}
/** @jsx jsx */
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';
import generateStyle from './styles';
import generateComponentTheme from './theme';
import t from '@instructure/quiz-i18n/es/format-message';
import { withI18nSupport, withStyleOverrides } from '@instructure/quiz-common';
export var AttemptHistory = (_dec = withStyleOverrides(generateStyle, generateComponentTheme), _dec(_class = (_AttemptHistory = /*#__PURE__*/function (_withI18nSupport) {
function AttemptHistory() {
_classCallCheck(this, AttemptHistory);
return _callSuper(this, AttemptHistory, arguments);
}
_inherits(AttemptHistory, _withI18nSupport);
return _createClass(AttemptHistory, [{
key: "componentDidMount",
value:
// =============
// LIFECYCLE
// =============
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 _this2 = this;
return function () {
_this2.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 jsx(Link, {
onClick: this.onAttemptClick(quizSessionId)
}, jsx("span", {
css: fontStyles
}, attemptName));
} else {
return attemptName;
}
}
}, {
key: "renderHiddenAttemptInfo",
value: function renderHiddenAttemptInfo() {
var _this3 = this;
var text = t('Not displayed per instructor settings.');
return jsx(AccessibleContent, {
alt: text
}, jsx(Tooltip, {
renderTip: text,
placement: "top",
mountNode: this.getMountNode
}, jsx("span", null, jsx(Focusable, null, function (_ref) {
var focusVisible = _ref.focusVisible;
return jsx(View, {
as: "div",
withFocusOutline: focusVisible
}, jsx("span", {
css: _this3.props.styles.hiddenIcon,
tabIndex: "0"
}, jsx(IconOffLine, {
size: "x-small"
})));
}))));
}
}, {
key: "getMountNode",
value: function getMountNode() {
return document.getElementById('main');
}
}, {
key: "renderAttemptHistory",
value: function renderAttemptHistory() {
var _this4 = this;
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 = _this4.formatPercentMax2FractionDigits(attempt.score / attempt.pointsPossible);
if (_this4.props.restrictQuantitativeData) {
letterGrade = scoreToLetterGrade(attempt.score / attempt.pointsPossible * 100, _this4.props.gradingScheme);
}
}
} else {
points = t.number(attempt.score);
}
}
if (attempt.percentage != null) {
score = _this4.formatPercentMax2FractionDigits(attempt.percentage);
if (_this4.props.restrictQuantitativeData) {
letterGrade = scoreToLetterGrade(attempt.percentage * 100, _this4.props.gradingScheme);
}
}
var fontStyles = _this4.props.selectedSessionId === attempt.quizSessionId ? _this4.props.styles.bold : _this4.props.styles.normal;
return jsx(Table.Row, {
key: JSON.stringify(attempt)
}, jsx(Table.Cell, null, jsx("span", {
css: fontStyles
}, _this4.renderAttemptLink(attempt, i))), !_this4.props.restrictQuantitativeData && jsx(Table.Cell, null, jsx("span", {
css: fontStyles
}, points || _this4.renderHiddenAttemptInfo())), !_this4.props.restrictQuantitativeData && jsx(Table.Cell, null, jsx("span", {
css: fontStyles
}, score || _this4.renderHiddenAttemptInfo())), _this4.props.restrictQuantitativeData && jsx(Table.Cell, null, jsx("span", {
css: fontStyles
}, letterGrade || _this4.renderHiddenAttemptInfo())), jsx(Table.Cell, null, jsx("span", {
css: fontStyles
}, _this4.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 averageScore = this.props.averageScore;
if (averageScore == null) return null;
var average = this.props.restrictQuantitativeData ? scoreToLetterGrade(averageScore * 100, this.props.gradingScheme) : this.formatPercentMax2FractionDigits(averageScore);
return jsx(Table.Row, null, jsx(Table.Cell, null, jsx("span", {
css: this.props.styles.bold
}, t('Average Score'))), !this.props.restrictQuantitativeData && jsx(Table.Cell, null, jsx("span", {
css: this.props.styles.bold
}, '--' /* eslint-disable-line react/jsx-no-literals */)), jsx(Table.Cell, null, jsx("span", {
css: this.props.styles.bold
}, average)), jsx(Table.Cell, null));
}
}, {
key: "renderTable",
value: function renderTable() {
var _this5 = this;
return jsx("div", {
css: this.props.styles.sectionWrapper
}, jsx(ToggleDetails, {
defaultExpanded: true,
summary: jsx(Heading, {
level: "h2",
color: "primary"
}, t('Attempt History'))
}, 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
jsx(Table, Object.assign({
caption: t('Attempt History')
}, props), jsx(Table.Head, null, jsx(Table.Row, null, jsx(Table.ColHeader, {
id: "attempt-history-results"
}, t('Results')), !_this5.props.restrictQuantitativeData ? jsx(Table.ColHeader, {
id: "attempt-history-points"
}, t('Points')) : jsx(React.Fragment, null), !_this5.props.restrictQuantitativeData ? jsx(Table.ColHeader, {
id: "attempt-history-score"
}, t('Score')) : jsx(React.Fragment, null), _this5.props.restrictQuantitativeData ? jsx(Table.ColHeader, {
id: "attempt-history-grade"
}, t('Grade')) : jsx(React.Fragment, null), jsx(Table.ColHeader, {
id: "attempt-history-score-to-keep"
}, t('({scoreToKeep} score is kept)', {
scoreToKeep: _this5.scoreToKeepString
})))), jsx(Table.Body, null, _this5.renderAttemptHistory(), _this5.renderAverageScore()))
);
})));
}
}, {
key: "render",
value: function render() {
var attemptHistory = this.props.attemptHistory;
if (!attemptHistory) return null;
if (attemptHistory.length > 0) {
return this.renderTable();
} else {
return jsx("div", {
css: this.props.styles.page
}, jsx("span", {
css: this.props.styles.emptyText
}, jsx(Text, {
color: "primary"
}, t('No attempt information to display yet.'))));
}
}
}]);
}(withI18nSupport(Component)), _defineProperty(_AttemptHistory, "displayName", 'AttemptHistory'), _defineProperty(_AttemptHistory, "componentId", "Quizzes".concat(_AttemptHistory.displayName)), _defineProperty(_AttemptHistory, "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,
scoreToKeep: PropTypes.oneOf(['highest', 'latest', 'average', 'first']).isRequired,
selectedSessionId: PropTypes.string.isRequired,
/* eslint-disable-next-line react/forbid-prop-types */
styles: PropTypes.object
}), _defineProperty(_AttemptHistory, "defaultProps", {
attemptHistory: null,
gradingScheme: [],
onAttemptClick: function onAttemptClick() {},
restrictQuantitativeData: false
}), _AttemptHistory)) || _class);
export default AttemptHistory;