Skip to content

Commit 492fa10

Browse files
committed
MOBILE-4266 site: Add site theme class to html tags
1 parent 0de0394 commit 492fa10

File tree

3 files changed

+106
-22
lines changed

3 files changed

+106
-22
lines changed

src/app/app.component.test.ts

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@ import { AppComponent } from '@/app/app.component';
1616
import { CoreEvents } from '@singletons/events';
1717
import { CoreLang, CoreLangProvider } from '@services/lang';
1818

19-
import { mockSingleton, renderComponent } from '@/testing/utils';
19+
import { mock, mockSingleton, renderComponent, wait } from '@/testing/utils';
2020
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
21+
import { CoreSites } from '@services/sites';
22+
import { Http } from '@singletons';
23+
import { of } from 'rxjs';
24+
import { CoreSite } from '@classes/sites/site';
25+
import { CoreUtils } from '@services/utils/utils';
2126

2227
describe('AppComponent', () => {
2328

2429
let langProvider: CoreLangProvider;
25-
let navigator: CoreNavigatorService;
26-
2730
beforeEach(() => {
28-
navigator = mockSingleton(CoreNavigator, ['navigate']);
29-
langProvider = mockSingleton(CoreLang, ['clearCustomStrings']);
31+
langProvider = mockSingleton(CoreLang, mock({ getCurrentLanguage: async () => 'en' , clearCustomStrings: () => null }));
32+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
33+
mockSingleton(Http, { get: () => of(null as any) });
3034
});
3135

3236
it('should render', async () => {
@@ -38,6 +42,7 @@ describe('AppComponent', () => {
3842

3943
it('cleans up on logout', async () => {
4044
const fixture = await renderComponent(AppComponent);
45+
const navigator: CoreNavigatorService = mockSingleton(CoreNavigator, ['navigate']);
4146

4247
fixture.componentInstance.ngOnInit();
4348
CoreEvents.trigger(CoreEvents.LOGOUT);
@@ -46,4 +51,66 @@ describe('AppComponent', () => {
4651
expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true });
4752
});
4853

54+
it('adds ionic platform and theme classes', async () => {
55+
const fixture = await renderComponent(AppComponent);
56+
const siteUrl = 'https://campus.example.edu';
57+
const themeName = 'mytheme';
58+
const themeName2 = 'anothertheme';
59+
60+
fixture.componentInstance.ngOnInit();
61+
62+
expect(document.documentElement.classList.contains('ionic7')).toBe(true);
63+
64+
const site = mock(new CoreSite('42', siteUrl, 'token', { info: {
65+
sitename: 'Example Campus',
66+
username: 'admin',
67+
firstname: 'Admin',
68+
lastname: 'User',
69+
fullname: 'Admin User',
70+
lang: 'en',
71+
userid: 1,
72+
siteurl: siteUrl,
73+
userpictureurl: '',
74+
theme: themeName,
75+
functions: [],
76+
} }));
77+
78+
mockSingleton(CoreSites, {
79+
getSite: () => Promise.resolve(site),
80+
getCurrentSiteId: () => '42',
81+
});
82+
83+
CoreEvents.trigger(CoreEvents.LOGIN, {}, '42');
84+
// Wait the event to be processed.
85+
await wait(50);
86+
87+
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(true);
88+
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
89+
90+
if (site.infos) {
91+
site.infos.theme = themeName2;
92+
}
93+
94+
CoreEvents.trigger(CoreEvents.SITE_UPDATED, site.infos , '42');
95+
96+
// Wait the event to be processed.
97+
await CoreUtils.nextTick();
98+
99+
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
100+
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
101+
102+
CoreEvents.trigger(CoreEvents.LOGOUT);
103+
104+
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
105+
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
106+
107+
CoreEvents.trigger(CoreEvents.SITE_ADDED, site.infos , '42');
108+
109+
// Wait the event to be processed.
110+
await CoreUtils.nextTick();
111+
112+
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
113+
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
114+
});
115+
49116
});

src/app/app.component.ts

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ import { CoreUrl } from '@singletons/url';
3636
import { CoreLogger } from '@singletons/logger';
3737
import { CorePromisedValue } from '@classes/promised-value';
3838
import { register } from 'swiper/element/bundle';
39+
import { CoreSiteInfo, CoreSiteInfoResponse } from '@classes/sites/unauthenticated-site';
3940

4041
const MOODLE_SITE_URL_PREFIX = 'url-';
4142
const MOODLE_VERSION_PREFIX = 'version-';
4243
const MOODLEAPP_VERSION_PREFIX = 'moodleapp-';
44+
const MOODLE_SITE_THEME_PREFIX = 'theme-site-';
4345

4446
register();
4547

@@ -68,7 +70,7 @@ export class AppComponent implements OnInit, AfterViewInit {
6870
CoreLang.clearCustomStrings();
6971

7072
// Remove version classes from body.
71-
this.removeModeClasses([MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX]);
73+
this.removeSiteClasses();
7274

7375
// Go to sites page when user is logged out.
7476
await CoreNavigator.navigate('/login/sites', { reset: true });
@@ -122,11 +124,7 @@ export class AppComponent implements OnInit, AfterViewInit {
122124
const site = await CoreSites.getSite(data.siteId);
123125
const info = site.getInfo();
124126
if (info) {
125-
// Add version classes to body.
126-
this.removeModeClasses([MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX]);
127-
128-
this.addVersionClass(MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(info.release || ''));
129-
this.addSiteUrlClass(info.siteurl);
127+
this.addSiteClasses(info);
130128
}
131129
}
132130

@@ -142,23 +140,15 @@ export class AppComponent implements OnInit, AfterViewInit {
142140
if (data.siteId === CoreSites.getCurrentSiteId()) {
143141
this.loadCustomStrings();
144142

145-
// Add version classes to body.
146-
this.removeModeClasses([MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX]);
147-
148-
this.addVersionClass(MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(data.release || ''));
149-
this.addSiteUrlClass(data.siteurl);
143+
this.addSiteClasses(data);
150144
}
151145
});
152146

153147
CoreEvents.on(CoreEvents.SITE_ADDED, (data) => {
154148
if (data.siteId === CoreSites.getCurrentSiteId()) {
155149
this.loadCustomStrings();
156150

157-
// Add version classes to body.
158-
this.removeModeClasses([MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX]);
159-
160-
this.addVersionClass(MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(data.release || ''));
161-
this.addSiteUrlClass(data.siteurl);
151+
this.addSiteClasses(data);
162152
}
163153
});
164154

@@ -320,6 +310,33 @@ export class AppComponent implements OnInit, AfterViewInit {
320310
}
321311
}
322312

313+
/**
314+
* Convenience function to add site classes to html.
315+
*
316+
* @param siteInfo Site Info.
317+
*/
318+
protected addSiteClasses(siteInfo: CoreSiteInfo | CoreSiteInfoResponse): void {
319+
// Add version classes to html tag.
320+
this.removeSiteClasses();
321+
322+
this.addVersionClass(MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(siteInfo.release || ''));
323+
this.addSiteUrlClass(siteInfo.siteurl);
324+
325+
if (siteInfo.theme) {
326+
CoreDomUtils.toggleModeClass(MOODLE_SITE_THEME_PREFIX + siteInfo.theme, true);
327+
}
328+
}
329+
330+
/**
331+
* Convenience function to remove all site mode classes form html.
332+
*/
333+
protected removeSiteClasses(): void {
334+
// Remove version classes from html tag.
335+
this.removeModeClasses(
336+
[MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX, MOODLE_SITE_THEME_PREFIX],
337+
);
338+
}
339+
323340
/**
324341
* Converts the provided URL into a CSS class that be used within the page.
325342
* This is primarily used to add the siteurl to the body tag as a CSS class.

src/core/features/courses/services/courses.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,7 @@ export type CoreCourseSearchedData = CoreCourseBasicSearchedData & {
14021402
enablecompletion?: number; // Completion enabled? 1: yes 0: no.
14031403
completionnotify?: number; // 1: yes 0: no.
14041404
lang?: string; // Forced course language.
1405-
theme?: string; // Fame of the forced theme.
1405+
theme?: string; // Name of the forced theme.
14061406
marker?: number; // Current course marker.
14071407
legacyfiles?: number; // If legacy files are enabled.
14081408
calendartype?: string; // Calendar type.

0 commit comments

Comments
 (0)