mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
📱fix: set initial nav visibility for small screens (#3208)
* fix: hide nav on small screens by default * test: add spec for Nav component
This commit is contained in:
parent
81292bb4dd
commit
b8f2bee3fc
4 changed files with 136 additions and 0 deletions
102
client/src/components/Nav/Nav.spec.tsx
Normal file
102
client/src/components/Nav/Nav.spec.tsx
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import 'test/resizeObserver.mock';
|
||||
import 'test/matchMedia.mock';
|
||||
import 'test/localStorage.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
|
||||
import { AuthContextProvider } from '~/hooks/AuthContext';
|
||||
import { SearchContext } from '~/Providers';
|
||||
import Nav from './Nav';
|
||||
|
||||
const renderNav = ({ search, navVisible, setNavVisible }) => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return render(
|
||||
<RecoilRoot>
|
||||
<BrowserRouter>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthContextProvider>
|
||||
<SearchContext.Provider value={search}>
|
||||
<Nav navVisible={navVisible} setNavVisible={setNavVisible} />
|
||||
</SearchContext.Provider>
|
||||
</AuthContextProvider>
|
||||
</QueryClientProvider>
|
||||
</BrowserRouter>
|
||||
</RecoilRoot>,
|
||||
);
|
||||
};
|
||||
|
||||
const mockMatchMedia = (mediaQueryList?: string[]) => {
|
||||
mediaQueryList = mediaQueryList || [];
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: mediaQueryList.includes(query),
|
||||
media: query,
|
||||
onchange: null,
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
};
|
||||
|
||||
describe('Nav', () => {
|
||||
beforeEach(() => {
|
||||
mockMatchMedia();
|
||||
});
|
||||
|
||||
it('renders visible', () => {
|
||||
const { getByTestId } = renderNav({
|
||||
search: { data: [], pageNumber: 1 },
|
||||
navVisible: true,
|
||||
setNavVisible: jest.fn(),
|
||||
});
|
||||
|
||||
expect(getByTestId('nav')).toBeVisible();
|
||||
});
|
||||
|
||||
it('renders hidden', async () => {
|
||||
const { getByTestId } = renderNav({
|
||||
search: { data: [], pageNumber: 1 },
|
||||
navVisible: false,
|
||||
setNavVisible: jest.fn(),
|
||||
});
|
||||
|
||||
expect(getByTestId('nav')).not.toBeVisible();
|
||||
});
|
||||
|
||||
it('renders hidden when small screen is detected', async () => {
|
||||
mockMatchMedia(['(max-width: 768px)']);
|
||||
|
||||
const navVisible = true;
|
||||
const mockSetNavVisible = jest.fn();
|
||||
|
||||
const { getByTestId } = renderNav({
|
||||
search: { data: [], pageNumber: 1 },
|
||||
navVisible: navVisible,
|
||||
setNavVisible: mockSetNavVisible,
|
||||
});
|
||||
|
||||
// nav is initially visible
|
||||
expect(getByTestId('nav')).toBeVisible();
|
||||
|
||||
// when small screen is detected, the nav is hidden
|
||||
expect(mockSetNavVisible.mock.calls).toHaveLength(1);
|
||||
const updatedNavVisible = mockSetNavVisible.mock.calls[0][0](navVisible);
|
||||
expect(updatedNavVisible).not.toEqual(navVisible);
|
||||
expect(updatedNavVisible).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
@ -42,6 +42,10 @@ const Nav = ({ navVisible, setNavVisible }) => {
|
|||
|
||||
useEffect(() => {
|
||||
if (isSmallScreen) {
|
||||
const savedNavVisible = localStorage.getItem('navVisible');
|
||||
if (savedNavVisible === null) {
|
||||
toggleNavVisible();
|
||||
}
|
||||
setNavWidth('320px');
|
||||
} else {
|
||||
setNavWidth('260px');
|
||||
|
|
@ -102,6 +106,7 @@ const Nav = ({ navVisible, setNavVisible }) => {
|
|||
<TooltipProvider delayDuration={250}>
|
||||
<Tooltip>
|
||||
<div
|
||||
data-testid="nav"
|
||||
className={
|
||||
'nav active max-w-[320px] flex-shrink-0 overflow-x-hidden bg-gray-50 dark:bg-gray-850 md:max-w-[260px]'
|
||||
}
|
||||
|
|
|
|||
21
client/test/localStorage.mock
Normal file
21
client/test/localStorage.mock
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
let store = {};
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
writable: true,
|
||||
value: {
|
||||
getItem: jest.fn().mockImplementation((key) => {
|
||||
if(key in store) {
|
||||
return store[key];
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
setItem: jest.fn().mockImplementation((key, value) => {
|
||||
store[key] = value.toString();
|
||||
}),
|
||||
clear: jest.fn().mockImplementation(() => {
|
||||
store = {};
|
||||
}),
|
||||
removeItem: jest.fn().mockImplementation(() => {
|
||||
delete store[key];
|
||||
}),
|
||||
},
|
||||
});
|
||||
8
client/test/resizeObserver.mock
Normal file
8
client/test/resizeObserver.mock
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Object.defineProperty(window, 'ResizeObserver', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation(() => ({
|
||||
disconnect: jest.fn(),
|
||||
observe: jest.fn(),
|
||||
unobserve: jest.fn(),
|
||||
}))
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue