UNPKG

@instructure/quiz-taking

Version:

319 lines (316 loc) • 12.5 kB
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;