Skip to content

Commit d80ecf0

Browse files
authored
feat: add theme settings (#134)
* feat: add theme settings * chore: fix package-lock * chore: fix package lock * fix: theme flash * fix: package import carbon labs * fix: theme settings import * fix: Welcome page test * fix: lock files * feat: add profile panel tests * feat: add local storage tests * feat: add theme context test * fix: remove only modifier
1 parent 68083e1 commit d80ecf0

File tree

17 files changed

+1211
-111
lines changed

17 files changed

+1211
-111
lines changed

package-lock.json

Lines changed: 269 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"prepare": "husky"
2323
},
2424
"dependencies": {
25+
"@carbon-labs/react-theme-settings": "^0.16.0",
26+
"@carbon/ibm-products": "^2.74.0",
2527
"@carbon/icons-react": "^11.53.0",
2628
"@carbon/react": "^1.73.0",
2729
"@carbon/styles": "^1.72.0",
@@ -38,6 +40,7 @@
3840
"devDependencies": {
3941
"@double-great/stylelint-a11y": "^3.0.4",
4042
"@eslint/js": "^9.21.0",
43+
"@testing-library/dom": "^10.4.1",
4144
"@testing-library/jest-dom": "^6.6.3",
4245
"@testing-library/react": "^16.2.0",
4346
"@testing-library/user-event": "^14.6.1",
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/**
2+
* Copyright IBM Corp. 2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import { test, expect, vi, beforeEach } from 'vitest';
9+
import { screen, waitFor } from '@testing-library/react';
10+
import '@testing-library/jest-dom';
11+
import userEvent from '@testing-library/user-event';
12+
import { renderWithTheme } from '../test/test-utils';
13+
import ProfilePanel from '../components/profilePanel/ProfilePanel';
14+
import { server } from '../test/server';
15+
import {
16+
setupBeforeAll,
17+
setupBeforeEach,
18+
setupAfterEach,
19+
setupAfterAll,
20+
} from '../test/setup';
21+
import * as ThemeContext from '../context/ThemeContext';
22+
23+
// Setup test environment
24+
beforeAll(() => setupBeforeAll(server));
25+
beforeEach(() => setupBeforeEach(server));
26+
afterEach(() => setupAfterEach(server));
27+
afterAll(() => setupAfterAll(server));
28+
29+
// Mock the ThemeContext hook
30+
const mockSetThemeSetting = vi.fn();
31+
const mockSetThemeMenuCompliment = vi.fn();
32+
33+
beforeEach(() => {
34+
// Reset mocks before each test
35+
mockSetThemeSetting.mockReset();
36+
mockSetThemeMenuCompliment.mockReset();
37+
38+
// Mock the useThemeContext hook
39+
vi.spyOn(ThemeContext, 'useThemeContext').mockImplementation(() => ({
40+
themeSetting: 'light',
41+
setThemeSetting: mockSetThemeSetting,
42+
themeMenuCompliment: false,
43+
setThemeMenuCompliment: mockSetThemeMenuCompliment,
44+
theme: 'g10',
45+
themeMenu: 'g10',
46+
ready: true,
47+
}));
48+
});
49+
50+
// Debug test to understand the DOM structure
51+
test('debug DOM structure', () => {
52+
renderWithTheme(<ProfilePanel />);
53+
});
54+
55+
test('renders profile panel with user information', () => {
56+
renderWithTheme(<ProfilePanel />);
57+
58+
// Check if user information is displayed
59+
expect(screen.getByText('Anne Profile')).toBeInTheDocument();
60+
expect(screen.getByText('[email protected]')).toBeInTheDocument();
61+
62+
// Check if theme settings are displayed
63+
expect(screen.getByText('Complement menu theme')).toBeInTheDocument();
64+
});
65+
66+
test('changes theme when theme switcher is used', async () => {
67+
const user = userEvent.setup();
68+
renderWithTheme(<ProfilePanel />);
69+
70+
// Find all buttons in the theme switcher area
71+
// This is more resilient than looking for specific text
72+
const buttons = screen.getAllByRole('tab');
73+
74+
// Find the dark theme button - it should be one of the buttons
75+
// We can look for buttons near the theme switcher area
76+
const darkButton = Array.from(buttons).find((button) => {
77+
// Look for a button that might represent dark theme
78+
// This could be by text content, aria-label, or other attributes
79+
return (
80+
button.textContent.toLowerCase().includes('dark') ||
81+
button.getAttribute('aria-label')?.toLowerCase().includes('dark')
82+
);
83+
});
84+
85+
// If we found a button that looks like the dark theme button, click it
86+
if (darkButton) {
87+
await user.click(darkButton);
88+
89+
// Verify that setThemeSetting was called with 'dark'
90+
await waitFor(() => {
91+
expect(mockSetThemeSetting).toHaveBeenCalledWith('dark');
92+
});
93+
} else {
94+
// If we couldn't find a button that looks like the dark theme button,
95+
// log the available buttons to help with debugging
96+
console.log(
97+
'Available buttons:',
98+
buttons.map((b) => ({
99+
text: b.textContent,
100+
ariaLabel: b.getAttribute('aria-label'),
101+
})),
102+
);
103+
throw new Error('Could not find dark theme button');
104+
}
105+
});
106+
107+
test('toggles menu complement when checkbox is clicked', async () => {
108+
const user = userEvent.setup();
109+
renderWithTheme(<ProfilePanel />);
110+
111+
// Find the menu complement checkbox by its label
112+
const complementLabel = screen.getByText('Complement menu theme');
113+
114+
// Find the closest checkbox to this label
115+
// This is more resilient than trying to navigate the DOM structure directly
116+
const checkboxes = screen.getAllByRole('checkbox');
117+
const complementCheckbox = checkboxes.find((checkbox) => {
118+
// Check if this checkbox is associated with the label
119+
const labelFor = complementLabel.getAttribute('for');
120+
if (labelFor && labelFor === checkbox.id) {
121+
return true;
122+
}
123+
124+
// Check if the checkbox is contained within the label
125+
return (
126+
complementLabel.contains(checkbox) ||
127+
complementLabel.parentElement?.contains(checkbox)
128+
);
129+
});
130+
131+
// If we found the checkbox, click it
132+
if (complementCheckbox) {
133+
await user.click(complementCheckbox);
134+
135+
// Verify that setThemeMenuCompliment was called with true
136+
await waitFor(() => {
137+
expect(mockSetThemeMenuCompliment).toHaveBeenCalledWith(true);
138+
});
139+
} else {
140+
// If we couldn't find the checkbox, try clicking the label itself
141+
// This often works because labels are associated with checkboxes
142+
await user.click(complementLabel);
143+
144+
// Verify that setThemeMenuCompliment was called with true
145+
await waitFor(() => {
146+
expect(mockSetThemeMenuCompliment).toHaveBeenCalledWith(true);
147+
});
148+
}
149+
});
150+
151+
// Made with Bob

0 commit comments

Comments
 (0)