fix: Broken header in public Help Center portal (#11704)

Fixes https://linear.app/chatwoot/issue/CW-4473/broken-header-in-help-center-portal
This commit is contained in:
Sivin Varghese
2025-06-12 00:37:24 +05:30
committed by GitHub
parent f627dbe42d
commit bf3b1676dd
11 changed files with 256 additions and 120 deletions

View File

@@ -29,22 +29,21 @@ describe('InitializationHelpers.navigateToLocalePage', () => {
delete global.window;
});
it('should return false if .locale-switcher is not found', () => {
it('sets up document event listener regardless of locale-switcher existence', () => {
document.querySelector('.locale-switcher').remove();
const result = InitializationHelpers.navigateToLocalePage();
expect(result).toBe(false);
const documentSpy = vi.spyOn(document, 'addEventListener');
InitializationHelpers.navigateToLocalePage();
expect(documentSpy).toHaveBeenCalledWith('change', expect.any(Function));
documentSpy.mockRestore();
});
it('should add change event listener to .locale-switcher', () => {
const localeSwitcher = document.querySelector('.locale-switcher');
const addEventListenerSpy = vi.spyOn(localeSwitcher, 'addEventListener');
it('adds document-level event listener to handle locale switching', () => {
const documentSpy = vi.spyOn(document, 'addEventListener');
InitializationHelpers.navigateToLocalePage();
expect(addEventListenerSpy).toHaveBeenCalledWith(
'change',
expect.any(Function)
);
expect(documentSpy).toHaveBeenCalledWith('change', expect.any(Function));
documentSpy.mockRestore();
});
});

View File

@@ -3,8 +3,7 @@ import {
removeQueryParamsFromUrl,
updateThemeInHeader,
switchTheme,
initializeThemeSwitchButtons,
initializeToggleButton,
initializeThemeHandlers,
initializeMediaQueryListener,
initializeTheme,
} from '../portalThemeHelper.js';
@@ -21,6 +20,7 @@ describe('portalThemeHelper', () => {
appearanceDropdown = document.createElement('div');
appearanceDropdown.id = 'appearance-dropdown';
appearanceDropdown.classList.add('appearance-menu');
document.body.appendChild(appearanceDropdown);
window.matchMedia = vi.fn().mockImplementation(query => ({
@@ -142,7 +142,7 @@ describe('portalThemeHelper', () => {
});
});
describe('#initializeThemeSwitchButtons', () => {
describe('#initializeThemeHandlers', () => {
beforeEach(() => {
appearanceDropdown.innerHTML = `
<button data-theme="light"><span class="check-mark-icon light-theme"></span></button>
@@ -153,43 +153,58 @@ describe('portalThemeHelper', () => {
it('does nothing if the appearance dropdown is not found', () => {
appearanceDropdown.remove();
expect(appearanceDropdown.dataset.currentTheme).toBeUndefined();
});
it('should set current theme to system if no theme in localStorage', () => {
localStorage.removeItem('theme');
initializeThemeSwitchButtons();
expect(appearanceDropdown.dataset.currentTheme).toBe('system');
expect(() => initializeThemeHandlers()).not.toThrow();
});
it('sets the current theme to the light theme', () => {
localStorage.theme = 'light';
appearanceDropdown.dataset.currentTheme = 'light';
initializeThemeSwitchButtons();
it('should handle theme button clicks', () => {
initializeThemeHandlers();
// Simulate clicking a theme button
const lightButton = appearanceDropdown.querySelector(
'button[data-theme="light"]'
);
const clickEvent = new Event('click', { bubbles: true });
Object.defineProperty(clickEvent, 'target', {
value: lightButton,
enumerable: true,
});
document.dispatchEvent(clickEvent);
expect(localStorage.theme).toBe('light');
expect(appearanceDropdown.dataset.currentTheme).toBe('light');
});
it('sets the current theme to the dark theme', () => {
localStorage.theme = 'dark';
appearanceDropdown.dataset.currentTheme = 'dark';
initializeThemeSwitchButtons();
expect(appearanceDropdown.dataset.currentTheme).toBe('dark');
});
});
it('should toggle dropdown visibility on toggle button click', () => {
initializeThemeHandlers();
describe('#initializeToggleButton', () => {
it('does nothing if the theme toggle button is not found', () => {
themeToggleButton.remove();
initializeToggleButton();
expect(appearanceDropdown.style.display).toBe('');
// Initially closed
expect(appearanceDropdown.dataset.dropdownOpen).toBeUndefined();
// Click to open
themeToggleButton.click();
expect(appearanceDropdown.dataset.dropdownOpen).toBe('true');
// Click to close
themeToggleButton.click();
expect(appearanceDropdown.dataset.dropdownOpen).toBe('false');
});
it('toggles the appearance dropdown show/hide', () => {
themeToggleButton.click();
appearanceDropdown.style.display = 'flex';
expect(appearanceDropdown.style.display).toBe('flex');
themeToggleButton.click();
appearanceDropdown.style.display = 'none';
expect(appearanceDropdown.style.display).toBe('none');
it('should close dropdown when clicking outside', () => {
initializeThemeHandlers();
// Open dropdown
appearanceDropdown.dataset.dropdownOpen = 'true';
// Click outside
const outsideClick = new Event('click', { bubbles: true });
Object.defineProperty(outsideClick, 'target', {
value: document.body,
enumerable: true,
});
document.dispatchEvent(outsideClick);
expect(appearanceDropdown.dataset.dropdownOpen).toBe('false');
});
});