mirror of
https://github.com/dream-num/univer.git
synced 2026-10-02 04:24:34 +08:00
fix(ui): align dropdowns correctly in RTL layouts (#7522)
This commit is contained in:
@@ -295,7 +295,6 @@ export function DefinedName({ disable }: { disable: boolean }) {
|
|||||||
univer-box-border univer-size-full univer-appearance-none univer-bg-transparent univer-pl-1.5
|
univer-box-border univer-size-full univer-appearance-none univer-bg-transparent univer-pl-1.5
|
||||||
univer-pr-5 univer-text-gray-900
|
univer-pr-5 univer-text-gray-900
|
||||||
focus:univer-outline-none
|
focus:univer-outline-none
|
||||||
rtl:univer-pl-5 rtl:univer-pr-1.5
|
|
||||||
dark:!univer-border-r-gray-700 dark:!univer-text-gray-0
|
dark:!univer-border-r-gray-700 dark:!univer-text-gray-0
|
||||||
`, borderRightClassName, {
|
`, borderRightClassName, {
|
||||||
'univer-cursor-not-allowed': disable,
|
'univer-cursor-not-allowed': disable,
|
||||||
@@ -323,7 +322,6 @@ export function DefinedName({ disable }: { disable: boolean }) {
|
|||||||
univer-absolute univer-right-0 univer-top-0 univer-flex univer-h-full univer-cursor-pointer
|
univer-absolute univer-right-0 univer-top-0 univer-flex univer-h-full univer-cursor-pointer
|
||||||
univer-items-center univer-justify-center univer-px-1 univer-transition-colors univer-duration-200
|
univer-items-center univer-justify-center univer-px-1 univer-transition-colors univer-duration-200
|
||||||
hover:univer-bg-gray-100
|
hover:univer-bg-gray-100
|
||||||
rtl:univer-left-0 rtl:univer-right-auto
|
|
||||||
dark:!univer-text-gray-0
|
dark:!univer-text-gray-0
|
||||||
dark:hover:!univer-bg-gray-800
|
dark:hover:!univer-bg-gray-800
|
||||||
`, {
|
`, {
|
||||||
|
|||||||
@@ -328,11 +328,26 @@ describe('FormulaBar', () => {
|
|||||||
|
|
||||||
it('keeps its toolbar layout LTR when the sheet host is RTL', () => {
|
it('keeps its toolbar layout LTR when the sheet host is RTL', () => {
|
||||||
currentBed = createFormulaBarTestBed();
|
currentBed = createFormulaBarTestBed();
|
||||||
const rendered = renderWithDependencies(<FormulaBar disableDefinedName />, currentBed.injector);
|
const rendered = renderWithDependencies(
|
||||||
|
<div dir="rtl">
|
||||||
|
<FormulaBar disableDefinedName />
|
||||||
|
</div>,
|
||||||
|
currentBed.injector
|
||||||
|
);
|
||||||
root = rendered.root;
|
root = rendered.root;
|
||||||
container = rendered.container;
|
container = rendered.container;
|
||||||
|
|
||||||
expect(rendered.container.querySelector('[data-u-comp="formula-bar"]')?.getAttribute('dir')).toBe('ltr');
|
const formulaBar = rendered.container.querySelector('[data-u-comp="formula-bar"]');
|
||||||
|
const definedName = formulaBar?.querySelector('[data-u-comp="defined-name"]');
|
||||||
|
const input = definedName?.querySelector('input');
|
||||||
|
const dropdownTrigger = definedName?.querySelector('a');
|
||||||
|
|
||||||
|
expect(formulaBar?.getAttribute('dir')).toBe('ltr');
|
||||||
|
expect(input?.classList).toContain('univer-pl-1.5');
|
||||||
|
expect(input?.classList).toContain('univer-pr-5');
|
||||||
|
expect(input?.className).not.toContain('rtl:');
|
||||||
|
expect(dropdownTrigger?.classList).toContain('univer-right-0');
|
||||||
|
expect(dropdownTrigger?.className).not.toContain('rtl:');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes editing through the command service when cancel is clicked', async () => {
|
it('closes editing through the command service when cancel is clicked', async () => {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import type { ReactNode } from 'react';
|
|||||||
import type { IMenuItem, IValueOption } from '../../../services/menu/menu';
|
import type { IMenuItem, IValueOption } from '../../../services/menu/menu';
|
||||||
import {
|
import {
|
||||||
clsx,
|
clsx,
|
||||||
|
ConfigContext,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -149,7 +150,8 @@ export const TooltipWrapper = forwardRef<ITooltipWrapperRef, ITooltipProps & { d
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function DropdownWrapper(props: Omit<Partial<IDropdownProps>, 'overlay'> & { overlay: ReactNode; align?: 'start' | 'end' | 'center' }) {
|
export function DropdownWrapper(props: Omit<Partial<IDropdownProps>, 'overlay'> & { overlay: ReactNode; align?: 'start' | 'end' | 'center' }) {
|
||||||
const { children, overlay, disabled, align = 'start' } = props;
|
const { children, overlay, disabled, align } = props;
|
||||||
|
const { direction } = useContext(ConfigContext);
|
||||||
const { dropdownVisible, setDropdownVisible } = useContext(TooltipWrapperContext);
|
const { dropdownVisible, setDropdownVisible } = useContext(TooltipWrapperContext);
|
||||||
const triggerRef = useRef<HTMLDivElement>(null);
|
const triggerRef = useRef<HTMLDivElement>(null);
|
||||||
const overlayRef = useRef<HTMLDivElement>(null);
|
const overlayRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -181,7 +183,7 @@ export function DropdownWrapper(props: Omit<Partial<IDropdownProps>, 'overlay'>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
align={align}
|
align={align ?? (direction === 'rtl' ? 'end' : 'start')}
|
||||||
overlay={(
|
overlay={(
|
||||||
<div ref={overlayRef} className="univer-grid univer-gap-2">
|
<div ref={overlayRef} className="univer-grid univer-gap-2">
|
||||||
{overlay}
|
{overlay}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
import type { ComponentType, ReactElement } from 'react';
|
import type { ComponentType, ReactElement } from 'react';
|
||||||
import { cleanup, fireEvent, render } from '@testing-library/react';
|
import { cleanup, fireEvent, render } from '@testing-library/react';
|
||||||
import { ILogService, Injector, LocaleService } from '@univerjs/core';
|
import { ILogService, Injector, LocaleService } from '@univerjs/core';
|
||||||
|
import { ConfigProvider } from '@univerjs/design';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
@@ -134,6 +135,25 @@ describe('DropdownMenuLabel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('DropdownWrapper', () => {
|
describe('DropdownWrapper', () => {
|
||||||
|
it('opens toward the left in RTL layouts', async () => {
|
||||||
|
const { findByText, getByRole } = render(
|
||||||
|
<ConfigProvider direction="rtl" mountContainer={document.body}>
|
||||||
|
<ToolbarDropdownProvider>
|
||||||
|
<TooltipWrapper dropdownKey="test-dropdown">
|
||||||
|
<DropdownWrapper overlay={<div>Dropdown content</div>}>
|
||||||
|
<button type="button">Open dropdown</button>
|
||||||
|
</DropdownWrapper>
|
||||||
|
</TooltipWrapper>
|
||||||
|
</ToolbarDropdownProvider>
|
||||||
|
</ConfigProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(getByRole('button', { name: 'Open dropdown' }));
|
||||||
|
const content = (await findByText('Dropdown content')).closest('[data-slot="popover-content"]');
|
||||||
|
|
||||||
|
expect(content?.getAttribute('data-align')).toBe('end');
|
||||||
|
});
|
||||||
|
|
||||||
it('closes on an outside pointerdown even when no click follows', async () => {
|
it('closes on an outside pointerdown even when no click follows', async () => {
|
||||||
const { findByText, getByRole, queryByText } = render(
|
const { findByText, getByRole, queryByText } = render(
|
||||||
<ToolbarDropdownProvider>
|
<ToolbarDropdownProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user