Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions xblocks_contrib/annotatable/annotatable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import logging
import textwrap
import uuid
from importlib.resources import files

import markupsafe
from django.utils.translation import gettext_noop as _
Expand All @@ -29,6 +28,7 @@
AnnotatableXBlock allows instructors to create annotated content that students can view interactively.
Annotations can be styled and customized, with internationalization support for multilingual environments.
"""

# Indicates that this XBlock has been extracted from edx-platform.
is_extracted = True

Expand Down Expand Up @@ -141,9 +141,8 @@
"""Renders annotatable content with annotation spans and returns HTML."""

xmltree = etree.fromstring(self.data)
content = etree.tostring(xmltree, encoding="unicode")
self._extract_instructions(xmltree)

xmltree = etree.fromstring(content)
xmltree.tag = "div"
if "display_name" in xmltree.attrib:
del xmltree.attrib["display_name"]
Expand Down Expand Up @@ -179,15 +178,15 @@
i18n_service=self.runtime.service(self, "i18n"),
)
)
frag.add_css(self.resource_string("static/css/annotatable.css"))
frag.add_javascript(self.resource_string("static/js/src/annotatable.js"))
frag.add_css(resource_loader.load_unicode("static/css/annotatable.css"))
frag.add_javascript(resource_loader.load_unicode("static/js/src/annotatable.js"))
frag.initialize_js("Annotatable")
return frag

Check warning on line 184 in xblocks_contrib/annotatable/annotatable.py

View check run for this annotation

Codecov / codecov/patch

xblocks_contrib/annotatable/annotatable.py#L181-L184

Added lines #L181 - L184 were not covered by tests

def studio_view(self, context=None): # pylint: disable=unused-argument
"""Return the studio view."""
frag = Fragment()
frag.add_content(

Check warning on line 189 in xblocks_contrib/annotatable/annotatable.py

View check run for this annotation

Codecov / codecov/patch

xblocks_contrib/annotatable/annotatable.py#L188-L189

Added lines #L188 - L189 were not covered by tests
resource_loader.render_django_template(
"templates/annotatable_editor.html",
{
Expand All @@ -197,9 +196,9 @@
)
)

frag.add_css(self.resource_string("static/css/annotatable_editor.css"))
frag.add_javascript(self.resource_string("static/js/src/annotatable_editor.js"))
frag.add_css(resource_loader.load_unicode("static/css/annotatable_editor.css"))
frag.add_javascript(resource_loader.load_unicode("static/js/src/annotatable_editor.js"))
frag.initialize_js("XMLEditingDescriptor")

Check warning on line 201 in xblocks_contrib/annotatable/annotatable.py

View check run for this annotation

Codecov / codecov/patch

xblocks_contrib/annotatable/annotatable.py#L199-L201

Added lines #L199 - L201 were not covered by tests
return frag

@XBlock.json_handler
Expand Down Expand Up @@ -231,7 +230,3 @@
""",
),
]

