From e549d1382a6442695fd37f39b9d1468a6925872f Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Mon, 17 Jun 2024 16:11:06 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat(text-align-panel):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=96=87=E5=AD=97=E5=AF=B9=E9=BD=90=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__snapshots__/index-spec.tsx.snap | 9 + .../common/tooltip-wrapper/index-spec.tsx | 35 + .../__snapshots__/index-spec.tsx.snap | 866 ++++++++++++++++++ .../config/text-align-panel/index-spec.tsx | 93 ++ .../s2-react-components/playground/index.less | 5 + .../s2-react-components/playground/index.tsx | 62 +- .../s2-react-components/playground/utils.ts | 3 + .../src/common/interface.ts | 23 + .../src/components/common/index.ts | 1 + .../common/tooltip-wrapper/index.tsx | 22 + .../common/tooltip-wrapper/interface.ts | 3 + .../src/components/config/index.ts | 2 + .../config/text-align-panel/icons.tsx | 221 +++++ .../config/text-align-panel/index.less | 26 + .../config/text-align-panel/index.tsx | 115 +++ .../config/text-align-panel/interface.ts | 25 + .../config/text-align-panel/utils.ts | 58 ++ .../components/config/theme-panel/index.tsx | 28 +- .../config/theme-panel/interface.ts | 22 +- 19 files changed, 1558 insertions(+), 61 deletions(-) create mode 100644 packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap create mode 100644 packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/index-spec.tsx create mode 100644 packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap create mode 100644 packages/s2-react-components/__tests__/unit/components/config/text-align-panel/index-spec.tsx create mode 100644 packages/s2-react-components/src/common/interface.ts create mode 100644 packages/s2-react-components/src/components/common/tooltip-wrapper/index.tsx create mode 100644 packages/s2-react-components/src/components/common/tooltip-wrapper/interface.ts create mode 100644 packages/s2-react-components/src/components/config/text-align-panel/icons.tsx create mode 100644 packages/s2-react-components/src/components/config/text-align-panel/index.less create mode 100644 packages/s2-react-components/src/components/config/text-align-panel/index.tsx create mode 100644 packages/s2-react-components/src/components/config/text-align-panel/interface.ts create mode 100644 packages/s2-react-components/src/components/config/text-align-panel/utils.ts diff --git a/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap b/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap new file mode 100644 index 0000000000..33ae4af0de --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Reset Button Component Tests should render correctly 1`] = ` + + + +`; diff --git a/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/index-spec.tsx b/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/index-spec.tsx new file mode 100644 index 0000000000..f7ef2435dc --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/index-spec.tsx @@ -0,0 +1,35 @@ +import { TooltipWrapper } from '@/components'; +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +describe('Tooltip Wrapper Component Tests', () => { + test('should render correctly', () => { + const { asFragment } = render( + , + ); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should render custom content', () => { + render( + +
自定义内容
+
, + ); + + expect(screen.getByText('自定义内容')).toBeTruthy(); + }); + + test('should show custom label when tooltip clicked', () => { + render( + +
自定义内容
+
, + ); + + fireEvent.click(screen.getByText('自定义内容')); + + expect(screen.getByText('测试')).toBeTruthy(); + }); +}); diff --git a/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap b/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap new file mode 100644 index 0000000000..185ed68547 --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap @@ -0,0 +1,866 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Text Align Panel Component Tests should default collapse panel 1`] = ` + +
+
+ +
+
+
+`; + +exports[`Text Align Panel Component Tests should render correctly panel 1`] = ` + +
+
+
+
+ + + +
+ + 文字对齐 + +
+ + + + + + 重置 + + +
+
+
+
+
+ + 表头 + + +
+ + + +
+
+
+
+ + 表身 (维度) + + +
+ + + +
+
+
+
+ + 表身 (指标) + + +
+ + + +
+
+
+
+
+
+
+
+`; diff --git a/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/index-spec.tsx b/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/index-spec.tsx new file mode 100644 index 0000000000..a42f43d1d8 --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/index-spec.tsx @@ -0,0 +1,93 @@ +import { TextAlignPanel } from '@/components'; +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +describe('Text Align Panel Component Tests', () => { + test('should render correctly panel', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + expect(screen.getByText('文字对齐')).toBeDefined(); + }); + + test('should render custom panel title', () => { + const title = '自定义标题'; + + render(); + + expect(screen.getByText(title)).toBeDefined(); + }); + + test('should default collapse panel', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should render custom content', () => { + const { container } = render( + + content + , + ); + + expect(container.querySelector('.custom-content')).toBeDefined(); + }); + + test('should trigger onReset event', () => { + const onRest = jest.fn(); + + render(); + + fireEvent.click(screen.getByText('重置')); + + expect(onRest).toHaveReturnedTimes(1); + expect(onRest).toHaveBeenCalledWith( + { + dataCellTextAlign: 'right', + rowCellTextAlign: 'left', + }, + expect.anything(), + expect.anything(), + ); + }); + + test('should trigger onReset event with prev options', () => { + const onRest = jest.fn(); + + const { container } = render(); + + fireEvent.click(container.querySelectorAll('.ant-radio-button')[1]!); + fireEvent.click(screen.getByText('重置')); + + expect(onRest).toHaveBeenCalledWith( + { + dataCellTextAlign: 'right', + rowCellTextAlign: 'left', + }, + { + colCellTextAlign: 'center', + dataCellTextAlign: 'center', + rowCellTextAlign: 'left', + }, + expect.anything(), + ); + }); + + test('should trigger onChange event', () => { + const onChange = jest.fn(); + + const { container } = render(); + + fireEvent.click(container.querySelectorAll('.ant-radio-button')[1]!); + + expect(onChange).toHaveBeenLastCalledWith( + { + colCellTextAlign: 'center', + dataCellTextAlign: 'center', + rowCellTextAlign: 'left', + }, + expect.anything(), + ); + }); +}); diff --git a/packages/s2-react-components/playground/index.less b/packages/s2-react-components/playground/index.less index 2c9c71e896..201e4766fb 100644 --- a/packages/s2-react-components/playground/index.less +++ b/packages/s2-react-components/playground/index.less @@ -2,4 +2,9 @@ padding: 40px; background-color: var(--antv-s2-background, #fff); color: var(--antv-s2-font, #fff); + + .config { + display: flex; + align-items: flex-start; + } } diff --git a/packages/s2-react-components/playground/index.tsx b/packages/s2-react-components/playground/index.tsx index bb41bdf42f..480d349d3f 100644 --- a/packages/s2-react-components/playground/index.tsx +++ b/packages/s2-react-components/playground/index.tsx @@ -14,11 +14,12 @@ import { version as AntdVersion, Space, Tag } from 'antd'; import React from 'react'; import { createRoot } from 'react-dom/client'; import pkg from '../package.json'; -import { ThemePanel } from '../src'; +import { TextAlignPanel, ThemePanel } from '../src'; import { s2DataConfig, s2Options } from './config'; import '@antv/s2-react/dist/style.min.css'; import './index.less'; +import { onSheetMounted } from './utils'; function MainLayout() { const s2Ref = React.useRef(); @@ -29,12 +30,48 @@ function MainLayout() { return (
+ + { + setThemeCfg({ + name: options.themeType as ThemeName, + theme, + }); + s2Ref.current?.setOptions({ + hierarchyType: options.hierarchyType, + }); + s2Ref.current?.render(false); + console.log('onChange:', options, theme); + }} + onReset={(options, prevOptions, theme) => { + console.log('onReset:', options, prevOptions, theme); + }} + /> + { + setThemeCfg({ + theme, + }); + s2Ref.current?.render(false); + console.log('onChange:', options, theme); + }} + onReset={(options, prevOptions, theme) => { + console.log('onReset:', options, prevOptions, theme); + }} + /> + {pkg.name} playground @@ -63,29 +100,6 @@ function MainLayout() { advancedSort: { open: true, }, - extra: ( - <> - { - setThemeCfg({ - name: options.themeType as ThemeName, - theme, - }); - s2Ref.current?.setOptions({ - hierarchyType: options.hierarchyType, - }); - s2Ref.current?.render(false); - console.log('onChange:', options, theme); - }} - onReset={(options, prevOptions, theme) => { - console.log('onReset:', options, prevOptions, theme); - }} - /> - - ), }} /> diff --git a/packages/s2-react-components/playground/utils.ts b/packages/s2-react-components/playground/utils.ts index c638769034..648974b128 100644 --- a/packages/s2-react-components/playground/utils.ts +++ b/packages/s2-react-components/playground/utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ /* eslint-disable no-restricted-imports */ /* eslint-disable no-console */ import type { SpreadSheet } from '@antv/s2'; @@ -9,5 +10,7 @@ export const onSheetMounted = (s2: SpreadSheet) => { window.s2 = s2; // @ts-ignore window.g_instances = [s2.container]; + // @ts-ignore + window.__g_instances__ = [s2.container]; window._ = _; }; diff --git a/packages/s2-react-components/src/common/interface.ts b/packages/s2-react-components/src/common/interface.ts new file mode 100644 index 0000000000..4d0b6e1d6f --- /dev/null +++ b/packages/s2-react-components/src/common/interface.ts @@ -0,0 +1,23 @@ +export interface BaseComponentProps> { + /** + * 标题 + * @default "文字对齐" + */ + title?: React.ReactNode; + + /** + * 默认是否折叠 + * @default false + */ + defaultCollapsed?: boolean; + + /** + * 默认配置 + */ + defaultOptions?: Partial; + + /** + * 子节点 + */ + children?: React.ReactNode; +} diff --git a/packages/s2-react-components/src/components/common/index.ts b/packages/s2-react-components/src/components/common/index.ts index d049c2ca03..86405fe89b 100644 --- a/packages/s2-react-components/src/components/common/index.ts +++ b/packages/s2-react-components/src/components/common/index.ts @@ -1,3 +1,4 @@ export { RadioGroup } from './radio-group'; export { ResetButton } from './reset-button'; export { ResetGroup } from './reset-group'; +export { TooltipWrapper } from './tooltip-wrapper'; diff --git a/packages/s2-react-components/src/components/common/tooltip-wrapper/index.tsx b/packages/s2-react-components/src/components/common/tooltip-wrapper/index.tsx new file mode 100644 index 0000000000..a6dadabc9f --- /dev/null +++ b/packages/s2-react-components/src/components/common/tooltip-wrapper/index.tsx @@ -0,0 +1,22 @@ +import { S2_PREFIX_CLS } from '@antv/s2'; +import { Tooltip } from 'antd'; +import cls from 'classnames'; +import React from 'react'; +import type { TooltipWrapperProps } from './interface'; + +const PRE_CLASS = `${S2_PREFIX_CLS}-tooltip-wrapper`; + +export const TooltipWrapper: React.FC = React.memo( + (props) => { + const { title, children, className, ...attrs } = props; + + // 增加 <> 用于 Tooltip 绑定事件 + return ( + + <>{children} + + ); + }, +); + +TooltipWrapper.displayName = 'TooltipWrapper'; diff --git a/packages/s2-react-components/src/components/common/tooltip-wrapper/interface.ts b/packages/s2-react-components/src/components/common/tooltip-wrapper/interface.ts new file mode 100644 index 0000000000..b869fbcf5c --- /dev/null +++ b/packages/s2-react-components/src/components/common/tooltip-wrapper/interface.ts @@ -0,0 +1,3 @@ +import type { TooltipPropsWithTitle } from 'antd/es/tooltip'; + +export interface TooltipWrapperProps extends TooltipPropsWithTitle {} diff --git a/packages/s2-react-components/src/components/config/index.ts b/packages/s2-react-components/src/components/config/index.ts index b023819a71..c9ec76c853 100644 --- a/packages/s2-react-components/src/components/config/index.ts +++ b/packages/s2-react-components/src/components/config/index.ts @@ -2,4 +2,6 @@ export { ThemePanel } from './theme-panel'; export { ColorBox } from './theme-panel/color-box'; export { ColorPickerPanel } from './theme-panel/color-picker-panel'; +export { TextAlignPanel } from './text-align-panel'; + export * from './theme-panel/interface'; diff --git a/packages/s2-react-components/src/components/config/text-align-panel/icons.tsx b/packages/s2-react-components/src/components/config/text-align-panel/icons.tsx new file mode 100644 index 0000000000..89dbf25094 --- /dev/null +++ b/packages/s2-react-components/src/components/config/text-align-panel/icons.tsx @@ -0,0 +1,221 @@ +import React from 'react'; + +type RadioIconProps = { + active: boolean; +}; + +export const LeftAlignIcon = React.memo(({ active }) => { + if (active) { + return ( + + + + + + + + + + + + + + + + ); + } + + return ( + + + + + + + + + + + + + + + + ); +}); + +export const CenterAlignIcon = React.memo(({ active }) => { + if (active) { + return ( + + + + + + + + + + + + + + + + + + ); + } + + return ( + + + + + + + + + + + + + + + + + + ); +}); + +export const RightAlignIcon = React.memo(({ active }) => { + if (active) { + return ( + + + + + + + + + + + + + + + + + + ); + } + + return ( + + + + + + + + + + + + + + + + + + ); +}); diff --git a/packages/s2-react-components/src/components/config/text-align-panel/index.less b/packages/s2-react-components/src/components/config/text-align-panel/index.less new file mode 100644 index 0000000000..01418a82b2 --- /dev/null +++ b/packages/s2-react-components/src/components/config/text-align-panel/index.less @@ -0,0 +1,26 @@ +@import '@antv/s2/src/styles/variables.less'; + +.@{s2-cls-prefix}-theme-panel-custom-color { + display: flex; + align-items: center; + padding-top: 10px; + + &::before { + position: relative; + display: block; + flex: 1; + content: ''; + } + + > div { + display: flex; + flex: 2; + align-items: center; + } + + &-title { + margin-right: 20px; + color: rgba(0, 0, 0, 0.45); + font-size: 12px; + } +} diff --git a/packages/s2-react-components/src/components/config/text-align-panel/index.tsx b/packages/s2-react-components/src/components/config/text-align-panel/index.tsx new file mode 100644 index 0000000000..e0f1a42392 --- /dev/null +++ b/packages/s2-react-components/src/components/config/text-align-panel/index.tsx @@ -0,0 +1,115 @@ +import { S2_PREFIX_CLS, i18n } from '@antv/s2'; +import { type RadioChangeEvent } from 'antd'; +import React from 'react'; +import { ResetGroup, TooltipWrapper } from '../../common'; +import { RadioGroup } from '../../common/radio-group'; +import type { RadioGroupProps } from '../../common/radio-group/interface'; +import { CenterAlignIcon, LeftAlignIcon, RightAlignIcon } from './icons'; +import './index.less'; +import type { TextAlignPanelOptions, TextAlignPanelProps } from './interface'; +import { generateCellTextAlignTheme } from './utils'; + +const PRE_CLASS = `${S2_PREFIX_CLS}-text-align-panel`; + +export const TextAlignPanel: React.FC = React.memo( + (props) => { + const { + title = i18n('文字对齐'), + defaultOptions: defaultTextAlignPanelOptions, + defaultCollapsed = false, + children, + onChange, + onReset, + } = props; + const [options, setOptions] = React.useState({ + rowCellTextAlign: 'left', + dataCellTextAlign: 'right', + ...defaultTextAlignPanelOptions, + }); + const defaultOptions = React.useRef(options); + + const onResetClick = () => { + const theme = generateCellTextAlignTheme(defaultOptions.current); + + setOptions(defaultOptions.current); + onReset?.(defaultOptions.current, options, theme!); + }; + + const onOptionsChange = + (field: keyof TextAlignPanelOptions) => (e: RadioChangeEvent) => { + const newOptions: TextAlignPanelOptions = { + ...options, + [field]: e.target.value, + }; + + // 指标和列头对齐 (含 EXTRA_FIELD 虚拟列), 可单独调整指标 + if (field === 'colCellTextAlign') { + newOptions.dataCellTextAlign = newOptions.colCellTextAlign; + } + + setOptions(newOptions); + }; + + const getCellAlignOptions = ( + field: keyof TextAlignPanelOptions, + ): RadioGroupProps['options'] => + [ + { label: i18n('左对齐'), value: 'left', component: LeftAlignIcon }, + { label: i18n('居中'), value: 'center', component: CenterAlignIcon }, + { label: i18n('右对齐'), value: 'right', component: RightAlignIcon }, + ].map(({ label, value, component: Component }) => { + return { + label: ( + + + + ), + value, + }; + }); + + const getRadioGroupProps = ( + field: keyof TextAlignPanelOptions, + ): Partial => { + return { + optionType: 'button', + value: options[field], + onChange: onOptionsChange(field), + options: getCellAlignOptions(field), + onlyIcon: true, + }; + }; + + React.useEffect(() => { + const theme = generateCellTextAlignTheme(options); + + onChange?.(options, theme!); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [options]); + + return ( + + {children} + + + + + ); + }, +); + +TextAlignPanel.displayName = 'TextAlignPanel'; diff --git a/packages/s2-react-components/src/components/config/text-align-panel/interface.ts b/packages/s2-react-components/src/components/config/text-align-panel/interface.ts new file mode 100644 index 0000000000..c381892979 --- /dev/null +++ b/packages/s2-react-components/src/components/config/text-align-panel/interface.ts @@ -0,0 +1,25 @@ +import type { S2Theme, TextAlign } from '@antv/s2'; +import type { BaseComponentProps } from '../../../common/interface'; + +export interface TextAlignPanelOptions { + rowCellTextAlign?: TextAlign; + colCellTextAlign?: TextAlign; + dataCellTextAlign?: TextAlign; +} + +export interface TextAlignPanelProps + extends BaseComponentProps { + /** + * 选择 + */ + onChange?: (options: TextAlignPanelOptions, theme: S2Theme) => void; + + /** + * 重置 + */ + onReset?: ( + options: TextAlignPanelOptions, + prevOptions: TextAlignPanelOptions, + theme: S2Theme, + ) => void; +} diff --git a/packages/s2-react-components/src/components/config/text-align-panel/utils.ts b/packages/s2-react-components/src/components/config/text-align-panel/utils.ts new file mode 100644 index 0000000000..4733c9bcb2 --- /dev/null +++ b/packages/s2-react-components/src/components/config/text-align-panel/utils.ts @@ -0,0 +1,58 @@ +import type { S2Theme } from '@antv/s2'; +import type { TextAlignPanelOptions } from './interface'; + +/** + * 生成表格文本对齐方式主题配置 + * @see https://s2.antv.antgroup.com/zh/docs/api/general/S2Theme + */ +export function generateCellTextAlignTheme( + options: TextAlignPanelOptions, +): S2Theme { + const { + colCellTextAlign: colCellAlignType, + rowCellTextAlign: rowCellAlignType = 'left', + dataCellTextAlign: dataCellAlignType = 'right', + } = options; + + return { + // 角头取列头的对齐方式, 但底表默认行角头是靠左, 列角头是靠右, 所以没有指定默认值时默认按底表对齐方式展示. + cornerCell: { + text: { + textAlign: options?.colCellTextAlign || 'left', + }, + bolderText: { + textAlign: options?.colCellTextAlign || 'right', + }, + }, + colCell: { + text: { + textAlign: colCellAlignType, + }, + bolderText: { + textAlign: colCellAlignType, + }, + measureText: { + textAlign: dataCellAlignType, + }, + }, + rowCell: { + text: { + textAlign: rowCellAlignType, + }, + bolderText: { + textAlign: rowCellAlignType, + }, + measureText: { + textAlign: rowCellAlignType, + }, + }, + dataCell: { + text: { + textAlign: dataCellAlignType, + }, + bolderText: { + textAlign: dataCellAlignType, + }, + }, + }; +} diff --git a/packages/s2-react-components/src/components/config/theme-panel/index.tsx b/packages/s2-react-components/src/components/config/theme-panel/index.tsx index 960852f7b0..43f12c1853 100644 --- a/packages/s2-react-components/src/components/config/theme-panel/index.tsx +++ b/packages/s2-react-components/src/components/config/theme-panel/index.tsx @@ -1,17 +1,12 @@ import { S2_PREFIX_CLS, i18n } from '@antv/s2'; -import { - Popover, - Tooltip, - type RadioChangeEvent, - type RadioGroupProps, -} from 'antd'; +import { Popover, type RadioChangeEvent, type RadioGroupProps } from 'antd'; import React from 'react'; import { DEFAULT_THEME_COLOR_LIST, SheetThemeColorType, SheetThemeType, } from '../../../common'; -import { ResetGroup } from '../../common'; +import { ResetGroup, TooltipWrapper } from '../../common'; import { RadioGroup } from '../../common/radio-group'; import { ColorBox } from './color-box'; import { ColorPickerPanel } from './color-picker-panel'; @@ -108,10 +103,6 @@ export const ThemePanel: React.FC = React.memo((props) => { setOptions(newOptions); }; - const renderIcon = (label: React.ReactNode, Component: React.ReactNode) => { - return {Component}; - }; - React.useEffect(() => { const theme = generateColorTheme({ themeType: options.themeType, @@ -128,9 +119,10 @@ export const ThemePanel: React.FC = React.memo((props) => { { label: i18n('树状'), value: 'tree', component: HierarchyTreeTypeIcon }, ].map(({ label, value, component: Component }) => { return { - label: renderIcon( - label, - , + label: ( + + + ), value, }; @@ -158,9 +150,10 @@ export const ThemePanel: React.FC = React.memo((props) => { component: ZebraThemeIcon, }, ].map(({ label, value, component: Component }) => ({ - label: renderIcon( - label, - , + label: ( + + + ), value, })); @@ -190,6 +183,7 @@ export const ThemePanel: React.FC = React.memo((props) => { title={title} onResetClick={onResetClick} defaultCollapsed={defaultCollapsed} + className={PRE_CLASS} > {children} { /** * 历史自定义颜色记录上限 * @default 5 @@ -29,17 +22,6 @@ export interface ThemePanelProps { */ disableCustomPrimaryColorPicker?: boolean; - /** - * 默认是否折叠 - * @default false - */ - defaultCollapsed?: boolean; - - /** - * 默认配置 - */ - defaultOptions?: Partial; - /** * 选择 */ From 6dea04055939f688a1407edf5d872df57425eedd Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Mon, 17 Jun 2024 16:17:48 +0800 Subject: [PATCH 2/8] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=BF=AB?= =?UTF-8?q?=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__snapshots__/index-spec.tsx.snap | 2 +- .../__snapshots__/index-spec.tsx.snap | 500 +++++++++--------- 2 files changed, 263 insertions(+), 239 deletions(-) diff --git a/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap b/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap index 33ae4af0de..66c21a53bf 100644 --- a/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap +++ b/packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Reset Button Component Tests should render correctly 1`] = ` +exports[`Tooltip Wrapper Component Tests should render correctly 1`] = `
- - - - + + + + - - + +
@@ -347,56 +355,60 @@ exports[`Theme Panel Component Tests should render correctly panel 1`] = ` /> - - - - - - + + + + + + - - + +
From 4ec3a4b4360f69a896e94bf82befb8872ac4c9f1 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Tue, 18 Jun 2024 10:28:48 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=9B=BD?= =?UTF-8?q?=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../s2-react-components/src/common/index.ts | 1 + .../{interface.ts => interface/components.ts} | 2 +- .../src/common/interface/icon.ts | 3 ++ .../src/common/interface/index.ts | 2 + .../config/text-align-panel/icons.tsx | 53 +++++++++++++++---- .../config/text-align-panel/interface.ts | 2 +- .../components/config/theme-panel/icons.tsx | 5 +- .../config/theme-panel/interface.ts | 2 +- packages/s2-shared/src/constant/i18n/en_US.ts | 7 +++ packages/s2-shared/src/constant/i18n/zh_CN.ts | 7 +++ 10 files changed, 67 insertions(+), 17 deletions(-) rename packages/s2-react-components/src/common/{interface.ts => interface/components.ts} (80%) create mode 100644 packages/s2-react-components/src/common/interface/icon.ts create mode 100644 packages/s2-react-components/src/common/interface/index.ts diff --git a/packages/s2-react-components/src/common/index.ts b/packages/s2-react-components/src/common/index.ts index c94f80f843..f4437a563d 100644 --- a/packages/s2-react-components/src/common/index.ts +++ b/packages/s2-react-components/src/common/index.ts @@ -1 +1,2 @@ export * from './constants'; +export * from './interface'; diff --git a/packages/s2-react-components/src/common/interface.ts b/packages/s2-react-components/src/common/interface/components.ts similarity index 80% rename from packages/s2-react-components/src/common/interface.ts rename to packages/s2-react-components/src/common/interface/components.ts index 4d0b6e1d6f..bf5d49a0ab 100644 --- a/packages/s2-react-components/src/common/interface.ts +++ b/packages/s2-react-components/src/common/interface/components.ts @@ -1,4 +1,4 @@ -export interface BaseComponentProps> { +export interface BaseComponentProps { /** * 标题 * @default "文字对齐" diff --git a/packages/s2-react-components/src/common/interface/icon.ts b/packages/s2-react-components/src/common/interface/icon.ts new file mode 100644 index 0000000000..d7b00db762 --- /dev/null +++ b/packages/s2-react-components/src/common/interface/icon.ts @@ -0,0 +1,3 @@ +export interface RadioIconProps { + active: boolean; +} diff --git a/packages/s2-react-components/src/common/interface/index.ts b/packages/s2-react-components/src/common/interface/index.ts new file mode 100644 index 0000000000..fdc62132bc --- /dev/null +++ b/packages/s2-react-components/src/common/interface/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +export * from './icon'; diff --git a/packages/s2-react-components/src/components/config/text-align-panel/icons.tsx b/packages/s2-react-components/src/components/config/text-align-panel/icons.tsx index 89dbf25094..48802ca984 100644 --- a/packages/s2-react-components/src/components/config/text-align-panel/icons.tsx +++ b/packages/s2-react-components/src/components/config/text-align-panel/icons.tsx @@ -1,13 +1,16 @@ import React from 'react'; - -type RadioIconProps = { - active: boolean; -}; +import type { RadioIconProps } from '../../../common/interface/icon'; export const LeftAlignIcon = React.memo(({ active }) => { if (active) { return ( - + (({ active }) => { } return ( - + (({ active }) => { export const CenterAlignIcon = React.memo(({ active }) => { if (active) { return ( - + (({ active }) => { } return ( - + (({ active }) => { export const RightAlignIcon = React.memo(({ active }) => { if (active) { return ( - + (({ active }) => { } return ( - + ( ({ active }) => { diff --git a/packages/s2-react-components/src/components/config/theme-panel/interface.ts b/packages/s2-react-components/src/components/config/theme-panel/interface.ts index f4ea32bea9..82371a278d 100644 --- a/packages/s2-react-components/src/components/config/theme-panel/interface.ts +++ b/packages/s2-react-components/src/components/config/theme-panel/interface.ts @@ -1,7 +1,7 @@ import type { S2Theme } from '@antv/s2'; import type { SheetComponentOptions } from '@antv/s2-react'; import type { SheetThemeColorType, SheetThemeType } from '../../../common'; -import type { BaseComponentProps } from '../../../common/interface'; +import type { BaseComponentProps } from '../../../common/interface/components'; export interface ThemePanelOptions { hierarchyType: SheetComponentOptions['hierarchyType']; diff --git a/packages/s2-shared/src/constant/i18n/en_US.ts b/packages/s2-shared/src/constant/i18n/en_US.ts index 0def203873..4dae05aa97 100644 --- a/packages/s2-shared/src/constant/i18n/en_US.ts +++ b/packages/s2-shared/src/constant/i18n/en_US.ts @@ -62,4 +62,11 @@ export const EN_US: Record = { 浅色主题: 'Light theme', 灰色: 'Gray', 自定义: 'Custom', + 文字对齐: 'Text align', + 左对齐: 'Align left', + 居中: 'Align center', + 右对齐: 'Align right', + 表头: 'Header', + '表身 (维度)': 'Body(dimension)', + '表身 (指标)': 'Body(measure)', }; diff --git a/packages/s2-shared/src/constant/i18n/zh_CN.ts b/packages/s2-shared/src/constant/i18n/zh_CN.ts index 2121698959..59bc4a4086 100644 --- a/packages/s2-shared/src/constant/i18n/zh_CN.ts +++ b/packages/s2-shared/src/constant/i18n/zh_CN.ts @@ -62,4 +62,11 @@ export const ZH_CN: Record = { 浅色主题: '浅色主题', 灰色: '灰色', 自定义: '自定义', + 文字对齐: '文字对齐', + 左对齐: '左对齐', + 居中: '居中', + 右对齐: '右对齐', + 表头: '表头', + '表身 (维度)': '表身 (维度)', + '表身 (指标)': '表身 (指标)', }; From e69327cd1ef1019e72999121affdb0c51719e083 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 19 Jun 2024 10:25:40 +0800 Subject: [PATCH 4/8] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=BF=AB?= =?UTF-8?q?=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../text-align-panel/__snapshots__/index-spec.tsx.snap | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap b/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap index 185ed68547..3d70658f7d 100644 --- a/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap +++ b/packages/s2-react-components/__tests__/unit/components/config/text-align-panel/__snapshots__/index-spec.tsx.snap @@ -199,6 +199,7 @@ exports[`Text Align Panel Component Tests should render correctly panel 1`] = ` > Date: Wed, 19 Jun 2024 16:30:30 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat(frozen-panel):=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=A1=8C=E5=88=97=E5=A4=B4=E5=86=BB=E7=BB=93=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../s2-core/src/common/interface/s2Options.ts | 15 +- .../__snapshots__/index-spec.tsx.snap | 2112 +++++++++++++++++ .../config/frozen-panel/index-spec.tsx | 165 ++ .../s2-react-components/playground/config.tsx | 77 +- .../s2-react-components/playground/index.tsx | 32 +- .../frozen-input-number/index.tsx | 46 + .../frozen-input-number/interface.ts | 7 + .../components/config/frozen-panel/index.less | 34 + .../components/config/frozen-panel/index.tsx | 141 ++ .../config/frozen-panel/interface.ts | 44 + .../src/components/config/index.ts | 1 + 11 files changed, 2599 insertions(+), 75 deletions(-) create mode 100644 packages/s2-react-components/__tests__/unit/components/config/frozen-panel/__snapshots__/index-spec.tsx.snap create mode 100644 packages/s2-react-components/__tests__/unit/components/config/frozen-panel/index-spec.tsx create mode 100644 packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/index.tsx create mode 100644 packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/interface.ts create mode 100644 packages/s2-react-components/src/components/config/frozen-panel/index.less create mode 100644 packages/s2-react-components/src/components/config/frozen-panel/index.tsx create mode 100644 packages/s2-react-components/src/components/config/frozen-panel/interface.ts diff --git a/packages/s2-core/src/common/interface/s2Options.ts b/packages/s2-core/src/common/interface/s2Options.ts index 7b3d296644..0eb22ef77e 100644 --- a/packages/s2-core/src/common/interface/s2Options.ts +++ b/packages/s2-core/src/common/interface/s2Options.ts @@ -353,18 +353,21 @@ export interface S2PivotSheetOptions { cornerExtraFieldText?: string; } -export interface S2Options< - T = TooltipContentType, - P = Pagination, - Menu = BaseTooltipOperatorMenuOptions, -> extends S2BasicOptions, - S2PivotSheetOptions { +export interface S2FrozenOptions { /** * 行列冻结 */ frozen?: S2PivotSheetFrozenOptions & S2BaseFrozenOptions; } +export interface S2Options< + T = TooltipContentType, + P = Pagination, + Menu = BaseTooltipOperatorMenuOptions, +> extends S2BasicOptions, + S2PivotSheetOptions, + S2FrozenOptions {} + /** * 自定义渲染模式 */ diff --git a/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/__snapshots__/index-spec.tsx.snap b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/__snapshots__/index-spec.tsx.snap new file mode 100644 index 0000000000..1f0b362735 --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/__snapshots__/index-spec.tsx.snap @@ -0,0 +1,2112 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Frozen Panel Component Tests should default collapse panel 1`] = ` + +
+
+ +
+
+ +`; + +exports[`Frozen Panel Component Tests should hidden frozen col 1`] = ` + +
+
+
+
+ + + +
+ + 冻结行列头 + +
+ + + + + + 重置 + + +
+
+
+
+
+ +
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+
+
+
+
+
+
+`; + +exports[`Frozen Panel Component Tests should hidden frozen row 1`] = ` + +
+
+
+
+ + + +
+ + 冻结行列头 + +
+ + + + + + 重置 + + +
+
+
+
+
+ +
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+
+
+
+
+
+
+`; + +exports[`Frozen Panel Component Tests should hidden frozen row header 1`] = ` + +
+
+
+
+ + + +
+ + 冻结行列头 + +
+ + + + + + 重置 + + +
+
+
+
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+
+
+
+
+
+
+`; + +exports[`Frozen Panel Component Tests should render correctly panel 1`] = ` + +
+
+
+
+ + + +
+ + 冻结行列头 + +
+ + + + + + 重置 + + +
+
+
+
+
+ +
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+
+
+
+
+
+
+`; + +exports[`Frozen Panel Component Tests should set custom input number props 1`] = ` + +
+
+
+
+ + + +
+ + 冻结行列头 + +
+ + + + + + 重置 + + +
+
+
+
+
+ +
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 行 +
+
+
+ + + 冻结前 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+ + 冻结后 +
+
+ + + + + + + + + + +
+
+ +
+
+ 列 +
+
+
+
+
+
+
+`; diff --git a/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/index-spec.tsx b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/index-spec.tsx new file mode 100644 index 0000000000..aae9207ba0 --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/index-spec.tsx @@ -0,0 +1,165 @@ +import { FrozenPanel } from '@/components'; +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +describe('Frozen Panel Component Tests', () => { + test('should render correctly panel', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + expect(screen.getByText('冻结行列头')).toBeDefined(); + expect(screen.getByText('冻结行头')).toBeDefined(); + expect(screen.getByText('冻结行')).toBeDefined(); + expect(screen.getByText('冻结列')).toBeDefined(); + }); + + test('should render custom panel title', () => { + const title = '自定义标题'; + + render(); + + expect(screen.getByText(title)).toBeDefined(); + }); + + test('should default collapse panel', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should render custom content', () => { + const { container } = render( + + content + , + ); + + expect(container.querySelector('.custom-content')).toBeDefined(); + }); + + test('should hidden frozen row header', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should hidden frozen row', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should hidden frozen col', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should set custom input number props', () => { + const { asFragment } = render( + , + ); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should trigger onReset event', () => { + const onRest = jest.fn(); + + render(); + + fireEvent.click(screen.getByText('重置')); + + expect(onRest).toHaveReturnedTimes(1); + expect(onRest).toHaveBeenCalledWith( + { + frozenCol: [], + frozenRow: [], + frozenRowHeader: true, + }, + expect.anything(), + ); + }); + + test('should trigger onReset event with prev options', () => { + const onRest = jest.fn(); + + render(); + + fireEvent.click(screen.getByText('冻结行头')); + fireEvent.click(screen.getByText('冻结行')); + fireEvent.click(screen.getByText('冻结列')); + fireEvent.click(screen.getByText('重置')); + + expect(onRest).toHaveBeenCalledWith( + { + frozenCol: [], + frozenRow: [], + frozenRowHeader: true, + }, + { + frozenCol: [1, 1], + frozenRow: [1, 1], + frozenRowHeader: false, + }, + ); + }); + + test('should trigger onChange event', () => { + const onChange = jest.fn(); + + render(); + + fireEvent.click(screen.getByText('冻结行头')); + + expect(onChange).toHaveBeenLastCalledWith({ + frozenCol: [], + frozenRow: [], + frozenRowHeader: false, + }); + + fireEvent.click(screen.getByText('冻结行')); + + expect(onChange).toHaveBeenLastCalledWith({ + frozenCol: [], + frozenRow: [1, 1], + frozenRowHeader: false, + }); + + fireEvent.click(screen.getByText('冻结列')); + + expect(onChange).toHaveBeenLastCalledWith({ + frozenCol: [1, 1], + frozenRow: [1, 1], + frozenRowHeader: false, + }); + }); + + test('should disable input number', () => { + const { container } = render(); + + expect([ + ...container.querySelectorAll('.ant-input-number-disabled'), + ]).toHaveLength(4); + }); + + test('should enable input number', () => { + const { container } = render( + , + ); + + expect([ + ...container.querySelectorAll('.ant-input-number-disabled'), + ]).toHaveLength(0); + }); +}); diff --git a/packages/s2-react-components/playground/config.tsx b/packages/s2-react-components/playground/config.tsx index f5a278e142..8afe9ef91b 100644 --- a/packages/s2-react-components/playground/config.tsx +++ b/packages/s2-react-components/playground/config.tsx @@ -1,6 +1,6 @@ /* eslint-disable max-classes-per-file */ /* eslint-disable no-console */ -import { EMPTY_PLACEHOLDER, ResizeType, type S2DataConfig } from '@antv/s2'; +import { type S2DataConfig } from '@antv/s2'; import type { SheetComponentOptions } from '@antv/s2-react'; import { data, @@ -21,80 +21,21 @@ export const s2Options: SheetComponentOptions = { width: 800, height: 600, hierarchyType: 'grid', - placeholder: { - cell: EMPTY_PLACEHOLDER, - empty: { - icon: 'Empty', - description: '暂无数据', - }, - }, - seriesNumber: { - enable: false, - }, - transformCanvasConfig() { - return { - supportsCSSTransform: true, - // devicePixelRatio: 3, - // cursor: 'crosshair', - }; - }, frozen: { rowHeader: true, - // rowCount: 1, - // trailingRowCount: 1, - // colCount: 1, - // trailingColCount: 1, }, - cornerText: '测试测试测试测试测试测试测试测试测试测试', interaction: { - copy: { - enable: true, - withFormat: true, - withHeader: true, - }, - hoverAfterScroll: true, - hoverHighlight: true, - selectedCellHighlight: true, - selectedCellMove: true, - rangeSelection: true, // 防止 mac 触控板横向滚动触发浏览器返回, 和移动端下拉刷新 overscrollBehavior: 'none', - brushSelection: { - dataCell: true, - colCell: true, - rowCell: true, + }, + tooltip: {}, + style: { + colCell: { + width: 120, }, - resize: { - rowResizeType: ResizeType.ALL, - colResizeType: ResizeType.ALL, + dataCell: { + width: 200, + height: 100, }, }, - // totals: { - // col: { - // showGrandTotals: true, - // showSubTotals: false, - // reverseGrandTotalsLayout: true, - // reverseSubTotalsLayout: true, - // subTotalsDimensions: ['type'], - // }, - // row: { - // showGrandTotals: true, - // showSubTotals: true, - // reverseGrandTotalsLayout: true, - // reverseSubTotalsLayout: true, - // subTotalsDimensions: ['province'], - // }, - // }, - // mergedCellsInfo: [ - // [ - // { colIndex: 1, rowIndex: 1, showText: true }, - // { colIndex: 1, rowIndex: 2 }, - // ], - // [ - // { colIndex: 2, rowIndex: 1 }, - // { colIndex: 2, rowIndex: 2, showText: true }, - // ], - // ], - tooltip: {}, - style: {}, }; diff --git a/packages/s2-react-components/playground/index.tsx b/packages/s2-react-components/playground/index.tsx index 480d349d3f..51af4cd2d8 100644 --- a/packages/s2-react-components/playground/index.tsx +++ b/packages/s2-react-components/playground/index.tsx @@ -14,7 +14,7 @@ import { version as AntdVersion, Space, Tag } from 'antd'; import React from 'react'; import { createRoot } from 'react-dom/client'; import pkg from '../package.json'; -import { TextAlignPanel, ThemePanel } from '../src'; +import { FrozenPanel, TextAlignPanel, ThemePanel } from '../src'; import { s2DataConfig, s2Options } from './config'; import '@antv/s2-react/dist/style.min.css'; @@ -64,6 +64,36 @@ function MainLayout() { console.log('onReset:', options, prevOptions, theme); }} /> + { + const [rowCount = 0, trailingRowCount = 0] = options.frozenRow; + const [colCount = 0, trailingColCount = 0] = options.frozenCol; + + s2Ref.current?.setOptions({ + frozen: { + rowHeader: options.frozenRowHeader, + rowCount, + colCount, + trailingRowCount, + trailingColCount, + }, + }); + s2Ref.current?.render(false); + console.log('onChange:', options); + }} + onReset={(options, prevOptions) => { + console.log('onReset:', options, prevOptions); + }} + /> = React.memo( + (props) => { + const { disabled, value, onChange, ...attrs } = props; + const [inputValue, setInputValue] = React.useState( + value, + ); + + const onDebounceChange = debounce((nextValue) => { + onChange?.(nextValue); + }, 500); + + React.useEffect(() => { + if (value !== inputValue) { + setInputValue(value); + } + }, [value]); + + return ( + { + setInputValue(nextVal); + onDebounceChange(nextVal); + }} + /> + ); + }, +); + +FrozenInputNumber.displayName = 'FrozenInputNumber'; diff --git a/packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/interface.ts b/packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/interface.ts new file mode 100644 index 0000000000..95d1f937ce --- /dev/null +++ b/packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/interface.ts @@ -0,0 +1,7 @@ +import type { InputNumberProps } from 'antd'; + +export interface FrozenInputNumberProps + extends Omit { + value: number | null; + onChange?: (value: number) => void; +} diff --git a/packages/s2-react-components/src/components/config/frozen-panel/index.less b/packages/s2-react-components/src/components/config/frozen-panel/index.less new file mode 100644 index 0000000000..eddd64e553 --- /dev/null +++ b/packages/s2-react-components/src/components/config/frozen-panel/index.less @@ -0,0 +1,34 @@ +@import '@antv/s2/src/styles/variables.less'; + +.@{s2-cls-prefix}-frozen-panel { + width: 400px; + + &-container { + display: flex; + align-items: center; + justify-content: space-between; + + &:not(:last-of-type) { + margin-bottom: 6px; + } + + &-group { + color: rgba(0, 0, 0, 0.43); + + &:not(:last-of-type) { + margin-right: 12px; + } + } + + .ant-checkbox-wrapper { + margin-right: 50px; + color: #535455; + } + + .ant-input-number.@{s2-cls-prefix}-frozen-input-number { + width: 50px; + margin: 0 4px; + border-radius: 4px; + } + } +} diff --git a/packages/s2-react-components/src/components/config/frozen-panel/index.tsx b/packages/s2-react-components/src/components/config/frozen-panel/index.tsx new file mode 100644 index 0000000000..15dbf7e86b --- /dev/null +++ b/packages/s2-react-components/src/components/config/frozen-panel/index.tsx @@ -0,0 +1,141 @@ +import { S2_PREFIX_CLS, i18n } from '@antv/s2'; +import { Checkbox } from 'antd'; +import type { CheckboxChangeEvent } from 'antd/es/checkbox'; +import { isEmpty } from 'lodash'; +import React from 'react'; +import { ResetGroup } from '../../common'; +import { FrozenInputNumber } from './frozen-input-number'; +import './index.less'; +import type { FrozenPanelOptions, FrozenPanelProps } from './interface'; + +const PRE_CLASS = `${S2_PREFIX_CLS}-frozen-panel`; + +export const FrozenPanel: React.FC = React.memo((props) => { + const { + title = i18n('冻结行列头'), + defaultOptions: defaultTextAlignPanelOptions, + defaultCollapsed = false, + inputNumberProps, + showFrozenRowHeader = true, + showFrozenRow = true, + showFrozenCol = true, + children, + onChange, + onReset, + } = props; + const [options, setOptions] = React.useState({ + frozenRow: [], + frozenCol: [], + frozenRowHeader: true, + ...defaultTextAlignPanelOptions, + }); + const defaultOptions = React.useRef(options); + + const onResetClick = () => { + setOptions(defaultOptions.current); + onReset?.(defaultOptions.current, options); + }; + + const onRowHeaderChange = (event: CheckboxChangeEvent) => { + const newOptions: FrozenPanelOptions = { + ...options, + frozenRowHeader: event.target.checked, + }; + + setOptions(newOptions); + onChange?.(newOptions); + }; + + const BASE_FROZEN_CONFIG: Array<{ + field: keyof Omit; + suffix: string; + visible: boolean; + }> = [ + { + suffix: i18n('行'), + field: 'frozenRow' as const, + visible: showFrozenRow, + }, + { + suffix: i18n('列'), + field: 'frozenCol' as const, + visible: showFrozenCol, + }, + ].filter(({ visible }) => visible); + + return ( + + {showFrozenRowHeader && ( +
+ + {i18n('冻结行头')} + +
+ )} + {BASE_FROZEN_CONFIG.map((config) => { + const leadingCount = options[config.field]?.[0]; + const trailingCount = options[config.field]?.[1]; + const enable = !isEmpty(options[config.field]); + + const onGroupChange = (value: [number?, number?]) => { + const newOptions: FrozenPanelOptions = { + ...options, + [config.field]: value, + }; + + setOptions(newOptions); + onChange?.(newOptions); + }; + + return ( +
+ { + onGroupChange(event.target.checked ? [1, 1] : []); + }} + > + {i18n('冻结')} + {config.suffix} + + + {i18n('冻结前')} + { + onGroupChange([val, trailingCount]); + }} + {...inputNumberProps} + /> + {config.suffix} + + + {i18n('冻结后')} + { + onGroupChange([leadingCount, val]); + }} + {...inputNumberProps} + /> + {config.suffix} + +
+ ); + })} + {children} +
+ ); +}); + +FrozenPanel.displayName = 'FrozenPanel'; diff --git a/packages/s2-react-components/src/components/config/frozen-panel/interface.ts b/packages/s2-react-components/src/components/config/frozen-panel/interface.ts new file mode 100644 index 0000000000..1ea6b1d4a2 --- /dev/null +++ b/packages/s2-react-components/src/components/config/frozen-panel/interface.ts @@ -0,0 +1,44 @@ +import type { BaseComponentProps } from '../../../common/interface/components'; +import type { FrozenInputNumberProps } from './frozen-input-number/interface'; + +export interface FrozenPanelOptions { + frozenRowHeader?: boolean; + frozenRow: [number?, number?]; + frozenCol: [number?, number?]; +} + +export interface FrozenPanelProps + extends BaseComponentProps { + /** + * 透传参数 + */ + inputNumberProps?: Partial; + + /** + * 是否开启 [冻结行头] + */ + showFrozenRowHeader?: boolean; + + /** + * 是否开启 [冻结行] + */ + showFrozenRow?: boolean; + + /** + * 是否开启 [冻结列] + */ + showFrozenCol?: boolean; + + /** + * 选择 + */ + onChange?: (options: FrozenPanelOptions) => void; + + /** + * 重置 + */ + onReset?: ( + options: FrozenPanelOptions, + prevOptions: FrozenPanelOptions, + ) => void; +} diff --git a/packages/s2-react-components/src/components/config/index.ts b/packages/s2-react-components/src/components/config/index.ts index c9ec76c853..3214b31615 100644 --- a/packages/s2-react-components/src/components/config/index.ts +++ b/packages/s2-react-components/src/components/config/index.ts @@ -2,6 +2,7 @@ export { ThemePanel } from './theme-panel'; export { ColorBox } from './theme-panel/color-box'; export { ColorPickerPanel } from './theme-panel/color-picker-panel'; +export { FrozenPanel } from './frozen-panel'; export { TextAlignPanel } from './text-align-panel'; export * from './theme-panel/interface'; From cc4e7785b73447aa5623f65e9653973a18175748 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 19 Jun 2024 16:55:28 +0800 Subject: [PATCH 6/8] =?UTF-8?q?chore:=20=E5=AE=8C=E5=96=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=92=8C=E5=9B=BD=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__snapshots__/index-spec.tsx.snap | 162 ++++++++++++++++++ .../frozen-input-number/index-spec.tsx | 24 +++ .../src/components/config/index.ts | 2 + packages/s2-shared/src/constant/i18n/en_US.ts | 8 + packages/s2-shared/src/constant/i18n/zh_CN.ts | 8 + 5 files changed, 204 insertions(+) create mode 100644 packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/__snapshots__/index-spec.tsx.snap create mode 100644 packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/index-spec.tsx diff --git a/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/__snapshots__/index-spec.tsx.snap b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/__snapshots__/index-spec.tsx.snap new file mode 100644 index 0000000000..6cbb7c8463 --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/__snapshots__/index-spec.tsx.snap @@ -0,0 +1,162 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Frozen Input Number Component Tests should render correctly panel 1`] = ` + +
+
+ + + + + + + + + + +
+
+ +
+
+
+`; + +exports[`Frozen Input Number Component Tests should set custom input number props 1`] = ` + +
+
+ + + + + + + + + + +
+
+ +
+
+
+`; diff --git a/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/index-spec.tsx b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/index-spec.tsx new file mode 100644 index 0000000000..81c3281b16 --- /dev/null +++ b/packages/s2-react-components/__tests__/unit/components/config/frozen-panel/frozen-input-number/index-spec.tsx @@ -0,0 +1,24 @@ +import { FrozenInputNumber } from '@/components'; +import { render } from '@testing-library/react'; +import React from 'react'; + +describe('Frozen Input Number Component Tests', () => { + test('should render correctly panel', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); + }); + + test('should set custom input number props', () => { + const { asFragment } = render( + , + ); + + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/packages/s2-react-components/src/components/config/index.ts b/packages/s2-react-components/src/components/config/index.ts index 3214b31615..b9266ec293 100644 --- a/packages/s2-react-components/src/components/config/index.ts +++ b/packages/s2-react-components/src/components/config/index.ts @@ -3,6 +3,8 @@ export { ColorBox } from './theme-panel/color-box'; export { ColorPickerPanel } from './theme-panel/color-picker-panel'; export { FrozenPanel } from './frozen-panel'; +export { FrozenInputNumber } from './frozen-panel/frozen-input-number'; + export { TextAlignPanel } from './text-align-panel'; export * from './theme-panel/interface'; diff --git a/packages/s2-shared/src/constant/i18n/en_US.ts b/packages/s2-shared/src/constant/i18n/en_US.ts index 4dae05aa97..d31ab091da 100644 --- a/packages/s2-shared/src/constant/i18n/en_US.ts +++ b/packages/s2-shared/src/constant/i18n/en_US.ts @@ -69,4 +69,12 @@ export const EN_US: Record = { 表头: 'Header', '表身 (维度)': 'Body(dimension)', '表身 (指标)': 'Body(measure)', + 冻结行列头: 'Frozen header', + 行: 'Row', + 列: 'Column', + 冻结行: 'Frozen row', + 冻结列: 'Frozen column', + 冻结: 'Frozen', + 冻结前: 'Freeze the first', + 冻结后: 'Freeze the last', }; diff --git a/packages/s2-shared/src/constant/i18n/zh_CN.ts b/packages/s2-shared/src/constant/i18n/zh_CN.ts index 59bc4a4086..713c7269be 100644 --- a/packages/s2-shared/src/constant/i18n/zh_CN.ts +++ b/packages/s2-shared/src/constant/i18n/zh_CN.ts @@ -69,4 +69,12 @@ export const ZH_CN: Record = { 表头: '表头', '表身 (维度)': '表身 (维度)', '表身 (指标)': '表身 (指标)', + 冻结行列头: '冻结行列头', + 行: '行', + 列: '列', + 冻结行: '冻结行', + 冻结列: '冻结列', + 冻结: '冻结', + 冻结前: '冻结前', + 冻结后: '冻结后', }; From 204848be75a2d24f55eafdf1b0cea47fa9c54991 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Fri, 21 Jun 2024 17:18:15 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=20debounce=20?= =?UTF-8?q?=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frozen-panel/frozen-input-number/index.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/index.tsx b/packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/index.tsx index 18def1fdf5..9a33e2e5ac 100644 --- a/packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/index.tsx +++ b/packages/s2-react-components/src/components/config/frozen-panel/frozen-input-number/index.tsx @@ -13,14 +13,20 @@ export const FrozenInputNumber: React.FC = React.memo( value, ); - const onDebounceChange = debounce((nextValue) => { - onChange?.(nextValue); - }, 500); + const onDebounceChange = React.useMemo(() => { + return debounce((nextValue) => { + onChange?.(nextValue); + }, 500); + }, []); React.useEffect(() => { if (value !== inputValue) { setInputValue(value); } + + return () => { + onDebounceChange.cancel(); + }; }, [value]); return ( @@ -34,9 +40,9 @@ export const FrozenInputNumber: React.FC = React.memo( disabled={disabled} {...attrs} value={inputValue} - onChange={(nextVal) => { - setInputValue(nextVal); - onDebounceChange(nextVal); + onChange={(nextValue) => { + setInputValue(nextValue); + onDebounceChange(nextValue); }} /> ); From 279edf7fd7983669e50663f455d825bfc9abc6d3 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Fri, 21 Jun 2024 17:20:16 +0800 Subject: [PATCH 8/8] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f03c70a303..5c777cfebe 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "build:umd": "pnpm -r --filter './packages/*' --stream build:umd", "build:size-limit": "pnpm -r --filter './packages/*' --stream build:size-limit", "build:size-limit-json": "pnpm -r --filter './packages/*' --stream build:size-limit-json", - "release": "pnpm -r --filter !@antv/s2-shared --filter !@antv/s2-site --workspace-concurrency=1 exec npx --no-install semantic-release", + "release": "pnpm -r --filter !@antv/s2-shared --filter !@antv/s2-site --filter !@antv/s2-react-components --workspace-concurrency=1 exec npx --no-install semantic-release", "release:preview": "pnpm release --dry-run --no-ci", "release:bump-version": "node ./scripts/bump-version.js", "test": "pnpm -r --filter './packages/*' --stream test",