fix: scroll enable on smaller screen (#51)
* fix: scroll enable on smaller screen * fix: fixed review error width * fix: make rubric and review error width align * fix: rubric position to be sticky * fix: remove double scroll bar at the bottom for small screen * fix: make submission more response on small screen * fix: rubric sticky and margin correctly on smaller screen * chore: linting and update snapshot * fix: make submission title resize to smaller on smaller screen * fix: miss align rubic and submission file on the top * fix: padding size on small screen * feat: add optional on feedback comment for clarity * chore: update test
This commit is contained in:
@@ -12,7 +12,7 @@ import './FileCard.scss';
|
||||
*/
|
||||
export const FileCard = ({ file, children }) => (
|
||||
<Card className="file-card" key={file.name}>
|
||||
<Collapsible className="file-collapsible" defaultOpen title={<h3>{file.name}</h3>}>
|
||||
<Collapsible className="file-collapsible" defaultOpen title={<h3 className="file-card-title">{file.name}</h3>}>
|
||||
<div className="preview-panel">
|
||||
<FileInfo><FilePopoverContent {...file} /></FileInfo>
|
||||
{children}
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
.file-card {
|
||||
margin: map-get($spacers, 1) 0;
|
||||
|
||||
.file-card-title {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.image-renderer {
|
||||
@@ -18,4 +24,10 @@
|
||||
|
||||
.txt-renderer {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.file-card-title {
|
||||
width: map-get($container-max-widths, "sm")/2;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ describe('File Preview Card component', () => {
|
||||
describe('Component', () => {
|
||||
test('collapsible title is name header', () => {
|
||||
const title = el.find(Collapsible).prop('title');
|
||||
expect(title).toEqual(<h3>{props.file.name}</h3>);
|
||||
expect(title).toEqual(<h3 className="file-card-title">{props.file.name}</h3>);
|
||||
});
|
||||
test('forwards children into preview-panel', () => {
|
||||
const previewPanelChildren = el.find('.preview-panel').children();
|
||||
|
||||
@@ -9,7 +9,9 @@ exports[`File Preview Card component snapshot 1`] = `
|
||||
className="file-collapsible"
|
||||
defaultOpen={true}
|
||||
title={
|
||||
<h3>
|
||||
<h3
|
||||
className="file-card-title"
|
||||
>
|
||||
test-file-name.pdf
|
||||
</h3>
|
||||
}
|
||||
|
||||
@@ -25,6 +25,15 @@ export class CriterionFeedback extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
get commentMessage() {
|
||||
const { config, isGrading } = this.props;
|
||||
let commentMessage = this.translate(isGrading ? messages.addComments : messages.comments);
|
||||
if (config === feedbackRequirement.optional) {
|
||||
commentMessage += ` ${this.translate(messages.optional)}`;
|
||||
}
|
||||
return commentMessage;
|
||||
}
|
||||
|
||||
translate = (msg) => this.props.intl.formatMessage(msg);
|
||||
|
||||
render() {
|
||||
@@ -42,9 +51,7 @@ export class CriterionFeedback extends React.Component {
|
||||
<Form.Control
|
||||
as="input"
|
||||
className="criterion-feedback feedback-input"
|
||||
floatingLabel={this.translate(
|
||||
isGrading ? messages.addComments : messages.comments,
|
||||
)}
|
||||
floatingLabel={this.commentMessage}
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
disabled={!isGrading}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
} from './CriterionFeedback';
|
||||
import messages from './messages';
|
||||
|
||||
jest.mock('data/redux/app/selectors', () => ({
|
||||
rubric: {
|
||||
@@ -27,7 +28,9 @@ jest.mock('data/redux/grading/selectors', () => ({
|
||||
})),
|
||||
},
|
||||
validation: {
|
||||
criterionFeedbackIsInvalid: jest.fn((...args) => ({ selectedFeedbackIsInvalid: args })),
|
||||
criterionFeedbackIsInvalid: jest.fn((...args) => ({
|
||||
selectedFeedbackIsInvalid: args,
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -67,11 +70,13 @@ describe('Criterion Feedback', () => {
|
||||
expect(el.instance().render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('is configure to disabled', () => {
|
||||
el.setProps({
|
||||
config: feedbackRequirement.disabled,
|
||||
Object.values(feedbackRequirement).forEach((requirement) => {
|
||||
test(`feedback is configured to ${requirement}`, () => {
|
||||
el.setProps({
|
||||
config: requirement,
|
||||
});
|
||||
expect(el.instance().render()).toMatchSnapshot();
|
||||
});
|
||||
expect(el.instance().render()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,6 +126,40 @@ describe('Criterion Feedback', () => {
|
||||
expect(props.setValue).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getter commentMessage', () => {
|
||||
test('is grading', () => {
|
||||
el.setProps({ config: feedbackRequirement.optional, isGrading: true });
|
||||
expect(el.instance().commentMessage).toContain(
|
||||
messages.optional.defaultMessage,
|
||||
);
|
||||
|
||||
el.setProps({ config: feedbackRequirement.required });
|
||||
expect(el.instance().commentMessage).not.toContain(
|
||||
messages.optional.defaultMessage,
|
||||
);
|
||||
|
||||
expect(el.instance().commentMessage).toContain(
|
||||
messages.addComments.defaultMessage,
|
||||
);
|
||||
});
|
||||
|
||||
test('is not grading', () => {
|
||||
el.setProps({ config: feedbackRequirement.optional, isGrading: false });
|
||||
expect(el.instance().commentMessage).toContain(
|
||||
messages.optional.defaultMessage,
|
||||
);
|
||||
|
||||
el.setProps({ config: feedbackRequirement.required });
|
||||
expect(el.instance().commentMessage).not.toContain(
|
||||
messages.optional.defaultMessage,
|
||||
);
|
||||
|
||||
expect(el.instance().commentMessage).toContain(
|
||||
messages.comments.defaultMessage,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapStateToProps', () => {
|
||||
@@ -142,7 +181,10 @@ describe('Criterion Feedback', () => {
|
||||
});
|
||||
test('selector.grading.validation.criterionFeedbackIsInvalid', () => {
|
||||
expect(mapped.isInvalid).toEqual(
|
||||
selectors.grading.validation.criterionFeedbackIsInvalid(testState, ownProps),
|
||||
selectors.grading.validation.criterionFeedbackIsInvalid(
|
||||
testState,
|
||||
ownProps,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Criterion Feedback snapshot feedback is configured to disabled 1`] = `null`;
|
||||
|
||||
exports[`Criterion Feedback snapshot feedback is configured to optional 1`] = `
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
as="input"
|
||||
className="criterion-feedback feedback-input"
|
||||
disabled={false}
|
||||
floatingLabel="Add comments (Optional)"
|
||||
onChange={[MockFunction this.onChange]}
|
||||
value="criterion value"
|
||||
/>
|
||||
</Form.Group>
|
||||
`;
|
||||
|
||||
exports[`Criterion Feedback snapshot feedback is configured to required 1`] = `
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
as="input"
|
||||
className="criterion-feedback feedback-input"
|
||||
disabled={false}
|
||||
floatingLabel="Add comments"
|
||||
onChange={[MockFunction this.onChange]}
|
||||
value="criterion value"
|
||||
/>
|
||||
</Form.Group>
|
||||
`;
|
||||
|
||||
exports[`Criterion Feedback snapshot feedback value is invalid 1`] = `
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
@@ -19,8 +47,6 @@ exports[`Criterion Feedback snapshot feedback value is invalid 1`] = `
|
||||
</Form.Group>
|
||||
`;
|
||||
|
||||
exports[`Criterion Feedback snapshot is configure to disabled 1`] = `null`;
|
||||
|
||||
exports[`Criterion Feedback snapshot is graded 1`] = `
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
|
||||
@@ -11,6 +11,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Comments',
|
||||
description: 'label for read-only feedback field',
|
||||
},
|
||||
optional: {
|
||||
id: 'ora-grading.CriterionFeedback.optional',
|
||||
defaultMessage: '(Optional)',
|
||||
description: 'addtional label for optional feedback field',
|
||||
},
|
||||
optionPoints: {
|
||||
id: 'ora-grading.RadioCriterion.optionPoints',
|
||||
defaultMessage: '{points} points',
|
||||
|
||||
@@ -47,6 +47,10 @@
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.response-display {
|
||||
min-width: 100vw;
|
||||
width: 100%;
|
||||
|
||||
.preview-display {
|
||||
padding: map-get($spacers, 1) 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,15 +15,19 @@ import ReviewErrors from './ReviewErrors';
|
||||
* <ReviewContent />
|
||||
*/
|
||||
export const ReviewContent = ({ isFailed, isLoaded, showRubric }) => (isLoaded || isFailed) && (
|
||||
<div className="content-block">
|
||||
<div className="content-block">
|
||||
<div className="content-wrapper">
|
||||
<ReviewErrors />
|
||||
{ isLoaded && (
|
||||
<Row className="flex-nowrap">
|
||||
<Col><ResponseDisplay /></Col>
|
||||
{ showRubric && <Rubric /> }
|
||||
</Row>
|
||||
{isLoaded && (
|
||||
<Row className="flex-nowrap m-0">
|
||||
<Col className="p-0">
|
||||
<ResponseDisplay />
|
||||
</Col>
|
||||
{showRubric && <Rubric />}
|
||||
</Row>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
ReviewContent.defaultProps = {
|
||||
isFailed: false,
|
||||
@@ -37,8 +41,12 @@ ReviewContent.propTypes = {
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
isFailed: selectors.requests.isFailed(state, { requestKey: RequestKeys.fetchSubmission }),
|
||||
isLoaded: selectors.requests.isCompleted(state, { requestKey: RequestKeys.fetchSubmission }),
|
||||
isFailed: selectors.requests.isFailed(state, {
|
||||
requestKey: RequestKeys.fetchSubmission,
|
||||
}),
|
||||
isLoaded: selectors.requests.isCompleted(state, {
|
||||
requestKey: RequestKeys.fetchSubmission,
|
||||
}),
|
||||
showRubric: selectors.app.showRubric(state),
|
||||
});
|
||||
|
||||
|
||||
@@ -17,15 +17,27 @@
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
width: min-content;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.review-modal-body {
|
||||
padding: 0 !important;
|
||||
overflow-y: hidden !important;
|
||||
overflow: hidden !important;
|
||||
|
||||
& > div.pgn__modal-body-content {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.content-block .col {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,11 @@ exports[`ReviewContent component component render tests snapshot: failed, showRu
|
||||
<div
|
||||
className="content-block"
|
||||
>
|
||||
<ReviewErrors />
|
||||
<div
|
||||
className="content-wrapper"
|
||||
>
|
||||
<ReviewErrors />
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -12,15 +16,21 @@ exports[`ReviewContent component component render tests snapshot: hide rubric 1`
|
||||
<div
|
||||
className="content-block"
|
||||
>
|
||||
<ReviewErrors />
|
||||
<Row
|
||||
className="flex-nowrap"
|
||||
<div
|
||||
className="content-wrapper"
|
||||
>
|
||||
<Col>
|
||||
<ResponseDisplay />
|
||||
</Col>
|
||||
<Rubric />
|
||||
</Row>
|
||||
<ReviewErrors />
|
||||
<Row
|
||||
className="flex-nowrap m-0"
|
||||
>
|
||||
<Col
|
||||
className="p-0"
|
||||
>
|
||||
<ResponseDisplay />
|
||||
</Col>
|
||||
<Rubric />
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -28,13 +38,19 @@ exports[`ReviewContent component component render tests snapshot: show rubric 1`
|
||||
<div
|
||||
className="content-block"
|
||||
>
|
||||
<ReviewErrors />
|
||||
<Row
|
||||
className="flex-nowrap"
|
||||
<div
|
||||
className="content-wrapper"
|
||||
>
|
||||
<Col>
|
||||
<ResponseDisplay />
|
||||
</Col>
|
||||
</Row>
|
||||
<ReviewErrors />
|
||||
<Row
|
||||
className="flex-nowrap m-0"
|
||||
>
|
||||
<Col
|
||||
className="p-0"
|
||||
>
|
||||
<ResponseDisplay />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.rubric-feedback {
|
||||
}
|
||||
|
||||
.criterion-feedback {
|
||||
margin-top: 1rem;
|
||||
@@ -51,6 +49,9 @@
|
||||
width: 320px;
|
||||
height: fit-content;
|
||||
max-height: 100%;
|
||||
margin-left: map-get($spacers, 3);
|
||||
position: sticky !important;
|
||||
top: map-get($spacers, 1) * -1;
|
||||
|
||||
.grading-rubric-header {
|
||||
box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.3) !important;
|
||||
@@ -74,3 +75,9 @@
|
||||
opacity: .4 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.grading-rubric-card {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ exports[`Rubric Feedback component snapshot feedback value is invalid 1`] = `
|
||||
as="input"
|
||||
className="rubric-feedback feedback-input"
|
||||
disabled={false}
|
||||
floatingLabel="Add comments"
|
||||
floatingLabel="Add comments (Optional)"
|
||||
onChange={[MockFunction this.onChange]}
|
||||
value="some value"
|
||||
/>
|
||||
@@ -67,7 +67,7 @@ exports[`Rubric Feedback component snapshot is graded 1`] = `
|
||||
as="input"
|
||||
className="rubric-feedback feedback-input"
|
||||
disabled={true}
|
||||
floatingLabel="Comments"
|
||||
floatingLabel="Comments (Optional)"
|
||||
onChange={[MockFunction this.onChange]}
|
||||
value="some value"
|
||||
/>
|
||||
@@ -98,7 +98,7 @@ exports[`Rubric Feedback component snapshot is grading 1`] = `
|
||||
as="input"
|
||||
className="rubric-feedback feedback-input"
|
||||
disabled={false}
|
||||
floatingLabel="Add comments"
|
||||
floatingLabel="Add comments (Optional)"
|
||||
onChange={[MockFunction this.onChange]}
|
||||
value="some value"
|
||||
/>
|
||||
|
||||
@@ -28,12 +28,12 @@ const messages = defineMessages({
|
||||
},
|
||||
addComments: {
|
||||
id: 'ora-grading.Rubric.addComments',
|
||||
defaultMessage: 'Add comments',
|
||||
defaultMessage: 'Add comments (Optional)',
|
||||
description: 'Rubric comments input label',
|
||||
},
|
||||
comments: {
|
||||
id: 'ora-grading.Rubric.comments',
|
||||
defaultMessage: 'Comments',
|
||||
defaultMessage: 'Comments (Optional)',
|
||||
description: 'Rubric comments display label',
|
||||
},
|
||||
overallFeedbackError: {
|
||||
|
||||
Reference in New Issue
Block a user