Skip to content

Commit 419ff2b

Browse files
committed
feat: TT-332 dark mode implementation
1 parent ff1ded3 commit 419ff2b

File tree

10 files changed

+170
-4
lines changed

10 files changed

+170
-4
lines changed

src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ import { TechnologyReportComponent } from './modules/technology-report/pages/tec
8686
import { CalendarComponent } from './modules/time-entries/components/calendar/calendar.component';
8787
import { DropdownComponent } from './modules/shared/components/dropdown/dropdown.component';
8888
import { NgSelectModule } from '@ng-select/ng-select';
89+
import { DarkModeComponent } from './modules/shared/components/dark-mode/dark-mode.component';
8990

9091
const maskConfig: Partial<IConfig> = {
9192
validation: false,
@@ -140,6 +141,7 @@ const maskConfig: Partial<IConfig> = {
140141
TechnologyReportTableComponent,
141142
CalendarComponent,
142143
DropdownComponent,
144+
DarkModeComponent,
143145
],
144146
imports: [
145147
NgxMaskModule.forRoot(maskConfig),
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<button
2+
class="w-10 h-6 rounded-full bg-white flex items-center transition duration-300 focus:outline-none shadow"
3+
(click)="changeToDarkOrLightTheme()"
4+
>
5+
<div
6+
#themeToggle
7+
class="w-7 h-7 relative rounded-full transition duration-500 transform bg-yellow-300 -translate-x-1 p-1"
8+
>
9+
<ng-container *ngIf="isDarkTheme(); else elseToggle">
10+
<img src="assets/icons/moon.svg" alt="moon icon" />
11+
</ng-container>
12+
<ng-template #elseToggle>
13+
<img src="assets/icons/sun.svg" alt="sun icon" />
14+
</ng-template>
15+
</div>
16+
</button>

src/app/modules/shared/components/dark-mode/dark-mode.component.scss

Whitespace-only changes.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { DarkModeComponent } from './dark-mode.component';
4+
5+
describe('DarkModeComponent', () => {
6+
let component: DarkModeComponent;
7+
let fixture: ComponentFixture<DarkModeComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [DarkModeComponent],
12+
}).compileComponents();
13+
});
14+
15+
beforeEach(() => {
16+
fixture = TestBed.createComponent(DarkModeComponent);
17+
component = fixture.componentInstance;
18+
fixture.detectChanges();
19+
});
20+
21+
afterEach(() => {
22+
localStorage.removeItem('theme');
23+
});
24+
25+
it('should be created', () => {
26+
expect(component).toBeTruthy();
27+
});
28+
29+
it('should be clear the default theme', () => {
30+
expect(component.theme).toBe('light');
31+
});
32+
33+
it('should change the value of the theme property if it exists in the local storage', () => {
34+
localStorage.setItem('theme', 'dark');
35+
component.checkThemeInLocalStorage();
36+
expect(component.theme).toBe('dark');
37+
});
38+
39+
it('should be light theme if it does not exist in local storage', () => {
40+
component.checkThemeInLocalStorage();
41+
expect(component.theme).toBe('light');
42+
});
43+
44+
it('should be changed to dark mode if the mode toggle is selected', () => {
45+
component.themeToggle.nativeElement.click();
46+
fixture.detectChanges();
47+
expect(component.theme).toBe('dark');
48+
expect(localStorage.getItem('theme')).toBe('dark');
49+
});
50+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { AfterViewInit, ElementRef, ViewChild, Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-dark-mode',
5+
templateUrl: './dark-mode.component.html',
6+
styleUrls: ['./dark-mode.component.scss'],
7+
})
8+
export class DarkModeComponent implements AfterViewInit {
9+
public theme = 'light';
10+
11+
@ViewChild('themeToggle') themeToggle: ElementRef;
12+
13+
constructor() {
14+
this.checkThemeInLocalStorage();
15+
this.addOrRemoveDarkMode();
16+
}
17+
18+
ngAfterViewInit(): void {
19+
this.switchThemeToggleStyles(this.theme);
20+
}
21+
22+
getLocalStorageTheme(): string {
23+
return localStorage.getItem('theme') || 'light';
24+
}
25+
26+
setLocalStorageTheme(theme: string): void {
27+
localStorage.setItem('theme', theme);
28+
}
29+
30+
checkThemeInLocalStorage(): void {
31+
if ('theme' in localStorage) {
32+
this.theme = this.getLocalStorageTheme();
33+
} else {
34+
this.setLocalStorageTheme(this.theme);
35+
}
36+
}
37+
38+
isDarkTheme(): boolean {
39+
return this.theme === 'dark' ? true : false;
40+
}
41+
42+
addOrRemoveDarkMode(): void {
43+
if (this.isDarkTheme()) {
44+
document.documentElement.classList.add('dark');
45+
} else {
46+
document.documentElement.classList.remove('dark');
47+
}
48+
}
49+
50+
changeToDarkOrLightTheme(): void {
51+
if (this.isDarkTheme()) {
52+
this.theme = 'light';
53+
} else {
54+
this.theme = 'dark';
55+
}
56+
this.setLocalStorageTheme(this.theme);
57+
this.switchThemeToggleStyles(this.theme);
58+
this.addOrRemoveDarkMode();
59+
}
60+
61+
switchThemeToggleStyles(theme: string): void {
62+
switch (theme) {
63+
case 'dark':
64+
this.themeToggle.nativeElement.classList.remove('bg-yellow-300', '-translate-x-1');
65+
this.themeToggle.nativeElement.classList.add('bg-gray-300', 'translate-x-1/2');
66+
break;
67+
case 'light':
68+
this.themeToggle.nativeElement.classList.remove('bg-gray-300', 'translate-x-1/2');
69+
this.themeToggle.nativeElement.classList.add('bg-yellow-300', '-translate-x-1');
70+
break;
71+
}
72+
}
73+
}

