Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 60 additions & 4 deletions src/components/theme-toggle/ThemeToggle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe("ThemeToggle component", () => {
});


it('should display dark icon when in system mode and effective theme is dark', async () => {
it('should display system icon when theme is system', async () => {
mockedUseTheme.mockReturnValue({
theme: Theme.SYSTEM,
setTheme: vi.fn(),
Expand All @@ -59,8 +59,8 @@ describe("ThemeToggle component", () => {
await act(async () => {
render(<ThemeToggle/>);
});

expect(screen.getByText('🌙')).toBeInTheDocument();
expect(screen.getByText('💻')).toBeInTheDocument();
expect(screen.queryByText('🌙')).not.toBeInTheDocument();
expect(screen.queryByText('☀️️')).not.toBeInTheDocument();
});

Expand All @@ -75,8 +75,9 @@ describe("ThemeToggle component", () => {
render(<ThemeToggle/>);
});

expect(screen.getByText('☀️')).toBeInTheDocument();
expect(screen.getByText('💻')).toBeInTheDocument();
expect(screen.queryByText('🌙')).not.toBeInTheDocument();
expect(screen.queryByText('☀️')).not.toBeInTheDocument();
});

it('should show all options when the dropdown is opened', async () => {
Expand Down Expand Up @@ -106,4 +107,59 @@ describe("ThemeToggle component", () => {
expect(lightOption).not.toHaveClass('themeOption--active');
expect(systemOption).not.toHaveClass('themeOption--active');
});

it('should not show the dropdown on initial render', async () => {
await act(async () => {
render(<ThemeToggle/>);
});

expect(screen.queryByText('Light')).not.toBeInTheDocument();
expect(screen.queryByText('Dark')).not.toBeInTheDocument();
expect(screen.queryByText('System')).not.toBeInTheDocument();
});

it('should call setTheme with the selected value and close the dropdown', async () => {
const setTheme = vi.fn();
mockedUseTheme.mockReturnValue({
theme: Theme.DARK,
setTheme,
getEffectiveTheme: vi.fn(() => Theme.DARK),
});

await act(async () => {
render(<ThemeToggle/>);
});

fireEvent.click(screen.getByRole('button', { name: '🌙' }));
fireEvent.click(screen.getByText('Light').closest('button')!);

expect(setTheme).toHaveBeenCalledWith(Theme.LIGHT);
expect(screen.queryByText('Light')).not.toBeInTheDocument();
});

it('should close the dropdown when clicking outside', async () => {
await act(async () => {
render(<ThemeToggle/>);
});

fireEvent.click(screen.getByRole('button', { name: '🌙' }));
expect(screen.getByText('Light')).toBeInTheDocument();

fireEvent.mouseDown(document.body);
expect(screen.queryByText('Light')).not.toBeInTheDocument();
});

it('should show a checkmark only on the active theme option', async () => {
await act(async () => {
render(<ThemeToggle/>);
});

fireEvent.click(screen.getByRole('button', { name: '🌙' }));

const darkOption = screen.getByText('Dark').closest('button')!;
const lightOption = screen.getByText('Light').closest('button')!;

expect(darkOption).toHaveTextContent('✓');
expect(lightOption).not.toHaveTextContent('✓');
});
});
6 changes: 3 additions & 3 deletions src/components/theme-toggle/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {useEffect, useRef, useState} from "react";
import {useTheme} from "../../context/hooks.tsx";
import {Theme} from "../../context/theme/ThemeContextTypes.ts";
import {THEME_OPTIONS} from "../../constants/Theme.ts";
import styles from "./ThemeToggle.module.css"

export function ThemeToggle() {
const { theme, setTheme, getEffectiveTheme } = useTheme();
const { theme, setTheme } = useTheme();
const [themeMenuOpen, setThemeMenuOpen] = useState(false);
const themeMenuRef = useRef<HTMLDivElement>(null);
const themeIcon = getEffectiveTheme() === Theme.DARK ? '🌙' : '☀️';
const themeIcon = THEME_OPTIONS.find(o => o.value === theme)?.icon;


useEffect(() => {
if (!themeMenuOpen) return;
Expand Down
Loading