[AA-933] fix: address issues with progress page fbe exception UI (#561)

* fix: address issues with progress page fbe exception UI

* address comments
This commit is contained in:
Matthew Piatetsky
2021-07-27 10:59:23 -04:00
committed by GitHub
parent 6003865840
commit e2b00d6684
10 changed files with 55 additions and 26 deletions

View File

@@ -52,8 +52,8 @@ function DetailedGrades({ intl }) {
<section className="text-dark-700">
<h3 className="h4 mb-3">{intl.formatMessage(messages.detailedGrades)}</h3>
{gradesFeatureIsPartiallyLocked && (
<div className="mb-3 small row ml-0">
<Icon className="mr-1 mt-1" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" />
<div className="mb-3 small ml-0 d-inline">
<Icon className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" />
{intl.formatMessage(messages.gradeSummaryLimitedAccessExplanation)}
</div>
)}

View File

@@ -32,7 +32,7 @@ function DetailedGradesTable({ intl }) {
const detailedGradesData = subsectionScores.map((subsection) => ({
subsectionTitle: <SubsectionTitleCell subsection={subsection} />,
score: <span className={subsection.learnerHasAccess ? '' : 'locked-overlay'}>{subsection.numPointsEarned}/{subsection.numPointsPossible}</span>,
score: <span className={subsection.learnerHasAccess ? '' : 'greyed-out'}>{subsection.numPointsEarned}/{subsection.numPointsPossible}</span>,
}));
return (

View File

@@ -1,15 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import messages from '../messages';
function ProblemScoreDrawer({ intl, problemScores }) {
function ProblemScoreDrawer({ intl, problemScores, subsection }) {
return (
<span className="row w-100 m-0 x-small ml-4 pt-2 pl-1 text-gray-700 flex-nowrap">
<span id="problem-score-label" className="col-auto p-0">{intl.formatMessage(messages.problemScoreLabel)}</span>
<div className="col p-0">
<div className={classNames('col', 'p-0', { 'greyed-out': subsection.learnerHasAccess })}>
<ul className="list-unstyled row w-100 m-0" aria-labelledby="problem-score-label">
{problemScores.map(problemScore => (
<li className="ml-3">{problemScore.earned}/{problemScore.possible}</li>
@@ -26,6 +27,7 @@ ProblemScoreDrawer.propTypes = {
earned: PropTypes.number.isRequired,
possible: PropTypes.number.isRequired,
})).isRequired,
subsection: PropTypes.shape({ learnerHasAccess: PropTypes.bool }).isRequired,
};
export default injectIntl(ProblemScoreDrawer);

View File

@@ -44,21 +44,22 @@ function SubsectionTitleCell({ intl, subsection }) {
<Collapsible.Advanced>
<Row className="w-100 m-0">
<Collapsible.Trigger
className="mr-1"
className="mr-1 position-absolute"
aria-label={intl.formatMessage(messages.problemScoreToggleAltText, { subsectionTitle: displayName })}
tabIndex={gradesFeatureIsFullyLocked ? '-1' : '0'}
>
<Collapsible.Visible whenClosed><Icon src={ArrowDropDown} /></Collapsible.Visible>
<Collapsible.Visible whenOpen><Icon src={ArrowDropUp} /></Collapsible.Visible>
</Collapsible.Trigger>
<span className="small row ml-0">
{gradesFeatureIsFullyLocked || subsection.learnerHasAccess ? '' : <Icon className="mr-1 mt-1" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" />}
<span className="small d-inline ml-4 pl-1">
{gradesFeatureIsFullyLocked || subsection.learnerHasAccess ? '' : <Icon id={`detailedGradesBlockedIcon${subsection.blockKey}`} aria-label={intl.formatMessage(messages.noAcessToSubsection, { displayName })} className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" />}
{url ? (
<a
href={url}
className="muted-link small"
onClick={logSubsectionClicked}
tabIndex={gradesFeatureIsFullyLocked ? '-1' : '0'}
aria-labelledby={`detailedGradesBlockedIcon${subsection.blockKey}`}
>
{displayName}
</a>
@@ -68,7 +69,7 @@ function SubsectionTitleCell({ intl, subsection }) {
</span>
</Row>
<Collapsible.Body>
<ProblemScoreDrawer problemScores={problemScores} />
<ProblemScoreDrawer problemScores={problemScores} subsection={subsection} />
</Collapsible.Body>
</Collapsible.Advanced>
);

View File

@@ -1,12 +1,14 @@
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Blocked } from '@edx/paragon/icons';
import { Icon } from '@edx/paragon';
import { useModel } from '../../../../generic/model-store';
import messages from '../messages';
function AssignmentTypeCell({
assignmentType, footnoteMarker, footnoteId, locked,
intl, assignmentType, footnoteMarker, footnoteId, locked,
}) {
const {
courseId,
@@ -16,7 +18,7 @@ function AssignmentTypeCell({
gradesFeatureIsFullyLocked,
} = useModel('progress', courseId);
const lockedIcon = locked ? <Icon className="mr-1 mt-1" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" /> : '';
const lockedIcon = locked ? <Icon id={`assignmentTypeBlockedIcon${assignmentType}`} aria-label={intl.formatMessage(messages.noAcessToAssignmentType, { assignmentType })} className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" /> : '';
return (
<div className="small">
@@ -29,6 +31,7 @@ function AssignmentTypeCell({
href={`#${footnoteId}-footnote`}
aria-describedby="grade-summary-footnote-label"
tabIndex={gradesFeatureIsFullyLocked ? '-1' : '0'}
aria-labelledby={`assignmentTypeBlockedIcon${assignmentType}`}
>
{footnoteMarker}
</a>
@@ -39,6 +42,7 @@ function AssignmentTypeCell({
}
AssignmentTypeCell.propTypes = {
intl: intlShape.isRequired,
assignmentType: PropTypes.string.isRequired,
footnoteId: PropTypes.string,
footnoteMarker: PropTypes.number,
@@ -51,4 +55,4 @@ AssignmentTypeCell.defaultProps = {
locked: false,
};
export default AssignmentTypeCell;
export default injectIntl(AssignmentTypeCell);

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { useModel } from '../../../../generic/model-store';
@@ -16,14 +16,16 @@ function GradeSummary() {
},
} = useModel('progress', courseId);
const [allOfSomeAssignmentTypeIsLocked, setAllOfSomeAssignmentTypeIsLocked] = useState(false);
if (assignmentPolicies.length === 0) {
return null;
}
return (
<section className="text-dark-700 mb-4">
<GradeSummaryHeader />
<GradeSummaryTable />
<GradeSummaryHeader allOfSomeAssignmentTypeIsLocked={allOfSomeAssignmentTypeIsLocked} />
<GradeSummaryTable setAllOfSomeAssignmentTypeIsLocked={setAllOfSomeAssignmentTypeIsLocked} />
</section>
);
}

View File

@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
@@ -10,15 +11,15 @@ import { Blocked, InfoOutline } from '@edx/paragon/icons';
import messages from '../messages';
import { useModel } from '../../../../generic/model-store';
function GradeSummaryHeader({ intl }) {
function GradeSummaryHeader({ intl, allOfSomeAssignmentTypeIsLocked }) {
const {
courseId,
} = useSelector(state => state.courseHome);
const {
gradesFeatureIsFullyLocked,
gradesFeatureIsPartiallyLocked,
} = useModel('progress', courseId);
const [showTooltip, setShowTooltip] = useState(false);
return (
<div className="row w-100 m-0 align-items-center">
<h3 className="h4 mb-3 mr-1">{intl.formatMessage(messages.gradeSummary)}</h3>
@@ -45,9 +46,9 @@ function GradeSummaryHeader({ intl }) {
disabled={gradesFeatureIsFullyLocked}
/>
</OverlayTrigger>
{gradesFeatureIsPartiallyLocked && (
<div className="mb-3 small row ml-0">
<Icon className="mr-1 mt-1" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" />
{!gradesFeatureIsFullyLocked && allOfSomeAssignmentTypeIsLocked && (
<div className="mb-3 small ml-0 d-inline">
<Icon className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" />
{intl.formatMessage(messages.gradeSummaryLimitedAccessExplanation)}
</div>
)}
@@ -57,6 +58,7 @@ function GradeSummaryHeader({ intl }) {
GradeSummaryHeader.propTypes = {
intl: intlShape.isRequired,
allOfSomeAssignmentTypeIsLocked: PropTypes.bool.isRequired,
};
export default injectIntl(GradeSummaryHeader);

View File

@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -11,7 +12,7 @@ import GradeSummaryTableFooter from './GradeSummaryTableFooter';
import messages from '../messages';
function GradeSummaryTable({ intl }) {
function GradeSummaryTable({ intl, setAllOfSomeAssignmentTypeIsLocked }) {
const {
courseId,
} = useSelector(state => state.courseHome);
@@ -37,9 +38,13 @@ function GradeSummaryTable({ intl }) {
&& (subsection.numPointsPossible > 0 || subsection.numPointsEarned > 0)
))).flat();
if (subsectionAssignmentsOfType.length) {
return !subsectionAssignmentsOfType.some((subsection) => (
const noAccessToAssignmentsOfType = !subsectionAssignmentsOfType.some((subsection) => (
subsection.learnerHasAccess === true
));
if (noAccessToAssignmentsOfType) {
setAllOfSomeAssignmentTypeIsLocked(true);
return true;
}
}
return false;
};
@@ -59,7 +64,7 @@ function GradeSummaryTable({ intl }) {
footnoteMarker = footnotes.length;
}
const locked = !gradesFeatureIsFullyLocked && hasNoAccessToAssignmentsOfType(assignment.type) ? 'locked-overlay' : '';
const locked = !gradesFeatureIsFullyLocked && hasNoAccessToAssignmentsOfType(assignment.type) ? 'greyed-out' : '';
return {
type: {
@@ -97,7 +102,7 @@ function GradeSummaryTable({ intl }) {
headerClassName: 'justify-content-end h5 mb-0',
// eslint-disable-next-line react/prop-types
Cell: ({ value }) => (
<span className={value.locked ? 'locked-overlay' : ''}>{value.weight}</span> // eslint-disable-line react/prop-types
<span className={value.locked ? 'greyed-out' : ''}>{value.weight}</span> // eslint-disable-line react/prop-types
),
cellClassName: 'float-right small',
},
@@ -107,7 +112,7 @@ function GradeSummaryTable({ intl }) {
headerClassName: 'justify-content-end h5 mb-0',
// eslint-disable-next-line react/prop-types
Cell: ({ value }) => (
<span className={value.locked ? 'locked-overlay' : ''}>{value.grade}</span> // eslint-disable-line react/prop-types
<span className={value.locked ? 'greyed-out' : ''}>{value.grade}</span> // eslint-disable-line react/prop-types
),
cellClassName: 'float-right small',
},
@@ -117,7 +122,7 @@ function GradeSummaryTable({ intl }) {
headerClassName: 'justify-content-end h5 mb-0 text-right',
// eslint-disable-next-line react/prop-types
Cell: ({ value }) => (
<span className={value.locked ? 'locked-overlay' : ''}>{value.weightedGrade}</span> // eslint-disable-line react/prop-types
<span className={value.locked ? 'greyed-out' : ''}>{value.weightedGrade}</span> // eslint-disable-line react/prop-types
),
cellClassName: 'float-right font-weight-bold small',
},
@@ -136,6 +141,7 @@ function GradeSummaryTable({ intl }) {
GradeSummaryTable.propTypes = {
intl: intlShape.isRequired,
setAllOfSomeAssignmentTypeIsLocked: PropTypes.func.isRequired,
};
export default injectIntl(GradeSummaryTable);

View File

@@ -139,6 +139,14 @@ const messages = defineMessages({
id: 'progress.weightedGradeSummary',
defaultMessage: 'Your current weighted grade summary',
},
noAcessToAssignmentType: {
id: 'progress.noAcessToAssignmentType',
defaultMessage: 'You do not have access to assignments of type {assignmentType}',
},
noAcessToSubsection: {
id: 'progress.noAcessToSubsection',
defaultMessage: 'You do not have access to subsection {displayName}',
},
});
export default messages;

View File

@@ -357,6 +357,10 @@
}
}
.greyed-out {
opacity: .3;
}
.locked-overlay {
opacity: .3;
pointer-events: none;