src/app/modules/shared/components/sidebar/sidebar.component.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
<div class="d-flex" id="wrapper">
2-
<div class="bg-light border-right" id="sidebar-wrapper">
1+
<div class="d-flex dark:bg-gray" id="wrapper">
2+
<div class="border-right dark:bg-grey-dark" id="sidebar-wrapper">
33
<div class="sidebar-heading" style="text-align: center">
44
<img src="assets/img/ioet.png" width="90" height="auto" style="padding-top: 2rem; padding-bottom: 2rem;" alt="logo" />
55
</div>
@@ -8,7 +8,7 @@
88
*ngFor="let item of itemsSidebar"
99
[routerLink]="item.route"
1010
routerLinkActive=""
11-
class="list-group-item list-group-item-action bg-light"
11+
class="list-group-item list-group-item-action dark:bg-grey-dark dark:text-white"
1212
[ngClass]="{active: item.active}"
1313
>
1414
<i class="{{ item.icon }}"></i> {{ item.text }}
@@ -20,6 +20,9 @@
2020
<button class="btn btn-primary" id="menu-toggle">
2121
Toggle Menu
2222
</button>
23+
<div class="dark-mode-toggle">
24+
<app-dark-mode></app-dark-mode>
25+
</div>
2326
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
2427
<span class="navbar-toggler-icon"></span>
2528
</button>

src/app/modules/shared/components/sidebar/sidebar.component.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ body {
5353
font-weight: bold;
5454
text-decoration: underline;
5555
border-color: $primary;
56+
background-color: transparent;
5657
}
5758

5859
.full-height {
@@ -62,3 +63,9 @@ body {
6263
height: -o-calc(100vh - 12vh);
6364
height: calc(100vh - 12vh);
6465
}
66+
67+
.dark-mode-toggle{
68+
position: absolute;
69+
left: 50%;
70+
transform: translateX(-50%);
71+
}

src/assets/icons/moon.svg

Lines changed: 8 additions & 0 deletions
Loading

src/assets/icons/sun.svg

Lines changed: 7 additions & 0 deletions
Loading

tailwind.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = {
55
'./src/**/*.{html,ts}',
66
]
77
},
8-
darkMode: false,
8+
darkMode: 'class',
99
theme: {
1010
extend: {
1111
screens: {

0 commit comments

Comments
 (0)