This repository has been archived by the owner on Dec 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add theme toggle functionality and dark mode support
- Loading branch information
1 parent
e398a6b
commit 78837bd
Showing
5 changed files
with
153 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,71 @@ | ||
// components.js | ||
function AboutPage() { | ||
// Create the main container | ||
// Create the theme toggle button | ||
function ThemeToggleButton() { | ||
const button = document.createElement('button'); | ||
button.setAttribute('aria-label', 'Toggle dark mode'); | ||
button.textContent = 'Toggle Theme'; | ||
button.classList.add('p-2', 'rounded', 'border', 'bg-gray-200', 'dark:bg-gray-700'); | ||
|
||
// Add click event to toggle dark mode | ||
button.addEventListener('click', () => { | ||
document.documentElement.classList.toggle('dark'); | ||
const isDarkMode = document.documentElement.classList.contains('dark'); | ||
localStorage.setItem('theme', isDarkMode ? 'dark' : 'light'); | ||
}); | ||
|
||
return button; | ||
} | ||
|
||
// Append the theme toggle button to the page | ||
function addThemeToggleToPage() { | ||
const toggleButton = ThemeToggleButton(); | ||
document.body.prepend(toggleButton); // Add the toggle button to the body | ||
} | ||
|
||
// Create About page | ||
function AboutPage() { | ||
const container = document.createElement('div'); | ||
container.classList.add('space-y-4', 'p-6', 'text-center'); | ||
|
||
// Create the main title | ||
|
||
const title = document.createElement('h1'); | ||
title.textContent = 'DWA Starter Vanilla'; | ||
title.classList.add('text-3xl', 'font-bold', 'mb-4'); | ||
|
||
// Create the first paragraph | ||
|
||
const para1 = document.createElement('p'); | ||
para1.textContent = "Decentralized Web App: it's a Web5 Progressive Web App."; | ||
para1.classList.add('text-lg'); | ||
|
||
// Create the subtitle | ||
|
||
const subtitle = document.createElement('h2'); | ||
subtitle.textContent = 'Why PWA?'; | ||
subtitle.classList.add('text-2xl', 'font-semibold', 'mt-4'); | ||
|
||
// Create the second paragraph | ||
|
||
const para2 = document.createElement('p'); | ||
para2.textContent = 'It\'s a perfect match with Web5 DWNs since a PWA can work offline and DWN has a synced local storage.'; | ||
para2.classList.add('text-lg'); | ||
|
||
// Append elements to the container | ||
|
||
container.appendChild(title); | ||
container.appendChild(para1); | ||
container.appendChild(subtitle); | ||
container.appendChild(para2); | ||
|
||
// Return the container element | ||
|
||
return container; | ||
} | ||
|
||
export function Home() { | ||
export function Home() { | ||
document.getElementById('app').innerHTML = `<h1>Home</h1>`; | ||
} | ||
|
||
export function About() { | ||
} | ||
export function About() { | ||
const app = document.getElementById('app'); | ||
app.innerHTML = ''; // Clear the current content | ||
|
||
// Create and append the About page | ||
const aboutPage = AboutPage(); // Call the AboutPage function to get the component | ||
app.innerHTML = ''; | ||
const aboutPage = AboutPage(); | ||
app.appendChild(aboutPage); | ||
} | ||
|
||
export function Settings() { | ||
} | ||
export function Settings() { | ||
document.getElementById('app').innerHTML = `<h1>Settings</h1>`; | ||
} | ||
|
||
export function NotFound() { | ||
} | ||
export function NotFound() { | ||
document.getElementById('app').innerHTML = `<h1>404 - Page Not Found</h1>`; | ||
} | ||
|
||
} | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,49 @@ | ||
// main.js | ||
// Function to check system preference and localStorage for theme preference | ||
function applyTheme() { | ||
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)").matches; | ||
const currentTheme = localStorage.getItem("theme") || (prefersDarkScheme ? "dark" : "light"); | ||
|
||
import { Home, About, Settings, NotFound } from './components.js'; | ||
// Toggle dark mode based on the stored or system preference | ||
document.documentElement.classList.toggle('dark', currentTheme === 'dark'); | ||
} | ||
|
||
// Define routes and their corresponding components | ||
const routes = { | ||
'/': Home, | ||
'/about': About, | ||
'/settings': Settings, | ||
}; | ||
// Function to add the theme toggle functionality to the page | ||
function addThemeToggleToPage() { | ||
const toggleButton = document.querySelector("#theme-toggle"); | ||
|
||
// Function to handle navigation | ||
function navigateTo(url) { | ||
history.pushState(null, null, url); | ||
router(); | ||
} | ||
// Listen for the toggle button click | ||
toggleButton.addEventListener("click", () => { | ||
const isDarkMode = document.documentElement.classList.contains('dark'); | ||
const newTheme = isDarkMode ? 'light' : 'dark'; | ||
|
||
// Router function to render components based on the current URL | ||
function router() { | ||
const path = window.location.pathname; | ||
const route = routes[path] || NotFound; | ||
route(); | ||
// Toggle the theme and save to localStorage | ||
document.documentElement.classList.toggle('dark', newTheme === 'dark'); | ||
localStorage.setItem("theme", newTheme); | ||
}); | ||
} | ||
|
||
// Event delegation for link clicks | ||
document.addEventListener('click', (e) => { | ||
if (e.target.matches('[data-link]')) { | ||
e.preventDefault(); | ||
navigateTo(e.target.href); | ||
} | ||
// Apply the theme on page load | ||
window.addEventListener("DOMContentLoaded", () => { | ||
applyTheme(); | ||
addThemeToggleToPage(); | ||
}); | ||
|
||
// Listen to popstate event (back/forward navigation) | ||
window.addEventListener('popstate', router); | ||
// Router logic | ||
const routes = { | ||
'/': () => `<h1>Home</h1>`, | ||
'/about': () => `<h1>DWA Starter Vanilla</h1>`, | ||
'/settings': () => `<h1>Settings</h1>`, | ||
'*': () => `<h1>404 - Page Not Found</h1>`, | ||
}; | ||
|
||
function handleRoute() { | ||
const path = window.location.pathname; | ||
const route = routes[path] || routes['*']; | ||
document.getElementById('app').innerHTML = route(); | ||
} | ||
|
||
// Handle back/forward navigation | ||
window.addEventListener('popstate', handleRoute); | ||
|
||
// Initial call to router to render the correct component on page load | ||
document.addEventListener('DOMContentLoaded', router); | ||
// Initial page load | ||
handleRoute(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,72 @@ | ||
// tests/router.spec.js | ||
import { test, expect } from '@playwright/test'; | ||
|
||
test.describe('Vanilla Router', () => { | ||
// Before all tests, start a local server if necessary | ||
|
||
test('should navigate to Home', async ({ page }) => { | ||
await page.goto('/'); | ||
await page.goto('http://localhost:5173/'); | ||
expect(await page.textContent('h1')).toBe('Home'); | ||
}); | ||
|
||
test('should navigate to About', async ({ page }) => { | ||
await page.goto('/about'); | ||
expect(await page.textContent('h1')).toBe('DWA Starter Vanilla'); | ||
await page.goto('http://localhost:5173/about'); | ||
expect(await page.textContent('h1')).toBe('DWA Starter Vanilla'); | ||
}); | ||
|
||
test('should navigate to Settings', async ({ page }) => { | ||
await page.goto('/settings'); | ||
await page.goto('http://localhost:5173/settings'); | ||
expect(await page.textContent('h1')).toBe('Settings'); | ||
}); | ||
|
||
test('should show Not Found for undefined routes', async ({ page }) => { | ||
await page.goto('/undefined-route'); | ||
await page.goto('http://localhost:5173/undefined-route'); | ||
expect(await page.textContent('h1')).toBe('404 - Page Not Found'); | ||
}); | ||
}); | ||
|
||
test.describe('Theme Toggle Functionality', () => { | ||
test('should toggle dark mode and remember preference', async ({ page }) => { | ||
await page.goto('http://localhost:5173/'); | ||
|
||
// Check initial state | ||
const initialIsDarkMode = await page.evaluate(() => document.documentElement.classList.contains('dark')); | ||
|
||
// Toggle the theme | ||
await page.click('#theme-toggle'); | ||
|
||
// Verify that the theme was toggled | ||
const isDarkModeAfterToggle = await page.evaluate(() => document.documentElement.classList.contains('dark')); | ||
expect(isDarkModeAfterToggle).toBe(!initialIsDarkMode); | ||
|
||
// Reload the page to check if preference is remembered | ||
await page.reload(); | ||
const isDarkModeAfterReload = await page.evaluate(() => document.documentElement.classList.contains('dark')); | ||
expect(isDarkModeAfterReload).toBe(isDarkModeAfterToggle); | ||
}); | ||
|
||
test('should load with correct theme based on system preference when no localStorage is set', async ({ page }) => { | ||
// Clear localStorage | ||
await page.evaluate(() => localStorage.removeItem('theme')); | ||
|
||
await page.goto('http://localhost:5173/'); | ||
|
||
// Get system preference | ||
const prefersDarkMode = await page.evaluate(() => window.matchMedia('(prefers-color-scheme: dark)').matches); | ||
|
||
// Check if the page loads with the correct theme based on system preference | ||
const isDarkMode = await page.evaluate(() => document.documentElement.classList.contains('dark')); | ||
expect(isDarkMode).toBe(prefersDarkMode); | ||
}); | ||
|
||
test('should persist theme across different routes', async ({ page }) => { | ||
await page.goto('http://localhost:5173/'); | ||
|
||
// Toggle to dark mode | ||
await page.click('#theme-toggle'); | ||
const isDarkMode = await page.evaluate(() => document.documentElement.classList.contains('dark')); | ||
expect(isDarkMode).toBe(true); | ||
|
||
// Navigate to a different route | ||
await page.goto('http://localhost:5173/about'); | ||
const isDarkModeOnNewRoute = await page.evaluate(() => document.documentElement.classList.contains('dark')); | ||
expect(isDarkModeOnNewRoute).toBe(true); | ||
}); | ||
}); |