def resource_string(self, path):
"""Handy helper for getting resources from our kit."""
return files(__package__).joinpath(path).read_text(encoding="utf-8")
28 changes: 13 additions & 15 deletions xblocks_contrib/annotatable/static/css/annotatable.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
.annotatable-section {
position: relative;
padding: 0.5em 1em;
border: 1px solid #c8c8c8;
border: 1px solid var(--gray-l3, #c8c8c8);
border-radius: 0.5em;
margin-bottom: 0.5em;
}
Expand All @@ -23,7 +23,7 @@
font-weight: normal;
}
.annotatable-section .annotatable-section-body {
border-top: 1px solid #c8c8c8;
border-top: 1px solid var(--gray-l3, #c8c8c8);
margin-top: 0.5em;
padding-top: 0.5em;
}
Expand Down Expand Up @@ -118,18 +118,18 @@
border: 1px solid #333;
border-radius: 1em;
background-color: rgba(0, 0, 0, 0.85);
color: #fff;
color: var(--white, #fff);
-webkit-font-smoothing: antialiased;
}
.ui-tooltip.qtip.ui-tooltip .ui-tooltip-titlebar {
font-size: 1em;
color: inherit;
background-color: transparent;
padding: 5px 10px;
padding: calc((var(--baseline, 20px) / 4)) calc((var(--baseline, 20px) / 2));
border: none;
}
.ui-tooltip.qtip.ui-tooltip .ui-tooltip-titlebar .ui-tooltip-title {
padding: 5px 0;
padding: calc((var(--baseline, 20px) / 4)) 0;
border-bottom: 2px solid #333;
font-weight: bold;
}
Expand All @@ -139,14 +139,14 @@
}
.ui-tooltip.qtip.ui-tooltip .ui-tooltip-titlebar .ui-state-hover {
color: inherit;
border: 1px solid #c8c8c8;
border: 1px solid var(--gray-l3, #c8c8c8);
}
.ui-tooltip.qtip.ui-tooltip .ui-tooltip-content {
color: inherit;
font-size: 0.875em;
text-align: left;
font-weight: 400;
padding: 0 10px 10px 10px;
padding: 0 calc((var(--baseline, 20px) / 2)) calc((var(--baseline, 20px) / 2)) calc((var(--baseline, 20px) / 2));
background-color: transparent;
border-color: transparent;
}
Expand All @@ -158,33 +158,31 @@
max-width: 375px;
}
.ui-tooltip.qtip.ui-tooltip-annotatable .ui-tooltip-content {
padding: 0 10px;
padding: 0 calc((var(--baseline, 20px) / 2));
}
.ui-tooltip.qtip.ui-tooltip-annotatable
.ui-tooltip-content
.annotatable-comment {
.ui-tooltip.qtip.ui-tooltip-annotatable .ui-tooltip-content .annotatable-comment {
display: block;
margin: 0 0 10px 0;
margin: 0 0 calc((var(--baseline, 20px) / 2)) 0;
max-height: 225px;
overflow: auto;
line-height: normal;
}
.ui-tooltip.qtip.ui-tooltip-annotatable .ui-tooltip-content .annotatable-reply {
display: block;
border-top: 2px solid #333;
padding: 5px 0;
padding: calc((var(--baseline, 20px) / 4)) 0;
margin: 0;
text-align: center;
}
.ui-tooltip.qtip.ui-tooltip-annotatable::after {
content: "";
content: '';
display: inline-block;
position: absolute;
bottom: -20px;
left: 50%;
height: 0;
width: 0;
margin-left: -5px;
margin-left: calc(-1 * (var(--baseline, 20px) / 4));
border: 10px solid transparent;
border-top-color: rgba(0, 0, 0, 0.85);
}
3 changes: 0 additions & 3 deletions xblocks_contrib/annotatable/static/css/annotatable_editor.css
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
.wrapper-comp-editor {
display: block;
}

.wrapper-comp-editor.is-inactive {
display: none;
}

.wrapper-comp-editor.latex-problem {
margin-top: 50px;
}

div.wrapper-comp-editor.is-inactive ~ div.launch-latex-compiler {
display: none;
}
11 changes: 3 additions & 8 deletions xblocks_contrib/annotatable/static/js/src/annotatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,28 @@ function Annotatable(runtime, element) {
var toggleAnnotationsSelector = '.annotatable-toggle-annotations';
var toggleInstructionsSelector = '.annotatable-toggle-instructions';
var instructionsSelector = '.annotatable-instructions';
var sectionSelector = '.annotatable-section';
var spanSelector = '.annotatable-span';
var replySelector = '.annotatable-reply';

/*
Selectors for responding to events from the annotation capa problem type
*/
var problemXModuleSelector = '.xmodule_CapaModule';
var problemSelector = 'div.problem';
var problemInputSelector = 'div.problem .annotation-input';
var problemReturnSelector = 'div.problem .annotation-return';

var annotationsHidden = false;
var instructionsHidden = false;

var el = element;
var $el = $(element);

if (_debug) {
console.log('loaded Annotatable');
}

init();

function init() {
initEvents();
initTips();
if (_debug) {
console.log('Annotatable XBlock Loaded');
}
}

function initEvents() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ function XMLEditingDescriptor(runtime, element) {
var _debug = false;

var el = element;
var $el = $(element);

var textarea = $(editBoxSelector, el)[0];
if (!textarea) {
Expand Down
40 changes: 12 additions & 28 deletions xblocks_contrib/annotatable/tests/test_annotatable.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,11 @@
from lxml import etree
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds
from xblock.runtime import Runtime
from xblock.test.tools import TestRuntime

from xblocks_contrib.annotatable.annotatable import AnnotatableBlock


class MockRuntime(Runtime):
"""A mock implementation of the Runtime class for testing purposes."""

def __init__(self):
# id_reader and id_generator are required by Runtime.
super().__init__(id_reader=lambda: None, id_generator=lambda: None)

def handler_url(self, block, handler_name, suffix="", query=""):
return f"/mock_url/{handler_name}"

def local_resource_url(self, block, resource):
return f"/mock_resource_url/{resource}"

def resource_url(self, resource):
return f"/mock_resource/{resource}"

def publish(self, block, event_type, event_data):
pass


class AnnotatableBlockTestCase(unittest.TestCase):
sample_xml = """
<annotatable display_name="Iliad">
Expand All @@ -54,7 +34,7 @@ class AnnotatableBlockTestCase(unittest.TestCase):

def setUp(self):
super().setUp()
runtime = MockRuntime()
runtime = TestRuntime()
scope_ids = ScopeIds("user_id", "block_type", "block_id", "course_id")
field_data = DictFieldData({"data": self.sample_xml})
self.annotatable = AnnotatableBlock(runtime, field_data, scope_ids)
Expand Down Expand Up @@ -83,6 +63,14 @@ def test_annotation_class_attr_default(self):
assert isinstance(actual_attr, dict)
self.assertDictEqual(expected_attr, actual_attr)

def test_instruction_removal(self):
xmltree = etree.fromstring(self.sample_xml)
instructions = self.annotatable._extract_instructions(xmltree)

assert instructions is not None
assert "Read the text." in instructions
assert xmltree.find("instructions") is None

def test_annotation_class_attr_with_valid_highlight(self):
xml = '<annotation title="x" body="y" problem="0" highlight="{highlight}">test</annotation>'

Expand All @@ -101,9 +89,7 @@ def test_annotation_class_attr_with_invalid_highlight(self):

for invalid_color in ["rainbow", "blink", "invisible", "", None]:
el = etree.fromstring(xml.format(highlight=invalid_color))
expected_attr = {
"class": {"value": "annotatable-span highlight", "_delete": "highlight"}
}
expected_attr = {"class": {"value": "annotatable-span highlight", "_delete": "highlight"}}
actual_attr = self.annotatable._get_annotation_class_attr(0, el)

assert isinstance(actual_attr, dict)
Expand All @@ -117,9 +103,7 @@ def test_render_annotation(self):
)
expected_el = etree.fromstring(expected_html)

actual_el = etree.fromstring(
'<annotation title="x" body="y" problem="0" highlight="yellow">z</annotation>'
)
actual_el = etree.fromstring('<annotation title="x" body="y" problem="0" highlight="yellow">z</annotation>')
self.annotatable._render_annotation(0, actual_el)

assert expected_el.tag == actual_el.tag
Expand Down