feat: Tagging UX refinements - refresh tag count on edit (#34059)
* style: drawer-cover color updated for all tagging drawers * feat: Update TagList component when a tag is updated on Manage tags drawer * feat: Refactor TagCount to be able to refresh the count * feat: Sync tag count in units
This commit is contained in:
13
cms/static/js/factories/tag_count.js
Normal file
13
cms/static/js/factories/tag_count.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as TagCountView from 'js/views/tag_count';
|
||||
import * as TagCountModel from 'js/models/tag_count';
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
'use strict';
|
||||
export default function TagCountFactory(TagCountJson, el) {
|
||||
var model = new TagCountModel(TagCountJson, {parse: true});
|
||||
var tagCountView = new TagCountView({el, model});
|
||||
tagCountView.setupMessageListener();
|
||||
tagCountView.render();
|
||||
}
|
||||
|
||||
export {TagCountFactory};
|
||||
13
cms/static/js/models/tag_count.js
Normal file
13
cms/static/js/models/tag_count.js
Normal file
@@ -0,0 +1,13 @@
|
||||
define(['backbone', 'underscore'], function(Backbone, _) {
|
||||
/**
|
||||
* Model for Tag count view
|
||||
*/
|
||||
var TagCountModel = Backbone.Model.extend({
|
||||
defaults: {
|
||||
content_id: null,
|
||||
tags_count: 0,
|
||||
course_authoring_url: null,
|
||||
},
|
||||
});
|
||||
return TagCountModel;
|
||||
});
|
||||
@@ -12,11 +12,11 @@ define(['jquery', 'underscore', 'js/views/xblock_outline', 'edx-ui-toolkit/js/ut
|
||||
'common/js/components/utils/view_utils', 'js/views/utils/xblock_utils',
|
||||
'js/models/xblock_outline_info', 'js/views/modals/course_outline_modals', 'js/utils/drag_and_drop',
|
||||
'common/js/components/views/feedback_notification', 'common/js/components/views/feedback_prompt',
|
||||
'js/views/utils/tagging_drawer_utils',],
|
||||
'js/views/utils/tagging_drawer_utils', 'js/views/tag_count', 'js/models/tag_count'],
|
||||
function(
|
||||
$, _, XBlockOutlineView, StringUtils, ViewUtils, XBlockViewUtils,
|
||||
XBlockOutlineInfo, CourseOutlineModalsFactory, ContentDragger, NotificationView, PromptView,
|
||||
TaggingDrawerUtils
|
||||
TaggingDrawerUtils, TagCountView, TagCountModel
|
||||
) {
|
||||
var CourseOutlineView = XBlockOutlineView.extend({
|
||||
// takes XBlockOutlineInfo as a model
|
||||
@@ -28,9 +28,28 @@ function(
|
||||
this.makeContentDraggable(this.el);
|
||||
// Show/hide the paste button
|
||||
this.initializePasteButton(this.el);
|
||||
this.renderTagCount();
|
||||
return renderResult;
|
||||
},
|
||||
|
||||
renderTagCount: function() {
|
||||
const contentId = this.model.get('id');
|
||||
const tagCountsByUnit = this.model.get('tag_counts_by_unit')
|
||||
const tagsCount = tagCountsByUnit !== undefined ? tagCountsByUnit[contentId] : 0
|
||||
var countModel = new TagCountModel({
|
||||
content_id: contentId,
|
||||
tags_count: tagsCount,
|
||||
course_authoring_url: this.model.get('course_authoring_url'),
|
||||
}, {parse: true});
|
||||
var tagCountView = new TagCountView({el: this.$('.tag-count'), model: countModel});
|
||||
tagCountView.setupMessageListener();
|
||||
tagCountView.render();
|
||||
this.$('.tag-count').click((event) => {
|
||||
event.preventDefault();
|
||||
this.openManageTagsDrawer();
|
||||
});
|
||||
},
|
||||
|
||||
shouldExpandChildren: function() {
|
||||
return this.expandedLocators.contains(this.model.get('id'));
|
||||
},
|
||||
@@ -461,10 +480,8 @@ function(
|
||||
},
|
||||
|
||||
openManageTagsDrawer() {
|
||||
const article = document.querySelector('[data-taxonomy-tags-widget-url]');
|
||||
const taxonomyTagsWidgetUrl = $(article).attr('data-taxonomy-tags-widget-url');
|
||||
const taxonomyTagsWidgetUrl = this.model.get('taxonomy_tags_widget_url');
|
||||
const contentId = this.model.get('id');
|
||||
|
||||
TaggingDrawerUtils.openDrawer(taxonomyTagsWidgetUrl, contentId);
|
||||
},
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ function($, _, Backbone, gettext, BasePage,
|
||||
el: this.$('.unit-tags'),
|
||||
model: this.model
|
||||
});
|
||||
this.tagListView.setupMessageListener();
|
||||
this.tagListView.render();
|
||||
|
||||
this.unitOutlineView = new UnitOutlineView({
|
||||
|
||||
@@ -370,6 +370,83 @@ function($, _, gettext, BaseView, ViewUtils, XBlockViewUtils, MoveXBlockUtils, H
|
||||
}
|
||||
},
|
||||
|
||||
setupMessageListener: function () {
|
||||
window.addEventListener(
|
||||
"message", (event) => {
|
||||
// Listen any message from Manage tags drawer.
|
||||
var data = event.data;
|
||||
var courseAuthoringUrl = this.model.get("course_authoring_url")
|
||||
if (event.origin == courseAuthoringUrl
|
||||
&& data.includes('[Manage tags drawer] Tags updated:')) {
|
||||
// This message arrives when there is a change in the tag list.
|
||||
// The message contains the new list of tags.
|
||||
let jsonData = data.replace(/\[Manage tags drawer\] Tags updated: /g, "");
|
||||
jsonData = JSON.parse(jsonData);
|
||||
if (jsonData.contentId == this.model.id) {
|
||||
this.model.set('tags', this.buildTaxonomyTree(jsonData));
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
buildTaxonomyTree: function(data) {
|
||||
// TODO We can use this function for the initial request of tags
|
||||
// and avoid to use two functions (see get_unit_tags on contentstore/views/component.py)
|
||||
|
||||
var taxonomyList = [];
|
||||
var totalCount = 0;
|
||||
var actualId = 0;
|
||||
data.taxonomies.forEach((taxonomy) => {
|
||||
// Build a tag tree for each taxonomy
|
||||
var rootTagsValues = [];
|
||||
var tags = {};
|
||||
taxonomy.tags.forEach((tag) => {
|
||||
// Creates the tags for all the lineage of this tag
|
||||
for (let i = tag.lineage.length - 1; i >= 0; i--){
|
||||
var tagValue = tag.lineage[i]
|
||||
var tagProcessedBefore = tags.hasOwnProperty(tagValue);
|
||||
if (!tagProcessedBefore) {
|
||||
tags[tagValue] = {
|
||||
id: actualId,
|
||||
value: tagValue,
|
||||
children: [],
|
||||
}
|
||||
actualId++;
|
||||
if (i == 0) {
|
||||
rootTagsValues.push(tagValue);
|
||||
}
|
||||
}
|
||||
if (i !== tag.lineage.length - 1) {
|
||||
// Add a child into the children list
|
||||
tags[tagValue].children.push(tags[tag.lineage[i + 1]])
|
||||
}
|
||||
if (tagProcessedBefore) {
|
||||
// Break this loop if the tag has been processed before,
|
||||
// we don't need to process lineage again to avoid duplicates.
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var tagCount = Object.keys(tags).length;
|
||||
// Add the tree to the taxonomy list
|
||||
taxonomyList.push({
|
||||
id: taxonomy.taxonomyId,
|
||||
value: taxonomy.name,
|
||||
tags: rootTagsValues.map(rootValue => tags[rootValue]),
|
||||
count: tagCount,
|
||||
});
|
||||
totalCount += tagCount;
|
||||
});
|
||||
|
||||
return {
|
||||
count: totalCount,
|
||||
taxonomies: taxonomyList,
|
||||
};
|
||||
},
|
||||
|
||||
handleKeyDownOnHeader: function(event) {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
|
||||
54
cms/static/js/views/tag_count.js
Normal file
54
cms/static/js/views/tag_count.js
Normal file
@@ -0,0 +1,54 @@
|
||||
define(['jquery', 'underscore', 'js/views/baseview', 'edx-ui-toolkit/js/utils/html-utils'],
|
||||
function($, _, BaseView, HtmlUtils) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* TagCountView displays the tag count of a unit/component
|
||||
*
|
||||
* This component is being rendered in this way to allow receiving
|
||||
* messages from the Manage tags drawer and being able to update the count.
|
||||
*/
|
||||
var TagCountView = BaseView.extend({
|
||||
// takes TagCountModel as a model
|
||||
|
||||
initialize: function() {
|
||||
BaseView.prototype.initialize.call(this);
|
||||
this.template = this.loadTemplate('tag-count');
|
||||
},
|
||||
|
||||
setupMessageListener: function () {
|
||||
window.addEventListener(
|
||||
'message', (event) => {
|
||||
// Listen any message from Manage tags drawer.
|
||||
var data = event.data;
|
||||
var courseAuthoringUrl = this.model.get("course_authoring_url")
|
||||
if (event.origin == courseAuthoringUrl
|
||||
&& data.includes('[Manage tags drawer] Count updated:')) {
|
||||
// This message arrives when there is a change in the tag list.
|
||||
// The message contains the new count of tags.
|
||||
let jsonData = data.replace(/\[Manage tags drawer\] Count updated: /g, "");
|
||||
jsonData = JSON.parse(jsonData);
|
||||
if (jsonData.contentId == this.model.get("content_id")) {
|
||||
this.model.set('tags_count', jsonData.count);
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
HtmlUtils.setHtml(
|
||||
this.$el,
|
||||
HtmlUtils.HTML(
|
||||
this.template({
|
||||
tags_count: this.model.get("tags_count"),
|
||||
})
|
||||
)
|
||||
);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return TagCountView;
|
||||
});
|
||||
Reference in New Issue
Block a user