Merge pull request #21961 from edx/dcs/jumpnav
Improve navigation on Studio unit page
This commit is contained in:
@@ -252,7 +252,7 @@
|
||||
|
||||
// update the data-attributes with latest contents only for updated problems.
|
||||
this.content_container
|
||||
.html(currentTab.text())
|
||||
.html(currentTab.text()) // xss-lint: disable=javascript-jquery-html
|
||||
.attr('aria-labelledby', currentTab.attr('aria-labelledby'))
|
||||
.data('bookmarked', bookmarked);
|
||||
|
||||
@@ -294,6 +294,9 @@
|
||||
// Links from courseware <a class='seqnav' href='n'>...</a>, was .target_tab
|
||||
if ($(event.currentTarget).hasClass('seqnav')) {
|
||||
newPosition = $(event.currentTarget).attr('href');
|
||||
} else if ($(event.currentTarget).data('href') !== undefined) {
|
||||
location.href = $(event.currentTarget).data('href');
|
||||
return true;
|
||||
// Tab links generated by backend template
|
||||
} else {
|
||||
newPosition = $(event.currentTarget).data('element');
|
||||
@@ -326,6 +329,7 @@
|
||||
this.render(newPosition);
|
||||
} else {
|
||||
alertTemplate = gettext('Sequence error! Cannot navigate to %(tab_name)s in the current SequenceModule. Please contact the course staff.'); // eslint-disable-line max-len
|
||||
// xss-lint: disable=javascript-interpolate
|
||||
alertText = interpolate(alertTemplate, {
|
||||
tab_name: newPosition
|
||||
}, true);
|
||||
|
||||
@@ -27,7 +27,7 @@ from .exceptions import NotFoundError
|
||||
from .fields import Date
|
||||
from .mako_module import MakoModuleDescriptor
|
||||
from .progress import Progress
|
||||
from .x_module import PUBLIC_VIEW, STUDENT_VIEW, XModule
|
||||
from .x_module import AUTHOR_VIEW, PUBLIC_VIEW, STUDENT_VIEW, XModule
|
||||
from .xml_module import XmlDescriptor
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -286,6 +286,13 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
prereq_met, prereq_meta_info = self._compute_is_prereq_met(True)
|
||||
return self._student_or_public_view(context or {}, prereq_met, prereq_meta_info, None, PUBLIC_VIEW)
|
||||
|
||||
def author_view(self, context):
|
||||
context = context or {}
|
||||
context['exclude_units'] = True
|
||||
if 'position' in context:
|
||||
context['position'] = int(context['position'])
|
||||
return self._student_or_public_view(context, True, {}, view=AUTHOR_VIEW)
|
||||
|
||||
def _special_exam_student_view(self):
|
||||
"""
|
||||
Checks whether this sequential is a special exam. If so, returns
|
||||
@@ -367,7 +374,8 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
'banner_text': banner_text,
|
||||
'save_position': view != PUBLIC_VIEW,
|
||||
'show_completion': view != PUBLIC_VIEW,
|
||||
'gated_content': self._get_gated_content_info(prereq_met, prereq_meta_info)
|
||||
'gated_content': self._get_gated_content_info(prereq_met, prereq_meta_info),
|
||||
'exclude_units': context.get('exclude_units', False)
|
||||
}
|
||||
fragment.add_content(self.system.render_template("seq_module.html", params))
|
||||
|
||||
@@ -464,9 +472,13 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
display_items. Returns a list of dict objects with information about
|
||||
the given display_items.
|
||||
"""
|
||||
render_items = not context.get('exclude_units', False)
|
||||
is_user_authenticated = self.is_user_authenticated(context)
|
||||
bookmarks_service = self.runtime.service(self, 'bookmarks')
|
||||
completion_service = self.runtime.service(self, 'completion')
|
||||
if render_items:
|
||||
bookmarks_service = self.runtime.service(self, 'bookmarks')
|
||||
completion_service = self.runtime.service(self, 'completion')
|
||||
else:
|
||||
bookmarks_service = completion_service = None
|
||||
context['username'] = self.runtime.service(self, 'user').get_current_user().opt_attrs.get(
|
||||
'edx-platform.username')
|
||||
display_names = [
|
||||
@@ -489,18 +501,21 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
show_bookmark_button = False
|
||||
is_bookmarked = False
|
||||
|
||||
if is_user_authenticated:
|
||||
if is_user_authenticated and render_items:
|
||||
show_bookmark_button = True
|
||||
is_bookmarked = bookmarks_service.is_bookmarked(usage_key=usage_id)
|
||||
|
||||
context['show_bookmark_button'] = show_bookmark_button
|
||||
context['bookmarked'] = is_bookmarked
|
||||
|
||||
rendered_item = item.render(view, context)
|
||||
fragment.add_fragment_resources(rendered_item)
|
||||
|
||||
if render_items:
|
||||
rendered_item = item.render(view, context)
|
||||
fragment.add_fragment_resources(rendered_item)
|
||||
content = rendered_item.content
|
||||
else:
|
||||
content = ''
|
||||
iteminfo = {
|
||||
'content': rendered_item.content,
|
||||
'content': content,
|
||||
'page_title': getattr(item, 'tooltip_title', ''),
|
||||
'type': item_type,
|
||||
'id': text_type(usage_id),
|
||||
@@ -508,8 +523,11 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
'path': " > ".join(display_names + [item.display_name_with_default]),
|
||||
'graded': item.graded
|
||||
}
|
||||
|
||||
if is_user_authenticated:
|
||||
if not render_items:
|
||||
# The item url format can be defined in the template context like so:
|
||||
# context['item_url'] = '/my/item/path/{usage_key}/whatever'
|
||||
iteminfo['href'] = context.get('item_url', '').format(usage_key=usage_id)
|
||||
if is_user_authenticated and render_items:
|
||||
if item.location.block_type == 'vertical':
|
||||
if completion_service:
|
||||
iteminfo['complete'] = completion_service.vertical_is_complete(item)
|
||||
@@ -679,6 +697,7 @@ class SequenceDescriptor(SequenceFields, ProctoringFields, MakoModuleDescriptor,
|
||||
mako_template = 'widgets/sequence-edit.html'
|
||||
module_class = SequenceModule
|
||||
resources_dir = None
|
||||
has_author_view = True
|
||||
|
||||
show_in_read_only_mode = True
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from bok_choy.promise import EmptyPromise, Promise
|
||||
|
||||
from common.test.acceptance.pages.common.utils import click_css, confirm_prompt
|
||||
from common.test.acceptance.pages.studio import BASE_URL
|
||||
from common.test.acceptance.pages.studio.utils import HelpMixin, type_in_codemirror
|
||||
from common.test.acceptance.pages.studio.utils import HelpMixin, set_input_value_and_save, type_in_codemirror
|
||||
from common.test.acceptance.tests.helpers import click_and_wait_for_window
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ class ContainerPage(PageObject, HelpMixin):
|
||||
Container page in Studio
|
||||
"""
|
||||
NAME_SELECTOR = '.page-header-title'
|
||||
NAME_INPUT_SELECTOR = '.page-header .xblock-field-input'
|
||||
NAME_FIELD_WRAPPER_SELECTOR = '.page-header .wrapper-xblock-field'
|
||||
NAME_INPUT_SELECTOR = '.wrapper-xblock-field .xblock-field-input'
|
||||
NAME_FIELD_WRAPPER_SELECTOR = '.wrapper-xblock-field'
|
||||
ADD_MISSING_GROUPS_SELECTOR = '.notification-action-button[data-notification-action="add-missing-groups"]'
|
||||
|
||||
def __init__(self, browser, locator):
|
||||
@@ -410,6 +410,13 @@ class ContainerPage(PageObject, HelpMixin):
|
||||
)
|
||||
return self.q(css=css).html
|
||||
|
||||
def set_name(self, name):
|
||||
"""
|
||||
Set the name of the unit.
|
||||
"""
|
||||
set_input_value_and_save(self, self.NAME_INPUT_SELECTOR, name)
|
||||
self.wait_for_ajax()
|
||||
|
||||
|
||||
class XBlockWrapper(PageObject):
|
||||
"""
|
||||
|
||||
@@ -33,7 +33,7 @@ class CourseOutlineItem(object):
|
||||
NAME_SELECTOR = '.item-title'
|
||||
NAME_INPUT_SELECTOR = '.xblock-field-input'
|
||||
NAME_FIELD_WRAPPER_SELECTOR = '.xblock-title .wrapper-xblock-field'
|
||||
STATUS_MESSAGE_SELECTOR = '> div[class$="status"] .status-message'
|
||||
STATUS_MESSAGE_SELECTOR = '> div[class$="-status"] .status-messages'
|
||||
CONFIGURATION_BUTTON_SELECTOR = '.action-item .configure-button'
|
||||
|
||||
def __repr__(self):
|
||||
@@ -84,7 +84,8 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
Returns the status message of this item.
|
||||
"""
|
||||
return self.q(css=self._bounded_selector(self.STATUS_MESSAGE_SELECTOR)).text[0] # pylint: disable=no-member
|
||||
selector = self._bounded_selector(self.STATUS_MESSAGE_SELECTOR)
|
||||
return self.q(css=selector).text[0] # pylint: disable=no-member
|
||||
|
||||
@property
|
||||
def has_staff_lock_warning(self):
|
||||
|
||||
@@ -88,14 +88,10 @@ class GradingPage(SettingsPage):
|
||||
Drag and drop grade range.
|
||||
"""
|
||||
self.wait_for_element_visibility(self.grade_ranges, "Grades ranges are visible")
|
||||
# We have used jquery here to adjust the width of slider to
|
||||
# desired range because drag and drop has behaved very inconsistently.
|
||||
# This does not updates the text of range on the slider.
|
||||
# So as a work around, we have used drag_and_drop without any offset
|
||||
self.browser.execute_script('$(".ui-resizable").css("width","10")')
|
||||
action = ActionChains(self.browser)
|
||||
moveable_css = self.q(css='.ui-resizable-e').results[0]
|
||||
action.drag_and_drop_by_offset(moveable_css, 0, 0).perform()
|
||||
action.drag_and_drop_by_offset(moveable_css, -280, 0)
|
||||
action.perform()
|
||||
|
||||
@property
|
||||
def get_assignment_names(self):
|
||||
|
||||
@@ -340,6 +340,7 @@ class WarningMessagesTest(CourseOutlineTest):
|
||||
subsection.add_unit()
|
||||
unit = ContainerPage(self.browser, None)
|
||||
unit.wait_for_page()
|
||||
unit.set_name(name)
|
||||
|
||||
if unit.is_staff_locked != unit_state.is_locked:
|
||||
unit.toggle_staff_lock()
|
||||
|
||||
Reference in New Issue
Block a user