feat(data-grid): 增加单元格 JSON 格式化入口

- 将格式化操作移至单元格编辑器字段栏,避免短窗口裁切
- 仅在可编辑 JSON 单元格中显示图标按钮与提示
- 补充格式化草稿及保存结果的交互回归测试
This commit is contained in:
Syngnat
2026-07-23 10:31:04 +08:00
parent 93b2bfef69
commit 0554b45371
2 changed files with 86 additions and 5 deletions

View File

@@ -239,6 +239,7 @@ vi.mock('@ant-design/icons', () => {
DatabaseOutlined: Icon,
NodeIndexOutlined: Icon,
ThunderboltOutlined: Icon,
FormatPainterOutlined: Icon,
};
});
@@ -2170,6 +2171,61 @@ describe('DataGrid DDL interactions', () => {
renderer!.unmount();
});
it('formats a writable JSON cell from the toolbar before saving the draft', async () => {
storeState.appearance.uiVersion = 'v2';
const compactJson = '{"billType":"YDApp","data":{"items":[{"count":100}]}}';
const formattedJson = JSON.stringify(JSON.parse(compactJson), null, 2);
const rows = [{ __gonavi_row_key__: 'row-1', id: 1, payload: compactJson }];
let renderer: ReactTestRenderer;
await act(async () => {
renderer = create(
<DataGrid
data={rows}
columnNames={['id', 'payload']}
loading={false}
tableName="orders"
dbName="main"
connectionId="conn-1"
pkColumns={['id']}
/>,
);
});
await waitForEffects();
const doubleClickSurface = renderer!.root.findAll(
(node) => typeof node.props.onDoubleClickCapture === 'function',
)[0];
await act(async () => {
doubleClickSurface.props.onDoubleClickCapture({
target: createRenderedCellTarget('row-1', 'payload'),
preventDefault: vi.fn(),
stopPropagation: vi.fn(),
});
});
const editorTitle = t('data_grid.cell_editor.title_with_column', { column: 'payload' });
const editor = renderer!.root.findByProps({ 'data-modal-title': editorTitle });
const toolbar = editor.findByProps({ 'data-grid-cell-editor-toolbar': 'true' });
const formatButton = toolbar.findByProps({ 'data-grid-cell-editor-format': 'true' });
expect(formatButton.props.disabled).not.toBe(true);
expect(textContent(editor.findByProps({ 'data-monaco-editor': 'true' }))).toBe(compactJson);
await act(async () => {
formatButton.props.onClick();
});
expect(textContent(editor.findByProps({ 'data-monaco-editor': 'true' }))).toBe(formattedJson);
expect(testRenderState.latestTableProps.dataSource[0].payload).toBe(compactJson);
await act(async () => {
findButton(renderer!, t('common.save')).props.onClick();
});
expect(testRenderState.latestTableProps.dataSource[0].payload).toBe(formattedJson);
renderer!.unmount();
});
it('preserves spaced and empty quoted column aliases when resolving a read-only cell', async () => {
storeState.appearance.uiVersion = 'v2';
const columnNames = [' payload ', ''];

View File

@@ -1,8 +1,8 @@
import Modal from './common/ResizableDraggableModal';
import React from 'react';
import { Button, Checkbox, DatePicker, Form, Input, TimePicker } from 'antd';
import { Button, Checkbox, DatePicker, Form, Input, TimePicker, Tooltip } from 'antd';
import dayjs from 'dayjs';
import { CopyOutlined } from '@ant-design/icons';
import { CopyOutlined, FormatPainterOutlined } from '@ant-design/icons';
import Editor from './MonacoEditor';
import {
TEMPORAL_FORMATS,
@@ -208,13 +208,38 @@ const DataGridModals: React.FC<DataGridModalsProps> = ({
<Button key="close" type="primary" onClick={onCloseCellEditor}>{translate('common.close')}</Button>,
]
: [
<Button key="format" onClick={onFormatJsonInEditor} disabled={!cellEditorIsJson}>{translate('data_grid.json_editor.format')}</Button>,
<Button key="cancel" onClick={onCloseCellEditor}>{translate('common.cancel')}</Button>,
<Button key="ok" type="primary" onClick={onSaveCellEditor}>{translate('common.save')}</Button>,
]}
>
<div style={{ marginBottom: 8, color: '#888', fontSize: 12 }}>
{cellEditorMeta ? `${tableName || ''}${tableName ? '.' : ''}${cellEditorMeta.dataIndex}` : ''}
<div
data-grid-cell-editor-toolbar="true"
style={{
marginBottom: 8,
color: '#888',
fontSize: 12,
display: 'flex',
alignItems: 'center',
gap: 8,
}}
>
<span style={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{cellEditorMeta ? `${tableName || ''}${tableName ? '.' : ''}${cellEditorMeta.dataIndex}` : ''}
</span>
<span style={{ flex: 1 }} />
{!cellEditorReadOnly && cellEditorIsJson && (
<Tooltip title={translate('data_grid.json_editor.format')}>
<Button
data-grid-cell-editor-format="true"
size="small"
icon={<FormatPainterOutlined aria-hidden="true" />}
aria-label={translate('data_grid.json_editor.format')}
onClick={onFormatJsonInEditor}
>
{translate('data_grid.json_editor.format')}
</Button>
</Tooltip>
)}
</div>
{cellEditorOpen && (
<Editor