From dd0f495b22a7ab382052662480a1ec565ce6d4f1 Mon Sep 17 00:00:00 2001 From: Afzal Khan Date: Thu, 18 Jul 2024 14:49:20 -0400 Subject: [PATCH] [GSoC'24] M2.2: Update the topic viewer page to show assigned classroom (#20663) * Fix part of #19849: Update topic viewer breadcrumbs * fix staging topic * fix test * update i18n key * add test for classroom * remove redundant i18 test bed --- assets/i18n/en.json | 1 + assets/i18n/qqq.json | 1 + core/controllers/topic_viewer.py | 14 ++- core/controllers/topic_viewer_test.py | 45 +++++-- .../read-only-topic-object.factory.spec.ts | 1 + .../read-only-topic-object.factory.ts | 13 +- .../topic-viewer-backend-api.service.spec.ts | 1 + .../learner-view-info.component.spec.ts | 1 + .../player-header.component.spec.ts | 1 + ...ry-viewer-navbar-breadcrumb.component.html | 6 +- ...viewer-navbar-breadcrumb.component.spec.ts | 1 + .../topic-preview-tab.component.html | 1 + ...ic-viewer-navbar-breadcrumb.component.html | 26 ---- ...viewer-navbar-breadcrumb.component.spec.ts | 115 ------------------ ...opic-viewer-navbar-breadcrumb.component.ts | 77 ------------ .../topic-viewer-stories-list.component.css | 43 +++++++ .../topic-viewer-stories-list.component.html | 25 ++++ ...opic-viewer-stories-list.component.spec.ts | 53 +++++++- .../topic-viewer-stories-list.component.ts | 24 ++++ .../topic-viewer-page.component.html | 1 + .../topic-viewer-page.component.ts | 2 + .../topic-viewer-page.import.ts | 4 - .../topic-viewer-page.mainpage.html | 4 - .../topic-viewer-page.module.ts | 16 +-- 24 files changed, 216 insertions(+), 260 deletions(-) delete mode 100644 core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.html delete mode 100644 core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.spec.ts delete mode 100644 core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.ts diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 5efc5245cafb..a7a186ae0cea 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -244,6 +244,7 @@ "I18N_ATTRIBUTION_PRINT_STEP_TWO": "Attach a copy of the \"<[link]>\"", "I18N_ATTRIBUTION_PRINT_TITLE": "Attribute in Print", "I18N_ATTRIBUTION_TITLE": "How to attribute this lesson for sharing or reusing", + "I18N_BACK_TO_CLASSROOM": "Back to <[classroomName]>", "I18N_BACK_TO_CLASSROOMS": "Back to Classrooms", "I18N_BACK_TO_HOME_TEXT": "Back to Home", "I18N_BLOG_AUTHOR_PROFILE_PAGE_BREADCRUMB": "Author Profile", diff --git a/assets/i18n/qqq.json b/assets/i18n/qqq.json index ba858659837a..08b316ad3721 100644 --- a/assets/i18n/qqq.json +++ b/assets/i18n/qqq.json @@ -244,6 +244,7 @@ "I18N_ATTRIBUTION_PRINT_STEP_TWO": "Step two of attributing Oppia in Print", "I18N_ATTRIBUTION_PRINT_TITLE": "Title of the attribution section that explains how to attribute Oppia in Print", "I18N_ATTRIBUTION_TITLE": "Title of the modal that lists the various ways in which Oppia can be attributed.", + "I18N_BACK_TO_CLASSROOM": "Text which we show in topic viewer page mobile breadcrumbs to redirect to the classroom page.", "I18N_BACK_TO_CLASSROOMS": "Text which we show in classroom page mobile breadcrumbs if we have more than one public classrooms.", "I18N_BACK_TO_HOME_TEXT": "Text displayed in breadcrumbs - shown in the top left corner.", "I18N_BLOG_AUTHOR_PROFILE_PAGE_BREADCRUMB": "Text displayed in the Blog Author Profile Page. - Text shown in the top left corner of the nav bar.", diff --git a/core/controllers/topic_viewer.py b/core/controllers/topic_viewer.py index e2085cba036a..e27712abaecd 100644 --- a/core/controllers/topic_viewer.py +++ b/core/controllers/topic_viewer.py @@ -23,6 +23,7 @@ from core.constants import constants from core.controllers import acl_decorators from core.controllers import base +from core.domain import classroom_config_services from core.domain import email_manager from core.domain import platform_parameter_list from core.domain import platform_parameter_services @@ -168,6 +169,10 @@ def get(self, topic_name: str) -> None: for skill_id in all_skill_ids: degrees_of_mastery[skill_id] = None + classroom_name = ( + classroom_config_services.get_classroom_name_for_topic_id( + topic.id)) + self.values.update({ 'topic_id': topic.id, 'topic_name': topic.name, @@ -180,6 +185,13 @@ def get(self, topic_name: str) -> None: 'skill_descriptions': skill_descriptions, 'practice_tab_is_displayed': topic.practice_tab_is_displayed, 'meta_tag_content': topic.meta_tag_content, - 'page_title_fragment_for_web': topic.page_title_fragment_for_web + 'page_title_fragment_for_web': topic.page_title_fragment_for_web, + 'classroom_name': ( + None if ( + classroom_name + == + str(constants.CLASSROOM_NAME_FOR_UNATTACHED_TOPICS) + ) else classroom_name + ) }) self.render_json(self.values) diff --git a/core/controllers/topic_viewer_test.py b/core/controllers/topic_viewer_test.py index 079891f6252b..d0ebb87e25f9 100644 --- a/core/controllers/topic_viewer_test.py +++ b/core/controllers/topic_viewer_test.py @@ -18,6 +18,7 @@ from core import feconf from core.constants import constants +from core.domain import classroom_config_services from core.domain import platform_parameter_list from core.domain import question_services from core.domain import skill_services @@ -99,6 +100,8 @@ def setUp(self) -> None: skill_services.create_user_skill_mastery( self.user_id, self.skill_id_2, 0.5) + self.math_classroom = self.save_new_valid_classroom() + class TopicViewerPageTests(BaseTopicViewerControllerTests): @@ -127,8 +130,13 @@ class TopicPageDataHandlerTests( BaseTopicViewerControllerTests, test_utils.EmailTestBase): def test_get_with_no_user_logged_in(self) -> None: + self.math_classroom.topic_id_to_prerequisite_topic_ids = { + self.topic_id: [] + } + classroom_config_services.update_classroom(self.math_classroom) + json_response = self.get_json( - '%s/staging/%s' % (feconf.TOPIC_DATA_HANDLER, 'public')) + '%s/math/%s' % (feconf.TOPIC_DATA_HANDLER, 'public')) expected_dict = { 'topic_name': 'public_topic_name', 'topic_id': self.topic_id, @@ -173,8 +181,20 @@ def test_get_with_no_user_logged_in(self) -> None: self.skill_id_1: 'Skill Description 1', self.skill_id_2: 'Skill Description 2' }, - 'practice_tab_is_displayed': False + 'practice_tab_is_displayed': False, + 'classroom_name': 'math' } + + self.assertDictContainsSubset(expected_dict, json_response) + + # Test with no classroom assigned. + self.math_classroom.topic_id_to_prerequisite_topic_ids = {} + expected_dict['classroom_name'] = None + classroom_config_services.update_classroom(self.math_classroom) + + json_response = self.get_json( + '%s/staging/%s' % (feconf.TOPIC_DATA_HANDLER, 'public')) + self.assertDictContainsSubset(expected_dict, json_response) @test_utils.set_platform_parameters( @@ -239,7 +259,8 @@ def test_get_with_user_logged_in(self) -> None: 'skill_descriptions': { self.skill_id_2: 'Skill Description 2' }, - 'practice_tab_is_displayed': False + 'practice_tab_is_displayed': False, + 'classroom_name': None } self.assertDictContainsSubset(expected_dict, json_response) @@ -290,7 +311,8 @@ def test_get_with_no_skills_ids(self) -> None: 'subtopics': [], 'degrees_of_mastery': {}, 'skill_descriptions': {}, - 'practice_tab_is_displayed': False + 'practice_tab_is_displayed': False, + 'classroom_name': None } self.assertDictContainsSubset(expected_dict, json_response) @@ -346,7 +368,8 @@ def test_get_with_five_or_more_questions(self) -> None: 'skill_descriptions': { self.skill_id_1: 'Skill Description 1' }, - 'practice_tab_is_displayed': True + 'practice_tab_is_displayed': True, + 'classroom_name': None } self.assertDictContainsSubset(expected_dict, json_response) self.logout() @@ -403,7 +426,8 @@ def test_get_with_twenty_or_more_questions(self) -> None: 'skill_descriptions': { self.skill_id_1: 'Skill Description 1', }, - 'practice_tab_is_displayed': True + 'practice_tab_is_displayed': True, + 'classroom_name': None } self.assertDictContainsSubset(expected_dict, json_response) self.logout() @@ -459,7 +483,8 @@ def test_get_with_twenty_or_more_questions_with_multiple_skills( 'topic_id': self.topic_id, 'canonical_story_dicts': [], 'additional_story_dicts': [], - 'practice_tab_is_displayed': True + 'practice_tab_is_displayed': True, + 'classroom_name': None } self.assertDictContainsSubset(expected_dict, json_response) self.logout() @@ -516,7 +541,8 @@ def test_get_with_lesser_questions_with_fifty_or_more_skills( 'topic_id': self.topic_id, 'canonical_story_dicts': [], 'additional_story_dicts': [], - 'practice_tab_is_displayed': False + 'practice_tab_is_displayed': False, + 'classroom_name': None } self.assertDictContainsSubset(expected_dict, json_response) self.logout() @@ -573,7 +599,8 @@ def test_get_with_more_questions_with_fifty_or_more_skills(self) -> None: 'topic_id': self.topic_id, 'canonical_story_dicts': [], 'additional_story_dicts': [], - 'practice_tab_is_displayed': True + 'practice_tab_is_displayed': True, + 'classroom_name': None } self.assertDictContainsSubset(expected_dict, json_response) self.logout() diff --git a/core/templates/domain/topic_viewer/read-only-topic-object.factory.spec.ts b/core/templates/domain/topic_viewer/read-only-topic-object.factory.spec.ts index 155d906e51ee..1654f669bad4 100644 --- a/core/templates/domain/topic_viewer/read-only-topic-object.factory.spec.ts +++ b/core/templates/domain/topic_viewer/read-only-topic-object.factory.spec.ts @@ -102,6 +102,7 @@ describe('Read only topic object Factory', () => { practice_tab_is_displayed: false, meta_tag_content: 'Topic meta tag content', page_title_fragment_for_web: 'topic page title', + classroom_name: 'math', }; _sampleReadOnlyTopic = diff --git a/core/templates/domain/topic_viewer/read-only-topic-object.factory.ts b/core/templates/domain/topic_viewer/read-only-topic-object.factory.ts index 2e3bdfd7409d..81b3b8ba8d49 100644 --- a/core/templates/domain/topic_viewer/read-only-topic-object.factory.ts +++ b/core/templates/domain/topic_viewer/read-only-topic-object.factory.ts @@ -49,6 +49,7 @@ export interface ReadOnlyTopicBackendDict { practice_tab_is_displayed: boolean; meta_tag_content: string; page_title_fragment_for_web: string; + classroom_name: string | null; } export class ReadOnlyTopic { @@ -64,6 +65,7 @@ export class ReadOnlyTopic { _practiceTabIsDisplayed: boolean; _metaTagContent: string; _pageTitleFragmentForWeb: string; + _classroomName: string | null; constructor( topicName: string, @@ -77,7 +79,8 @@ export class ReadOnlyTopic { skillDescriptions: SkillIdToDescriptionMap, practiceTabIsDisplayed: boolean, metaTagContent: string, - pageTitleFragmentForWeb: string + pageTitleFragmentForWeb: string, + classroomName: string | null ) { this._topicName = topicName; this._topicId = topicId; @@ -91,6 +94,7 @@ export class ReadOnlyTopic { this._practiceTabIsDisplayed = practiceTabIsDisplayed; this._metaTagContent = metaTagContent; this._pageTitleFragmentForWeb = pageTitleFragmentForWeb; + this._classroomName = classroomName; } getTopicName(): string { @@ -140,6 +144,10 @@ export class ReadOnlyTopic { getPageTitleFragmentForWeb(): string { return this._pageTitleFragmentForWeb; } + + getClassroomName(): string | null { + return this._classroomName; + } } @Injectable({ @@ -233,7 +241,8 @@ export class ReadOnlyTopicObjectFactory { skillDescriptions, topicDataDict.practice_tab_is_displayed, topicDataDict.meta_tag_content, - topicDataDict.page_title_fragment_for_web + topicDataDict.page_title_fragment_for_web, + topicDataDict.classroom_name ); } } diff --git a/core/templates/domain/topic_viewer/topic-viewer-backend-api.service.spec.ts b/core/templates/domain/topic_viewer/topic-viewer-backend-api.service.spec.ts index 02d4f8d63e97..be609c0e7bed 100644 --- a/core/templates/domain/topic_viewer/topic-viewer-backend-api.service.spec.ts +++ b/core/templates/domain/topic_viewer/topic-viewer-backend-api.service.spec.ts @@ -120,6 +120,7 @@ describe('Topic viewer backend API service', () => { practice_tab_is_displayed: false, meta_tag_content: 'Topic meta tag content', page_title_fragment_for_web: 'topic page title', + classroom_name: 'math', }; sampleDataResultsObjects = diff --git a/core/templates/pages/exploration-player-page/layout-directives/learner-view-info.component.spec.ts b/core/templates/pages/exploration-player-page/layout-directives/learner-view-info.component.spec.ts index 82f3baca0cc9..50cabcc3cffd 100644 --- a/core/templates/pages/exploration-player-page/layout-directives/learner-view-info.component.spec.ts +++ b/core/templates/pages/exploration-player-page/layout-directives/learner-view-info.component.spec.ts @@ -103,6 +103,7 @@ describe('Learner view info component', () => { practice_tab_is_displayed: false, meta_tag_content: 'content', page_title_fragment_for_web: 'title', + classroom_name: 'math', } as ReadOnlyTopicBackendDict) ); diff --git a/core/templates/pages/exploration-player-page/new-lesson-player/new-lesson-player-components/player-header.component.spec.ts b/core/templates/pages/exploration-player-page/new-lesson-player/new-lesson-player-components/player-header.component.spec.ts index d0f5a2b9868b..3a62ea7a07c3 100644 --- a/core/templates/pages/exploration-player-page/new-lesson-player/new-lesson-player-components/player-header.component.spec.ts +++ b/core/templates/pages/exploration-player-page/new-lesson-player/new-lesson-player-components/player-header.component.spec.ts @@ -107,6 +107,7 @@ describe('Lesson player header component', () => { practice_tab_is_displayed: false, meta_tag_content: 'content', page_title_fragment_for_web: 'title', + classroom_name: 'math', } as ReadOnlyTopicBackendDict) ); diff --git a/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.html b/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.html index feae91e6aaad..5138f7ba508a 100644 --- a/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.html +++ b/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.html @@ -40,13 +40,13 @@ } .oppia-story-navbar-topic { - color: #fff; + color: white; font-family: 'Roboto', Arial, sans-serif; font-size: 12px; } - .oppia-story-navbar-topic:hover { - color: #fff; + .oppia-story-navbar-topic span { + color: white; } .oppia-story-viewer-nav-separator { diff --git a/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.spec.ts b/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.spec.ts index 095996a204bd..58fef8cf7257 100644 --- a/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.spec.ts +++ b/core/templates/pages/story-viewer-page/navbar-breadcrumb/story-viewer-navbar-breadcrumb.component.spec.ts @@ -105,6 +105,7 @@ describe('Subtopic viewer navbar breadcrumb component', () => { practice_tab_is_displayed: false, meta_tag_content: 'content', page_title_fragment_for_web: 'title', + classroom_name: 'math', } as ReadOnlyTopicBackendDict) ); }); diff --git a/core/templates/pages/topic-editor-page/preview-tab/topic-preview-tab.component.html b/core/templates/pages/topic-editor-page/preview-tab/topic-preview-tab.component.html index 23b484dcc58c..4a00a34548ee 100644 --- a/core/templates/pages/topic-editor-page/preview-tab/topic-preview-tab.component.html +++ b/core/templates/pages/topic-editor-page/preview-tab/topic-preview-tab.component.html @@ -44,6 +44,7 @@ [topicDescription]="topic.getDescription()" [canonicalStorySummaries]="canonicalStorySummaries" [classroomUrlFragment]="''" + [classroomName]="''" [topicUrlFragment]="''"> diff --git a/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.html b/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.html deleted file mode 100644 index d7b504fc12a9..000000000000 --- a/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - diff --git a/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.spec.ts b/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.spec.ts deleted file mode 100644 index 6bc2aef0e61c..000000000000 --- a/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.spec.ts +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2020 The Oppia Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview Unit tests for classroom page component. - */ - -import {TestBed, ComponentFixture, waitForAsync} from '@angular/core/testing'; -import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {UrlService} from 'services/contextual/url.service'; -import {TopicViewerBackendApiService} from 'domain/topic_viewer/topic-viewer-backend-api.service'; -import { - ReadOnlyTopicBackendDict, - ReadOnlyTopicObjectFactory, -} from 'domain/topic_viewer/read-only-topic-object.factory'; -import {TopicViewerNavbarBreadcrumbComponent} from 'pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component'; -import {I18nLanguageCodeService} from 'services/i18n-language-code.service'; -import {MockTranslatePipe} from 'tests/unit-test-utils'; - -describe('Topic viewer navbar breadcrumb component', () => { - let component: TopicViewerNavbarBreadcrumbComponent; - let fixture: ComponentFixture; - let readOnlyTopicObjectFactory = null; - let topicViewerBackendApiService = null; - let i18nLanguageCodeService: I18nLanguageCodeService; - let urlService = null; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [MockTranslatePipe, TopicViewerNavbarBreadcrumbComponent], - }).compileComponents(); - - readOnlyTopicObjectFactory = TestBed.inject(ReadOnlyTopicObjectFactory); - topicViewerBackendApiService = TestBed.inject(TopicViewerBackendApiService); - i18nLanguageCodeService = TestBed.inject(I18nLanguageCodeService); - urlService = TestBed.inject(UrlService); - - spyOn(urlService, 'getTopicUrlFragmentFromLearnerUrl').and.returnValue( - 'topic1' - ); - spyOn(urlService, 'getClassroomUrlFragmentFromLearnerUrl').and.returnValue( - 'classroom1' - ); - - spyOn(topicViewerBackendApiService, 'fetchTopicDataAsync').and.resolveTo( - readOnlyTopicObjectFactory.createFromBackendDict({ - subtopics: [], - skill_descriptions: {}, - uncategorized_skill_ids: [], - degrees_of_mastery: {}, - canonical_story_dicts: [], - additional_story_dicts: [], - topic_name: 'Topic Name 1', - topic_id: 'topic1', - topic_description: 'Description', - practice_tab_is_displayed: false, - meta_tag_content: 'content', - page_title_fragment_for_web: 'title', - } as ReadOnlyTopicBackendDict) - ); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(TopicViewerNavbarBreadcrumbComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should set topic name using the data retrieved from the backend', waitForAsync(() => { - component.ngOnInit(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(component.topicName).toBe('Topic Name 1'); - }); - })); - - it( - 'should set topic name translation key and check whether hacky ' + - 'translations are displayed or not correctly', - waitForAsync(() => { - component.ngOnInit(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(component.topicNameTranslationKey).toBe( - 'I18N_TOPIC_topic1_TITLE' - ); - - spyOn( - i18nLanguageCodeService, - 'isHackyTranslationAvailable' - ).and.returnValue(true); - spyOn( - i18nLanguageCodeService, - 'isCurrentLanguageEnglish' - ).and.returnValue(false); - - let hackyTopicNameTranslationIsDisplayed = - component.isHackyTopicNameTranslationDisplayed(); - expect(hackyTopicNameTranslationIsDisplayed).toBe(true); - }); - }) - ); -}); diff --git a/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.ts b/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.ts deleted file mode 100644 index ec0db46d3ff7..000000000000 --- a/core/templates/pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component.ts +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018 The Oppia Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview Component for the navbar breadcrumb of the topic viewer. - */ - -import {Component, OnInit} from '@angular/core'; -import {downgradeComponent} from '@angular/upgrade/static'; - -import {ReadOnlyTopic} from 'domain/topic_viewer/read-only-topic-object.factory'; -import {TopicViewerBackendApiService} from 'domain/topic_viewer/topic-viewer-backend-api.service'; -import {UrlService} from 'services/contextual/url.service'; -import { - I18nLanguageCodeService, - TranslationKeyType, -} from 'services/i18n-language-code.service'; - -@Component({ - selector: 'topic-viewer-navbar-breadcrumb', - templateUrl: './topic-viewer-navbar-breadcrumb.component.html', - styleUrls: [], -}) -export class TopicViewerNavbarBreadcrumbComponent implements OnInit { - topicName: string = ''; - topicNameTranslationKey: string = ''; - constructor( - private topicViewerBackendApiService: TopicViewerBackendApiService, - private i18nLanguageCodeService: I18nLanguageCodeService, - private urlService: UrlService - ) {} - - ngOnInit(): void { - this.topicViewerBackendApiService - .fetchTopicDataAsync( - this.urlService.getTopicUrlFragmentFromLearnerUrl(), - this.urlService.getClassroomUrlFragmentFromLearnerUrl() - ) - .then((readOnlyTopic: ReadOnlyTopic) => { - this.topicName = readOnlyTopic.getTopicName(); - this.topicNameTranslationKey = - this.i18nLanguageCodeService.getTopicTranslationKey( - readOnlyTopic.getTopicId(), - TranslationKeyType.TITLE - ); - }); - } - - isHackyTopicNameTranslationDisplayed(): boolean { - return ( - this.i18nLanguageCodeService.isHackyTranslationAvailable( - this.topicNameTranslationKey - ) && !this.i18nLanguageCodeService.isCurrentLanguageEnglish() - ); - } - - isLanguageRTL(): boolean { - return this.i18nLanguageCodeService.isCurrentLanguageRTL(); - } -} -angular - .module('oppia') - .directive( - 'topicViewerNavbarBreadcrumb', - downgradeComponent({component: TopicViewerNavbarBreadcrumbComponent}) - ); diff --git a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.css b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.css index b27ff987058f..0869e38cd266 100644 --- a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.css +++ b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.css @@ -37,6 +37,39 @@ stories-list .mat-card-margin-desktop { margin-top: 10em; } +stories-list .oppia-topic-viewer-breadcrumbs .desktop-breadcrumbs { + align-items: center; + display: flex; + gap: 4px; +} + +stories-list .oppia-topic-viewer-breadcrumbs .desktop-breadcrumbs .classroom-name-container { + align-items: center; + display: flex; + gap: 4px; +} + +stories-list .oppia-topic-viewer-breadcrumbs .mobile-breadcrumbs { + display: none; +} + +stories-list .oppia-topic-viewer-breadcrumbs .mobile-breadcrumbs a, +stories-list .oppia-topic-viewer-breadcrumbs .desktop-breadcrumbs a, +stories-list .oppia-topic-viewer-breadcrumbs .mobile-breadcrumbs span, +stories-list .oppia-topic-viewer-breadcrumbs .desktop-breadcrumbs span { + color: #00645c; + font-family: 'Capriola', 'Roboto', Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 15px; +} + +stories-list .oppia-topic-viewer-breadcrumbs .mobile-breadcrumbs i, +stories-list .oppia-topic-viewer-breadcrumbs .desktop-breadcrumbs i { + font-size: 13px; +} + @media(max-width: 1024px) { stories-list .oppia-stories-list-container .oppia-page-card { width: 80vw; @@ -51,6 +84,16 @@ stories-list .mat-card-margin-desktop { stories-list .story-info-container { padding-top: 0; } + + stories-list .oppia-topic-viewer-breadcrumbs .desktop-breadcrumbs { + display: none; + } + + stories-list .oppia-topic-viewer-breadcrumbs .mobile-breadcrumbs { + align-items: center; + display: flex; + gap: 4px; + } } @media(max-width: 500px) { diff --git a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.html b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.html index 693fecd9cb9b..f7dc3794a283 100644 --- a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.html +++ b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.html @@ -1,6 +1,31 @@
+
+
+ {{ 'I18N_TOPNAV_HOME' | translate }} + + + + + {{ topicName }} + + + {{ topicNameTranslationKey | translate }} + +
+ +

{{ topicName }} diff --git a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.spec.ts b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.spec.ts index eec940265329..5f177f665cbc 100644 --- a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.spec.ts +++ b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.spec.ts @@ -46,8 +46,8 @@ describe('Topic Viewer Stories List Component', () => { component.topicName = 'Topic Name'; component.topicDescription = 'Topic Description'; component.topicId = 'topicId'; + component.classroomName = 'math'; windowDimensionsService = TestBed.inject(WindowDimensionsService); - i18nLanguageCodeService = TestBed.inject(I18nLanguageCodeService); spyOn(i18nLanguageCodeService, 'isCurrentLanguageRTL').and.returnValue( true @@ -59,6 +59,16 @@ describe('Topic Viewer Stories List Component', () => { 'I18N_TOPIC_123abcd_TITLE', 'I18N_TOPIC_123abcd_DESCRIPTION' ); + spyOn( + i18nLanguageCodeService, + 'getClassroomTranslationKeys' + ).and.returnValue({ + name: 'I18N_CLASSROOM_MATH_NAME', + courseDetails: 'I18N_CLASSROOM_MATH_COURSE_DETAILS', + teaserText: 'I18N_CLASSROOM_MATH_TEASER_TEXT', + topicListIntro: 'I18N_CLASSROOM_MATH_TOPICS_LIST_INTRO', + }); + expect(component).toBeDefined(); component.ngOnInit(); @@ -67,26 +77,52 @@ describe('Topic Viewer Stories List Component', () => { expect(component.topicDescTranslationKey).toBe( 'I18N_TOPIC_123abcd_DESCRIPTION' ); + expect(component.classroomNameTranslationKey).toBe( + 'I18N_CLASSROOM_MATH_NAME' + ); }); - it('should check if topic name, desc translation is displayed correctly', () => { + it('should check if topic name, desc translation and classroom name is displayed correctly', () => { spyOn(i18nLanguageCodeService, 'getTopicTranslationKey').and.returnValues( 'I18N_TOPIC_123abcd_TITLE', 'I18N_TOPIC_123abcd_DESCRIPTION' ); + spyOn( + i18nLanguageCodeService, + 'getClassroomTranslationKeys' + ).and.returnValue({ + name: 'I18N_CLASSROOM_MATH_NAME', + courseDetails: 'I18N_CLASSROOM_MATH_COURSE_DETAILS', + teaserText: 'I18N_CLASSROOM_MATH_TEASER_TEXT', + topicListIntro: 'I18N_CLASSROOM_MATH_TOPICS_LIST_INTRO', + }); spyOn( i18nLanguageCodeService, 'isHackyTranslationAvailable' - ).and.returnValues(true, true); + ).and.returnValues(true, true, true); spyOn(i18nLanguageCodeService, 'isCurrentLanguageEnglish').and.returnValues( + false, false, false ); component.ngOnInit(); - expect(component.isHackyTopicNameTranslationDisplayed()).toBe(true); - expect(component.isHackyTopicDescTranslationDisplayed()).toBe(true); + expect(component.isHackyTopicNameTranslationDisplayed()).toBeTrue(); + expect(component.isHackyTopicDescTranslationDisplayed()).toBeTrue(); + expect(component.isHackyClassroomNameTranslationDisplayed()).toBeTrue(); + }); + + it('should not return the classroom name i18n key if the topic is not assigned to any classroom', () => { + component.classroomName = null; + spyOn(i18nLanguageCodeService, 'getClassroomTranslationKeys'); + + component.ngOnInit(); + + expect( + i18nLanguageCodeService.getClassroomTranslationKeys + ).not.toHaveBeenCalled(); + expect(component.isHackyClassroomNameTranslationDisplayed()).toBeFalse(); }); it('should check if the view is tablet or not', () => { @@ -97,4 +133,11 @@ describe('Topic Viewer Stories List Component', () => { widthSpy.and.returnValue(800); expect(component.checkTabletView()).toBe(false); }); + + it('should get RTL language status correctly', () => { + spyOn(i18nLanguageCodeService, 'isCurrentLanguageEnglish').and.returnValue( + true + ); + expect(component.isLanguageRTL()).toBeTrue(); + }); }); diff --git a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.ts b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.ts index a1d6e93792a4..cd20c7a887b1 100644 --- a/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.ts +++ b/core/templates/pages/topic-viewer-page/stories-list/topic-viewer-stories-list.component.ts @@ -39,12 +39,14 @@ export class StoriesListComponent implements OnInit { // https://github.com/oppia/oppia/wiki/Guide-on-defining-types#ts-7-1 @Input() canonicalStorySummaries!: StorySummary[]; @Input() classroomUrlFragment!: string; + @Input() classroomName!: string | null; @Input() topicUrlFragment!: string; @Input() topicName!: string; @Input() topicDescription!: string; @Input() topicId!: string; topicNameTranslationKey!: string; topicDescTranslationKey!: string; + classroomNameTranslationKey!: string; constructor( private i18nLanguageCodeService: I18nLanguageCodeService, @@ -62,6 +64,13 @@ export class StoriesListComponent implements OnInit { this.topicId, TranslationKeyType.DESCRIPTION ); + + if (this.classroomName) { + this.classroomNameTranslationKey = + this.i18nLanguageCodeService.getClassroomTranslationKeys( + this.classroomName + ).name; + } } isHackyTopicNameTranslationDisplayed(): boolean { @@ -80,9 +89,24 @@ export class StoriesListComponent implements OnInit { ); } + isHackyClassroomNameTranslationDisplayed(): boolean { + if (!this.classroomName) { + return false; + } + return ( + this.i18nLanguageCodeService.isHackyTranslationAvailable( + this.classroomNameTranslationKey + ) && !this.i18nLanguageCodeService.isCurrentLanguageEnglish() + ); + } + checkTabletView(): boolean { return this.windowDimensionsService.getWidth() < 768; } + + isLanguageRTL(): boolean { + return this.i18nLanguageCodeService.isCurrentLanguageRTL(); + } } angular .module('oppia') diff --git a/core/templates/pages/topic-viewer-page/topic-viewer-page.component.html b/core/templates/pages/topic-viewer-page/topic-viewer-page.component.html index 8c6477e97e55..0063401cefdd 100644 --- a/core/templates/pages/topic-viewer-page/topic-viewer-page.component.html +++ b/core/templates/pages/topic-viewer-page/topic-viewer-page.component.html @@ -38,6 +38,7 @@ [topicDescription]="topicDescription" [canonicalStorySummaries]="canonicalStorySummaries" [classroomUrlFragment]="classroomUrlFragment" + [classroomName]="classroomName" [topicUrlFragment]="topicUrlFragment"> diff --git a/core/templates/pages/topic-viewer-page/topic-viewer-page.component.ts b/core/templates/pages/topic-viewer-page/topic-viewer-page.component.ts index 79c4990a428b..c598f473a8f3 100644 --- a/core/templates/pages/topic-viewer-page/topic-viewer-page.component.ts +++ b/core/templates/pages/topic-viewer-page/topic-viewer-page.component.ts @@ -49,6 +49,7 @@ export class TopicViewerPageComponent implements OnInit, OnDestroy { canonicalStorySummaries: StorySummary[] = []; topicUrlFragment: string = ''; classroomUrlFragment: string = ''; + classroomName: string | null = ''; topicIsLoading: boolean = true; topicId: string = ''; topicName: string = ''; @@ -97,6 +98,7 @@ export class TopicViewerPageComponent implements OnInit, OnDestroy { this.topicName = readOnlyTopic.getTopicName(); this.topicDescription = readOnlyTopic.getTopicDescription(); this.pageTitleFragment = readOnlyTopic.getPageTitleFragmentForWeb(); + this.classroomName = readOnlyTopic.getClassroomName(); // The onLangChange event is initially fired before the topic is // loaded. Hence the first setpageTitle() call needs to made diff --git a/core/templates/pages/topic-viewer-page/topic-viewer-page.import.ts b/core/templates/pages/topic-viewer-page/topic-viewer-page.import.ts index 179b4338e620..489b6c3786a7 100644 --- a/core/templates/pages/topic-viewer-page/topic-viewer-page.import.ts +++ b/core/templates/pages/topic-viewer-page/topic-viewer-page.import.ts @@ -41,8 +41,4 @@ require('App.ts'); require('base-components/base-content.component.ts'); require('base-components/oppia-root.directive.ts'); -require( - 'pages/topic-viewer-page/navbar-breadcrumb/' + - 'topic-viewer-navbar-breadcrumb.component.ts' -); require('pages/topic-viewer-page/topic-viewer-page.component.ts'); diff --git a/core/templates/pages/topic-viewer-page/topic-viewer-page.mainpage.html b/core/templates/pages/topic-viewer-page/topic-viewer-page.mainpage.html index 30cd985603d0..5c00efb39ace 100644 --- a/core/templates/pages/topic-viewer-page/topic-viewer-page.mainpage.html +++ b/core/templates/pages/topic-viewer-page/topic-viewer-page.mainpage.html @@ -14,10 +14,6 @@
- - - - diff --git a/core/templates/pages/topic-viewer-page/topic-viewer-page.module.ts b/core/templates/pages/topic-viewer-page/topic-viewer-page.module.ts index 9b5333f8f6ea..0f93c7bb0985 100644 --- a/core/templates/pages/topic-viewer-page/topic-viewer-page.module.ts +++ b/core/templates/pages/topic-viewer-page/topic-viewer-page.module.ts @@ -30,10 +30,6 @@ import {APP_BASE_HREF} from '@angular/common'; import {OppiaAngularRootComponent} from 'components/oppia-angular-root.component'; import {SharedComponentsModule} from 'components/shared-component.module'; -import { - TopicViewerNavbarBreadcrumbComponent, - // eslint-disable-next-line max-len -} from 'pages/topic-viewer-page/navbar-breadcrumb/topic-viewer-navbar-breadcrumb.component'; import {RequestInterceptor} from 'services/request-interceptor.service'; import {TopicViewerPageComponent} from 'pages/topic-viewer-page/topic-viewer-page.component'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; @@ -55,16 +51,8 @@ import {AppErrorHandlerProvider} from 'pages/oppia-root/app-error-handler'; TopicPlayerViewerCommonModule, ToastrModule.forRoot(toastrConfig), ], - declarations: [ - TopicViewerNavbarBreadcrumbComponent, - TopicViewerPageComponent, - PracticeSessionConfirmationModal, - ], - entryComponents: [ - TopicViewerNavbarBreadcrumbComponent, - TopicViewerPageComponent, - PracticeSessionConfirmationModal, - ], + declarations: [TopicViewerPageComponent, PracticeSessionConfirmationModal], + entryComponents: [TopicViewerPageComponent, PracticeSessionConfirmationModal], providers: [ { provide: HTTP_INTERCEPTORS,