dd52e0591c
新增公共 AutoGrowTextArea 组件及旧浏览器高度测量降级 将生成、编辑、角色、图标、规范、宣发和 Agent 输入框接入公共组件 统一最大高度、内部滚动与输入框视觉样式 补齐组件和各业务面板回归测试 同步画布编辑器与 Agent 面板技术文档
133 lines
3.9 KiB
TypeScript
133 lines
3.9 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { act, fireEvent, render, screen } from '@testing-library/react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { AutoGrowTextArea } from './AutoGrowTextArea';
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe('AutoGrowTextArea', () => {
|
|
it('uses native field sizing when the browser supports it', () => {
|
|
const supports = vi.fn().mockReturnValue(true);
|
|
const resizeObserver = vi.fn();
|
|
const onChange = vi.fn();
|
|
vi.stubGlobal('CSS', { supports });
|
|
vi.stubGlobal('ResizeObserver', resizeObserver);
|
|
|
|
render(
|
|
<AutoGrowTextArea
|
|
aria-label="自动增长输入框"
|
|
value=""
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText(
|
|
'自动增长输入框',
|
|
) as HTMLTextAreaElement;
|
|
expect(supports).toHaveBeenCalledWith('field-sizing', 'content');
|
|
expect(resizeObserver).not.toHaveBeenCalled();
|
|
expect(input.getAttribute('rows')).toBe('1');
|
|
expect(input.className).toContain('auto-grow-text-area');
|
|
expect(input.className).toContain('[field-sizing:content]');
|
|
expect(input.className).toContain('min-h-10');
|
|
expect(input.className).toContain('max-h-32');
|
|
expect(input.className).toContain('overflow-y-auto');
|
|
expect(input.style.height).toBe('');
|
|
|
|
fireEvent.change(input, { target: { value: '新内容' } });
|
|
expect(onChange).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('falls back to measured sizing, preserves css limits and remeasures width changes', () => {
|
|
let resizeObserverCallback!: ResizeObserverCallback;
|
|
const observe = vi.fn();
|
|
const disconnect = vi.fn();
|
|
vi.stubGlobal('CSS', { supports: vi.fn().mockReturnValue(false) });
|
|
vi.stubGlobal(
|
|
'ResizeObserver',
|
|
vi.fn().mockImplementation((callback: ResizeObserverCallback) => {
|
|
resizeObserverCallback = callback;
|
|
return {
|
|
observe,
|
|
unobserve: vi.fn(),
|
|
disconnect,
|
|
};
|
|
}),
|
|
);
|
|
|
|
const { rerender, unmount } = render(
|
|
<AutoGrowTextArea
|
|
aria-label="自动增长输入框"
|
|
value=""
|
|
style={{ minHeight: '80px', maxHeight: '128px' }}
|
|
onChange={vi.fn()}
|
|
/>,
|
|
);
|
|
const input = screen.getByLabelText(
|
|
'自动增长输入框',
|
|
) as HTMLTextAreaElement;
|
|
expect(observe).toHaveBeenCalledWith(input);
|
|
let contentHeight = 92;
|
|
Object.defineProperty(input, 'offsetHeight', {
|
|
configurable: true,
|
|
get: () => {
|
|
const styleHeight = Number.parseFloat(input.style.height);
|
|
return Number.isNaN(styleHeight) ? 82 : styleHeight;
|
|
},
|
|
});
|
|
Object.defineProperty(input, 'clientHeight', {
|
|
configurable: true,
|
|
get: () => input.offsetHeight - 2,
|
|
});
|
|
Object.defineProperty(input, 'scrollHeight', {
|
|
configurable: true,
|
|
get: () => contentHeight,
|
|
});
|
|
|
|
rerender(
|
|
<AutoGrowTextArea
|
|
aria-label="自动增长输入框"
|
|
value="第一行\n第二行\n第三行\n第四行\n第五行"
|
|
style={{ minHeight: '80px', maxHeight: '128px' }}
|
|
onChange={vi.fn()}
|
|
/>,
|
|
);
|
|
expect(input.style.height).toBe('94px');
|
|
expect(input.style.overflowY).toBe('hidden');
|
|
|
|
contentHeight = 180;
|
|
act(() => {
|
|
resizeObserverCallback(
|
|
[
|
|
{
|
|
target: input,
|
|
contentRect: { width: 180 },
|
|
} as unknown as ResizeObserverEntry,
|
|
],
|
|
{} as ResizeObserver,
|
|
);
|
|
});
|
|
expect(input.style.height).toBe('128px');
|
|
expect(input.style.overflowY).toBe('auto');
|
|
|
|
contentHeight = 30;
|
|
rerender(
|
|
<AutoGrowTextArea
|
|
aria-label="自动增长输入框"
|
|
value="短内容"
|
|
style={{ minHeight: '80px', maxHeight: '128px' }}
|
|
onChange={vi.fn()}
|
|
/>,
|
|
);
|
|
expect(input.style.height).toBe('80px');
|
|
expect(input.style.overflowY).toBe('hidden');
|
|
|
|
unmount();
|
|
expect(disconnect).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|