diff --git a/library/src/skins/default/src/sass/modules/tool/rubrics/_rubrics.scss b/library/src/skins/default/src/sass/modules/tool/rubrics/_rubrics.scss index ae9a174803f3..887ebcbb76ad 100644 --- a/library/src/skins/default/src/sass/modules/tool/rubrics/_rubrics.scss +++ b/library/src/skins/default/src/sass/modules/tool/rubrics/_rubrics.scss @@ -476,6 +476,10 @@ sakai-rubric-student-comment { background-color: unset; } } + + div.rubric-comment-body { + min-width: 180px; + } } .rubric-criterion-comment-title { diff --git a/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/SakaiGrader.js b/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/SakaiGrader.js index 0bf2c4ccc969..60d7bf7f0550 100644 --- a/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/SakaiGrader.js +++ b/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/SakaiGrader.js @@ -173,17 +173,13 @@ export class SakaiGrader extends graderRenderingMixin(gradableDataMixin(SakaiEle document.getElementById("grader").addEventListener("hide.bs.offcanvas", e => { + this.querySelector("sakai-rubric-grading")?.closeCommentEditors(); + if (this.modified || this.querySelector("sakai-grader-file-picker")?.hasFiles()) { e.preventDefault(); this._save({ closeSidebarTimeout: 2000 }); } - }); - - document.getElementById("grader").addEventListener("hide.bs.offcanvas", () => { - this.querySelectorAll(".rubric-comment-trigger").forEach(trigger => { - bootstrap.Popover.getInstance(trigger).hide(); - }); }); document.getElementById("grader").addEventListener("hidden.bs.offcanvas", () => { @@ -237,17 +233,13 @@ export class SakaiGrader extends graderRenderingMixin(gradableDataMixin(SakaiEle this._rubricShowing = false; this.querySelector("sakai-rubric-grading")?.displayGradingTab(); - } - _doneWithRubric() { + this.updateComplete.then(() => { - this.querySelector("#grader-rubric-link").focus(); - - this.querySelector("sakai-rubric-grading-button").setHasEvaluation(); - this.querySelector("sakai-rubric-evaluation-remover").setHasEvaluation(); - this.requestUpdate(); - - this._closeRubric(); + this.querySelector("#grader-rubric-link").focus(); + this.querySelector("sakai-rubric-grading-button").setHasEvaluation(); + this.querySelector("sakai-rubric-evaluation-remover").setHasEvaluation(); + }); } _replaceWithEditor(id, changedCallback) { @@ -259,7 +251,7 @@ export class SakaiGrader extends graderRenderingMixin(gradableDataMixin(SakaiEle editor.on("change", e => { - changedCallback && changedCallback(e.editor.getData()); + changedCallback?.(e.editor.getData()); this.modified = true; }); @@ -640,7 +632,7 @@ export class SakaiGrader extends graderRenderingMixin(gradableDataMixin(SakaiEle filtered = filtered.filter(s => s.graded); } - if (this.currentGroups && this.currentGroups.length === 1 && this.currentGroups[0].includes("/group")) { + if (this.currentGroups?.length === 1 && this.currentGroups[0].includes("/group")) { const group = this.groups.find(g => g.reference === this.currentGroups[0]); filtered = filtered.filter(s => group.users.includes(s.firstSubmitterId)); } @@ -671,7 +663,7 @@ export class SakaiGrader extends graderRenderingMixin(gradableDataMixin(SakaiEle } _areSettingsInAction() { - return (this.currentGroups && this.currentGroups.length > 0 && this.currentGroups[0] !== `/site/${portal.siteId}`) || this._submittedOnly || this._ungradedOnly || this._gradedOnly; + return (this.currentGroups?.length > 0 && this.currentGroups[0] !== `/site/${portal.siteId}`) || this._submittedOnly || this._ungradedOnly || this._gradedOnly; } _getSubmitter(submission) { @@ -758,7 +750,7 @@ export class SakaiGrader extends graderRenderingMixin(gradableDataMixin(SakaiEle if (!confirm(this.i18n.confirm_remove_private_notes)) return false; this._submission.privateNotes = ""; - this.privateNotesEditor && this.privateNotesEditor.setData(""); + this.privateNotesEditor?.setData(""); this.modified = true; this._gradeOrCommentsModified = true; this._privateNotesRemoved = true; @@ -769,7 +761,7 @@ export class SakaiGrader extends graderRenderingMixin(gradableDataMixin(SakaiEle if (!confirm(this.i18n.confirm_remove_feedback_comment)) return false; this._submission.feedbackComment = ""; - this.feedbackCommentEditor && this.feedbackCommentEditor.setData(""); + this.feedbackCommentEditor?.setData(""); this.modified = true; this._gradeOrCommentsModified = true; this._feedbackCommentRemoved = true; diff --git a/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/sakai-grader-rendering-mixin.js b/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/sakai-grader-rendering-mixin.js index cecda64b6fa6..ba1067ce897b 100644 --- a/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/sakai-grader-rendering-mixin.js +++ b/webcomponents/tool/src/main/frontend/packages/sakai-grader/src/sakai-grader-rendering-mixin.js @@ -401,41 +401,43 @@ export const graderRenderingMixin = Base => class extends Base { ${this._renderGradeInputs(this.i18n["gen.assign.gra"])} ${this.hasAssociatedRubric === "true" ? html` -
-
- -
-
- - - - + ${!this._rubricShowing ? html` +
+
+ +
+
+ + + + +
-
+ ` : nothing}
class extends Base {
diff --git a/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGrading.js b/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGrading.js index f52056c0eede..e1e57e3f00bb 100644 --- a/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGrading.js +++ b/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGrading.js @@ -84,6 +84,10 @@ export class SakaiRubricGrading extends rubricsApiMixin(RubricsElement) { } } + closeCommentEditors() { + this.querySelectorAll("sakai-rubric-grading-comment").forEach(c => c.hideEditor()); + } + shouldUpdate() { return this._i18n && this.association; } @@ -114,12 +118,12 @@ export class SakaiRubricGrading extends rubricsApiMixin(RubricsElement) { -
+
${this._evaluation && this._evaluation.status === "DRAFT" && !this.isPeerOrSelf ? html`
${this.tr("draft_evaluation", [ this.tr(`draft_evaluation_${this.toolId}`) ])}
- ` : nothing } + ` : nothing }
${this._criteria.map(c => html`
diff --git a/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGradingComment.js b/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGradingComment.js index 642d7a5c5c41..85444df2903a 100644 --- a/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGradingComment.js +++ b/webcomponents/tool/src/main/frontend/packages/sakai-rubrics/src/SakaiRubricGradingComment.js @@ -17,109 +17,95 @@ export class SakaiRubricGradingComment extends RubricsElement { this.randombit = Math.floor(Math.random() * 15001); } - - set criterion(newValue) { - - const oldValue = this._criterion; - this._criterion = newValue; - this._criterion.comments = newValue.comments && newValue.comments.indexOf("null") === 0 ? "" : newValue.comments; - this.requestUpdate("criterion", oldValue); + hideEditor() { + bootstrap.Dropdown.getOrCreateInstance(this.querySelector(".dropdown-menu"))?.hide(); } - get criterion() { return this._criterion; } + _setupEditor() { - firstUpdated() { + const editorOptions = { + startupFocus: true, + versionCheck: false, + removePlugins: "wordcount", + height: 60, + }; - this.setupEditor(); + if (sakai && sakai.editor) { + editorOptions.toolbarSet = "BasicText"; + } else { + editorOptions.toolbar = [ [ "Bold", "Italic", "Underline" ] ]; + } - const trigger = this.querySelector("button.rubric-comment-trigger"); - const popover = this.querySelector("div.rubric-comment-popover"); + const editorKey = `criterion-${this.criterion.id}-${this.evaluatedItemId}-comment-${this.randombit}`; + const editorFunction = sakai && sakai.editor ? sakai.editor.launch : CKEDITOR.replace; + this._commentEditor = editorFunction(editorKey, editorOptions); + + this._commentEditor.focus(); + + this._commentEditor.on("blur", () => { + + // When we click away from the comment editor we need to save the comment, but only if the comment has been updated + if (this._commentEditor.checkDirty()) { + this.criterion.comments = this._commentEditor.getData(); + const updateEvent = new CustomEvent("update-comment", { + detail: { + evaluatedItemId: this.evaluatedItemId, + entityId: this.entityId, + criterionId: this.criterion.id, + value: this.criterion.comments + }, + bubbles: true, composed: true }); + this.dispatchEvent(updateEvent); + this.requestUpdate(); + } + + this.hideEditor(); + }); + } - trigger.addEventListener("show.bs.popover", () => popover.classList.remove("d-none")); + _updateEditor() { - new bootstrap.Popover(trigger, { - content: popover, - html: true, + this._commentEditor?.setData(this.criterion.comments, () => { + this._commentEditor.updateElement(); + this._commentEditor.resetDirty(); }); } + firstUpdated() { this._setupEditor(); } + + updated() { this._updateEditor(); } + render() { return html` - - -
-
${this.tr("comment_for_criterion", [ this.criterion.title ])}
-
- -
-
- + aria-expanded="false"> + + + +
`; } - - hideTooltip() { - bootstrap.Popover.getInstance(this.querySelector("button.rubric-comment-trigger"))?.hide(); - } - - setupEditor() { - - const editorKey = `criterion-${this.criterion.id}-${this.evaluatedItemId}-comment-${this.randombit}`; - const editorOptions = { - startupFocus: true, - versionCheck: false, - removePlugins: "wordcount", - height: 60, - }; - let editorFunction; - if (sakai && sakai.editor) { - editorOptions.toolbarSet = "BasicText"; - editorFunction = sakai.editor.launch; - } else { - editorOptions.toolbar = [ [ "Bold", "Italic", "Underline" ] ] ; - editorFunction = CKEDITOR.replace; - } - - try { - const commentEditor = editorFunction(editorKey, editorOptions); - - commentEditor.focus(); - - commentEditor.on("blur", () => { - - // When we click away from the comment editor we need to save the comment, but only if the comment has been updated - const updatedComments = commentEditor.getData(); - const nonEmptyComment = this.criterion.comments !== undefined || updatedComments.trim().length > 0; - if (this.criterion.comments !== updatedComments && nonEmptyComment) { - this.criterion.comments = updatedComments; - const updateEvent = new CustomEvent("update-comment", { - detail: { - evaluatedItemId: this.evaluatedItemId, - entityId: this.entityId, - criterionId: this.criterion.id, - value: this.criterion.comments - }, - bubbles: true, composed: true }); - this.dispatchEvent(updateEvent); - this.requestUpdate(); - } - - this.hideTooltip(); - }); - } catch (error) { - console.error(error); - } - } }