From bbc0cc2baaf7efc6319e0a65230c5d89ffce2bf6 Mon Sep 17 00:00:00 2001 From: Agrendalath Date: Mon, 6 Jan 2025 23:47:18 +0100 Subject: [PATCH] fix: show correct icons in the sidebar for units with custom XBlocks Currently, the sidebar relies only on the XBlock's `category` class attribute (called `type` in the transformers). This behavior is inconsistent with the legacy subsection navigation, which relies on the `XModuleMixin.get_icon_class` method. This commit adds the `icon_class` to the fields collected by the transformers and uses it to determine whether the "problem" or "video" icon should be displayed for a unit in the sidebar. --- .../course_api/blocks/serializers.py | 1 + .../blocks/transformers/blocks_api.py | 10 ++++++- .../course_home_api/outline/serializers.py | 6 +++- .../outline/tests/test_view.py | 30 +++++++++++++++++++ openedx/features/course_experience/utils.py | 1 + 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/course_api/blocks/serializers.py b/lms/djangoapps/course_api/blocks/serializers.py index b3370fc677..acf77c3076 100644 --- a/lms/djangoapps/course_api/blocks/serializers.py +++ b/lms/djangoapps/course_api/blocks/serializers.py @@ -59,6 +59,7 @@ SUPPORTED_FIELDS = [ SupportedFieldType('weight'), SupportedFieldType('show_correctness'), SupportedFieldType('hide_from_toc'), + SupportedFieldType('icon_class'), # 'student_view_data' SupportedFieldType(StudentViewTransformer.STUDENT_VIEW_DATA, StudentViewTransformer), # 'student_view_multi_device' diff --git a/lms/djangoapps/course_api/blocks/transformers/blocks_api.py b/lms/djangoapps/course_api/blocks/transformers/blocks_api.py index 81dc388aa1..cbfc398d81 100644 --- a/lms/djangoapps/course_api/blocks/transformers/blocks_api.py +++ b/lms/djangoapps/course_api/blocks/transformers/blocks_api.py @@ -54,7 +54,15 @@ class BlocksAPITransformer(BlockStructureTransformer): transform method. """ # collect basic xblock fields - block_structure.request_xblock_fields('graded', 'format', 'display_name', 'category', 'due', 'show_correctness') + block_structure.request_xblock_fields( + 'graded', + 'format', + 'display_name', + 'category', + 'due', + 'show_correctness', + 'icon_class', + ) # collect data from containing transformers StudentViewTransformer.collect(block_structure) diff --git a/lms/djangoapps/course_home_api/outline/serializers.py b/lms/djangoapps/course_home_api/outline/serializers.py index 83b8a9729e..cfa518138a 100644 --- a/lms/djangoapps/course_home_api/outline/serializers.py +++ b/lms/djangoapps/course_home_api/outline/serializers.py @@ -82,7 +82,11 @@ class CourseBlockSerializer(serializers.Serializer): video """ children = block.get('children', []) - child_classes = {child.get('type') for child in children} + child_classes = { + value + for child in children + for value in (child.get('type'), child.get('icon_class')) + } if 'problem' in child_classes: return 'problem' if 'video' in child_classes: diff --git a/lms/djangoapps/course_home_api/outline/tests/test_view.py b/lms/djangoapps/course_home_api/outline/tests/test_view.py index 7c736e0013..74e22e5fcc 100644 --- a/lms/djangoapps/course_home_api/outline/tests/test_view.py +++ b/lms/djangoapps/course_home_api/outline/tests/test_view.py @@ -870,3 +870,33 @@ class SidebarBlocksTestViews(BaseCourseHomeTests): assert vertical_data['complete'] assert sequence_data['completion_stat'] == expected_sequence_completion_stat assert vertical_data['completion_stat'] == expected_vertical_completion_stat + + @ddt.data( + (['html'], 'other'), + (['html', 'video'], 'video'), + (['html', 'video', 'problem'], 'problem'), + ) + @ddt.unpack + def test_vertical_icon(self, block_categories, expected_icon): + """Test that the API checks the children `category` to determine the icon for the unit.""" + self.add_blocks_to_course() + CourseEnrollment.enroll(self.user, self.course.id) + + for category in block_categories: + BlockFactory.create(parent=self.vertical, category=category) + + response = self.client.get(reverse('course-home:course-navigation', args=[self.course.id])) + vertical_data = response.data['blocks'][str(self.vertical.location)] + + assert vertical_data['icon'] == expected_icon + + @patch('xmodule.html_block.HtmlBlock.icon_class', 'video') + def test_vertical_icon_determined_by_icon_class(self): + """Test that the API checks the children `icon_class` to determine the icon for the unit.""" + self.add_blocks_to_course() + CourseEnrollment.enroll(self.user, self.course.id) + + BlockFactory.create(parent=self.vertical, category='html') + response = self.client.get(reverse('course-home:course-navigation', args=[self.course.id])) + vertical_data = response.data['blocks'][str(self.vertical.location)] + assert vertical_data['icon'] == 'video' diff --git a/openedx/features/course_experience/utils.py b/openedx/features/course_experience/utils.py index 769bb8bad0..c20b7f0771 100644 --- a/openedx/features/course_experience/utils.py +++ b/openedx/features/course_experience/utils.py @@ -116,6 +116,7 @@ def get_course_outline_block_tree(request, course_id, user=None, allow_start_dat 'complete', 'resume_block', 'hide_from_toc', + 'icon_class', ], allow_start_dates_in_future=allow_start_dates_in_future, )