feat: 支持表格单元格快捷编辑
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import React, { act } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import dayjs from 'dayjs';
|
||||
import EditableCell, {
|
||||
editableValuesEqual,
|
||||
normalizeEditableValue,
|
||||
serializeEditableValue,
|
||||
} from './index';
|
||||
|
||||
let container: HTMLDivElement | null = null;
|
||||
let root: ReturnType<typeof createRoot> | null = null;
|
||||
|
||||
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
afterEach(async () => {
|
||||
if (root) {
|
||||
await act(async () => root?.unmount());
|
||||
}
|
||||
container?.remove();
|
||||
root = null;
|
||||
container = null;
|
||||
});
|
||||
|
||||
describe('editable cell value mapping', () => {
|
||||
it('normalizes and serializes date values', () => {
|
||||
const value = normalizeEditableValue('2026-07-21', 'date');
|
||||
expect(dayjs.isDayjs(value)).toBe(true);
|
||||
expect(serializeEditableValue(value, 'date')).toBe('2026-07-21');
|
||||
});
|
||||
|
||||
it('normalizes and serializes date ranges', () => {
|
||||
const value = normalizeEditableValue(['2026-07-01', '2026-07-31'], 'date-range');
|
||||
expect(serializeEditableValue(value, 'date-range')).toEqual(['2026-07-01', '2026-07-31']);
|
||||
});
|
||||
|
||||
it('converts numeric input and preserves zero', () => {
|
||||
expect(serializeEditableValue(normalizeEditableValue('12.50', 'money'), 'money')).toBe(12.5);
|
||||
expect(serializeEditableValue(0, 'number')).toBe(0);
|
||||
});
|
||||
|
||||
it('normalizes empty and multi-select values', () => {
|
||||
expect(serializeEditableValue('', 'number')).toBeUndefined();
|
||||
expect(normalizeEditableValue(undefined, 'multi-select')).toEqual([]);
|
||||
});
|
||||
|
||||
it('compares structured values without reference equality', () => {
|
||||
expect(editableValuesEqual([1, 2], [1, 2])).toBe(true);
|
||||
expect(editableValuesEqual(' a ', 'a')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('editable cell interactions', () => {
|
||||
it('saves a single-select value immediately when an option is clicked', async () => {
|
||||
const onSave = vi.fn(async () => undefined);
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
|
||||
await act(async () => {
|
||||
root?.render(
|
||||
React.createElement(
|
||||
EditableCell,
|
||||
{
|
||||
value: 'active',
|
||||
editor: 'select',
|
||||
options: [
|
||||
{ value: 'active', label: '启用' },
|
||||
{ value: 'disabled', label: '停用' },
|
||||
],
|
||||
onSave,
|
||||
},
|
||||
'启用',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
container?.querySelector('.editable-cell')?.dispatchEvent(
|
||||
new MouseEvent('dblclick', { bubbles: true }),
|
||||
);
|
||||
await flush();
|
||||
});
|
||||
|
||||
const options = Array.from(document.querySelectorAll<HTMLElement>('.ant-select-item-option'));
|
||||
const disabledOption = options.find((option) => option.textContent?.includes('停用'));
|
||||
expect(disabledOption).toBeTruthy();
|
||||
|
||||
await act(async () => {
|
||||
disabledOption?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
await flush();
|
||||
});
|
||||
|
||||
expect(onSave).toHaveBeenCalledOnce();
|
||||
expect(onSave).toHaveBeenCalledWith('disabled');
|
||||
expect(container.querySelector('.editable-cell--editing')).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user