mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-08 19:48:51 +01:00
🔧 fix+chore: Resolve Overflow in Settings Modal & Upgrade to Headless UI 2.0 (#2661)
* fix: dropdown overflow * fix: make dropdown work on mobile * feat: update headlessui to 2.0 and use portal * feat: rewrite modal using headlessui * fix: applying of maxHeight * fix: optimize backdrop for dark mode * fix: rendering dropdown width * feat: match small screen layout to radix-ui dialog * revert: mobile modifications * fix: modal animations * fix: z-index * chore: Migrate from HeadlessUI 1.0 to 2.0 * fix: h2 nesting * fix: use lighter border for PopoverButtons * feat: Move modal to the top if using a small screen * fix: mobile position * fix: frontend tests * feat: use row layout in mobile instead of col * fix: remove config path from tsconfig * fix: fix dropdown tests (gpt4o ftw!) * feat: Upgrade to latest headlessui version * fix:test1 * fix: ThemeSelector test * fix: re-add speech tab * style: use pl and pr-3 * fix: speech tab dropdowns * style: use maxHeight for language * feat: convert DropdownNoState to v2.0 * fix: use v2 params for voiceDropdown * style: reduce maxHeight for VoiceDropdown and set fixed width * chore: rebuild package-lock * style(fix): copy over the same styles for the settingsTab * style(fix): use -top-1 for speech tabs * style(fix): use max-w-[400px] --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
b34a4ddac1
commit
03fe361917
23 changed files with 573 additions and 328 deletions
|
|
@ -34,9 +34,8 @@ export const ThemeSelector = ({
|
|||
value={theme}
|
||||
onChange={onChange}
|
||||
options={themeOptions}
|
||||
width={180}
|
||||
position={'left'}
|
||||
maxHeight="200px"
|
||||
sizeClasses="w-[220px]"
|
||||
anchor="bottom start"
|
||||
testId="theme-selector"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -111,8 +110,8 @@ export const LangSelector = ({
|
|||
<Dropdown
|
||||
value={langcode}
|
||||
onChange={onChange}
|
||||
position={'left'}
|
||||
maxHeight="271px"
|
||||
sizeClasses="[--anchor-max-height:256px]"
|
||||
anchor="bottom start"
|
||||
options={languageOptions}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ describe('LangSelector', () => {
|
|||
});
|
||||
|
||||
it('renders correctly', () => {
|
||||
global.ResizeObserver = class MockedResizeObserver {
|
||||
observe = jest.fn();
|
||||
unobserve = jest.fn();
|
||||
disconnect = jest.fn();
|
||||
};
|
||||
const { getByText } = render(
|
||||
<RecoilRoot>
|
||||
<LangSelector langcode="en-US" onChange={mockOnChange} />
|
||||
|
|
@ -24,6 +29,11 @@ describe('LangSelector', () => {
|
|||
});
|
||||
|
||||
it('calls onChange when the select value changes', async () => {
|
||||
global.ResizeObserver = class MockedResizeObserver {
|
||||
observe = jest.fn();
|
||||
unobserve = jest.fn();
|
||||
disconnect = jest.fn();
|
||||
};
|
||||
const { getByText, getByTestId } = render(
|
||||
<RecoilRoot>
|
||||
<LangSelector langcode="en-US" onChange={mockOnChange} />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// ThemeSelector.spec.tsx
|
||||
import 'test/matchMedia.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { render, fireEvent, waitFor } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { ThemeSelector } from './General';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
|
@ -13,6 +15,11 @@ describe('ThemeSelector', () => {
|
|||
});
|
||||
|
||||
it('renders correctly', () => {
|
||||
global.ResizeObserver = class MockedResizeObserver {
|
||||
observe = jest.fn();
|
||||
unobserve = jest.fn();
|
||||
disconnect = jest.fn();
|
||||
};
|
||||
const { getByText } = render(
|
||||
<RecoilRoot>
|
||||
<ThemeSelector theme="system" onChange={mockOnChange} />
|
||||
|
|
@ -24,6 +31,11 @@ describe('ThemeSelector', () => {
|
|||
});
|
||||
|
||||
it('calls onChange when the select value changes', async () => {
|
||||
global.ResizeObserver = class MockedResizeObserver {
|
||||
observe = jest.fn();
|
||||
unobserve = jest.fn();
|
||||
disconnect = jest.fn();
|
||||
};
|
||||
const { getByText, getByTestId } = render(
|
||||
<RecoilRoot>
|
||||
<ThemeSelector theme="system" onChange={mockOnChange} />
|
||||
|
|
@ -42,9 +54,9 @@ describe('ThemeSelector', () => {
|
|||
const darkOption = getByText('Dark');
|
||||
fireEvent.click(darkOption);
|
||||
|
||||
// Ensure that the onChange is called with the expected value after a short delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith('dark');
|
||||
// Ensure that the onChange is called with the expected value
|
||||
await waitFor(() => {
|
||||
expect(mockOnChange).toHaveBeenCalledWith('dark');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ export const ForkSettings = () => {
|
|||
value={forkSetting}
|
||||
onChange={setForkSetting}
|
||||
options={forkOptions}
|
||||
position={'left'}
|
||||
maxHeight="199px"
|
||||
sizeClasses="w-[200px]"
|
||||
anchor="bottom start"
|
||||
testId="fork-setting-dropdown"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ export default function EngineSTTDropdown() {
|
|||
value={engineSTT}
|
||||
onChange={handleSelect}
|
||||
options={endpointOptions}
|
||||
width={180}
|
||||
position={'left'}
|
||||
sizeClasses="w-[180px]"
|
||||
anchor="bottom start"
|
||||
testId="EngineSTTDropdown"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -98,8 +98,8 @@ export default function LanguageSTTDropdown() {
|
|||
value={languageSTT}
|
||||
onChange={handleSelect}
|
||||
options={languageOptions}
|
||||
width={220}
|
||||
position={'left'}
|
||||
sizeClasses="[--anchor-max-height:256px]"
|
||||
anchor="bottom start"
|
||||
testId="LanguageSTTDropdown"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ function Speech() {
|
|||
<Tabs.Content
|
||||
value={SettingsTabValues.SPEECH}
|
||||
role="tabpanel"
|
||||
className="w-full px-4 md:min-h-[300px]"
|
||||
className="w-full md:min-h-[271px]"
|
||||
ref={contentRef}
|
||||
>
|
||||
<Tabs.Root
|
||||
|
|
@ -138,8 +138,8 @@ function Speech() {
|
|||
orientation="horizontal"
|
||||
value={advancedMode ? 'advanced' : 'simple'}
|
||||
>
|
||||
<div className="sticky top-0 z-50 bg-white dark:bg-gray-700">
|
||||
<Tabs.List className="sticky top-0 mb-4 flex justify-center bg-white dark:bg-gray-700">
|
||||
<div className="sticky -top-1 z-50 mb-4 bg-white dark:bg-gray-700">
|
||||
<Tabs.List className="flex justify-center bg-white dark:bg-gray-700">
|
||||
<Tabs.Trigger
|
||||
onClick={() => setAdvancedMode(false)}
|
||||
className={cn(
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ export default function EngineTTSDropdown() {
|
|||
value={engineTTS}
|
||||
onChange={handleSelect}
|
||||
options={endpointOptions}
|
||||
width={180}
|
||||
position={'left'}
|
||||
sizeClasses="w-[180px]"
|
||||
anchor="bottom start"
|
||||
testId="EngineTTSDropdown"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ export default function VoiceDropdown() {
|
|||
value={voice}
|
||||
onChange={setVoice}
|
||||
options={voiceOptions}
|
||||
sizeClasses="min-w-[200px] !max-w-[400px] [--anchor-max-width:400px]"
|
||||
anchor="bottom start"
|
||||
position="left"
|
||||
testId="VoiceDropdown"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue