Skip to content

Commit

Permalink
feat: selected cell highlight (#1878)
Browse files Browse the repository at this point in the history
* feat: upgrade selectedCellHighlight option & 修复 selectedCellHighlight 单元格因滚动状态失效的问题

* feat: selectedCellHighlight option 复制功能

* fix: selectedCellHighlight 为 false 的情况

* fix: 修复单测报错

* feat: 补充单测

* feat: selectedCellHighlight 配置项补充文档

* refactor: 调整 BaseDataSet.getRowData 为抽象方法

* refactor: remove unused code

* refactor: 调整 selectedCellHighlight option 实现逻辑

* feat: 修改文档 & react playground

* refactor: 调整 selectedCellHighlight option 复制实现

* feat: 调整单测

* fix: type error

* refactor: selectedCellHighlight option 调整数据单元格点击事件 headerCells 存储逻辑

* feat: selectedCellHighlight option 统一刷选的交互 & afterSelectDataCells 函数提取

* refactor: remove duplicate constant

* refactor: selectedCellHighlight option selectedCellHighlightAdaptor 调整为 getSelectedCellHighlight

* fix: lint error

* fix: unit test

Co-authored-by: 嘤嘤嘤 <[email protected]>
Co-authored-by: stone <[email protected]>
  • Loading branch information
3 people authored Dec 23, 2022
1 parent ec62409 commit 3e11a37
Show file tree
Hide file tree
Showing 22 changed files with 499 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import type { S2Options } from '@/common/interface';
import type { SpreadSheet } from '@/sheet-type';
import {
HOVER_FOCUS_DURATION,
type InteractionCellSelectedHighlightType,
InteractionName,
InteractionStateName,
InterceptType,
S2Event,
} from '@/common/constant';
import type { Node } from '@/facet/layout/node';

jest.mock('@/interaction/event-controller');

Expand Down Expand Up @@ -50,6 +52,7 @@ describe('Interaction Data Cell Click Tests', () => {
expect(s2.interaction.getState()).toEqual({
cells: [mockCellInfo.mockCellMeta],
stateName: InteractionStateName.SELECTED,
onUpdateCells: expect.any(Function),
});
expect(s2.showTooltipWithInfo).toHaveBeenCalled();
});
Expand All @@ -68,6 +71,7 @@ describe('Interaction Data Cell Click Tests', () => {
expect(s2.interaction.getState()).toEqual({
cells: [mockCellInfo.mockCellMeta],
stateName: InteractionStateName.SELECTED,
onUpdateCells: expect.any(Function),
});
});

Expand Down Expand Up @@ -160,4 +164,66 @@ describe('Interaction Data Cell Click Tests', () => {
expect(s2.interaction.isHoverFocusState()).toBeFalsy();
expect(clearHoverTimerSpy).toHaveBeenCalledTimes(2);
});

test('should highlight the column header cell when data cell clicked', () => {
const headerCellId0 = 'header-0';
const headerCellId1 = 'header-1';
const columnNode: Array<Partial<Node>> = [
{
belongsCell: {
getMeta: () => ({
id: headerCellId0,
colIndex: -1,
rowIndex: -1,
}),
} as any,
id: headerCellId0,
},
{
belongsCell: {
getMeta: () => ({
id: headerCellId1,
colIndex: -1,
rowIndex: -1,
}),
} as any,
id: headerCellId1,
},
];
s2.getColumnNodes = jest.fn(() => columnNode) as any;
s2.getRowNodes = jest.fn(() => []);

const firstDataCellInfo = createMockCellInfo(
`${headerCellId0}[&]first-data-cell`,
);
s2.getCell = () => firstDataCellInfo.mockCell as any;

s2.setOptions({
interaction: {
selectedCellHighlight: {
colHeader: true,
} as InteractionCellSelectedHighlightType,
},
});

const mockHeaderCellInfo = createMockCellInfo(headerCellId0, {
colIndex: columnNode[0].belongsCell.getMeta().colIndex,
rowIndex: columnNode[0].belongsCell.getMeta().rowIndex,
});

s2.interaction.getAllColHeaderCells = jest.fn();
s2.interaction.updateCells = jest.fn();

s2.emit(S2Event.DATA_CELL_CLICK, {
stopPropagation() {},
} as unknown as GEvent);

expect(s2.interaction.getState()).toEqual({
cells: [firstDataCellInfo.mockCellMeta, mockHeaderCellInfo.mockCellMeta],
stateName: InteractionStateName.SELECTED,
onUpdateCells: expect.any(Function),
});
expect(s2.interaction.getAllColHeaderCells).toHaveBeenCalled();
expect(s2.interaction.updateCells).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ describe('Interaction Data Cell Brush Selection Tests', () => {
mockSpreadSheetInstance.showTooltipWithInfo = jest.fn();
mockRootInteraction.getPanelGroupAllDataCells = () =>
panelGroupAllDataCells;
mockRootInteraction.getSelectedCellHighlight = () => ({
rowHeader: false,
colHeader: false,
rowCells: false,
colCells: false,
});
mockSpreadSheetInstance.interaction = mockRootInteraction;
mockSpreadSheetInstance.render();
mockSpreadSheetInstance.facet.layoutResult.colLeafNodes = Array.from(
Expand Down Expand Up @@ -135,6 +141,12 @@ describe('Interaction Data Cell Brush Selection Tests', () => {
mockSpreadSheetInstance.setOptions({
interaction: { selectedCellHighlight: true },
});
mockRootInteraction.getSelectedCellHighlight = () => ({
rowHeader: true,
colHeader: true,
rowCells: false,
colCells: false,
});

brushSelectionInstance.getBrushRange = () => {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ describe('Interaction Data Cell Multi Selection Tests', () => {
expect(s2.interaction.getState()).toEqual({
cells: [mockCellA.mockCellMeta, mockCellB.mockCellMeta],
stateName: InteractionStateName.SELECTED,
onUpdateCells: expect.any(Function),
});

expect(
Expand Down Expand Up @@ -151,6 +152,7 @@ describe('Interaction Data Cell Multi Selection Tests', () => {
expect(s2.interaction.getState()).toEqual({
cells: [mockCellB.mockCellMeta],
stateName: InteractionStateName.SELECTED,
onUpdateCells: expect.any(Function),
});

expect(
Expand Down
21 changes: 21 additions & 0 deletions packages/s2-core/__tests__/unit/utils/export/copy-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,27 @@ describe('List Table Core Data Process', () => {
const data = getSelectedData(sss);
expect(data).toBe(convertString(newLineText));
});

it('should copy row data when select data row cell', () => {
s2.setOptions({
interaction: {
selectedCellHighlight: {
rowCells: true,
},
},
});

const cell = s2.interaction
.getAllCells()
.filter(({ cellType }) => cellType === CellTypes.DATA_CELL)[0];

s2.interaction.changeState({
cells: [getCellMeta(cell)],
stateName: InteractionStateName.SELECTED,
});

expect(getSelectedData(s2).split('\t').length).toBe(5);
});
});

describe('Pivot Table Core Data Process', () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/s2-core/__tests__/util/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,13 @@ export function getGradient(

export const createMockCellInfo = (
cellId: string,
{ colIndex = 0, rowIndex = 0 } = {},
{ colIndex = 0, rowIndex = 0, colId = '0' } = {},
) => {
const mockCellViewMeta: Partial<ViewMeta> = {
id: cellId,
colIndex,
rowIndex,
colId,
type: undefined,
x: 0,
y: 0,
Expand All @@ -154,6 +155,7 @@ export const createMockCellInfo = (
'y',
'update',
'spreadsheet',
'colId',
]);
const mockCell = {
...mockCellViewMeta,
Expand Down
13 changes: 10 additions & 3 deletions packages/s2-core/src/cell/data-cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import type {
ViewMetaIndexType,
} from '../common/interface';
import { getBorderPositionAndStyle, getMaxTextWidth } from '../utils/cell/cell';
import { includeCell } from '../utils/cell/data-cell';
import {
includeCell,
shouldUpdateBySelectedCellsHighlight,
updateBySelectedCellsHighlight,
} from '../utils/cell/data-cell';
import { getIconPositionCfg } from '../utils/condition/condition';
import { renderLine, renderRect, updateShapeAttr } from '../utils/g-renders';
import { EMPTY_PLACEHOLDER } from '../common/constant/basic';
Expand Down Expand Up @@ -80,6 +84,7 @@ export class DataCell extends BaseCell<ViewMeta> {

protected handleSelect(cells: CellMeta[]) {
const currentCellType = cells?.[0]?.type;

switch (currentCellType) {
// 列多选
case CellTypes.COL_CELL:
Expand All @@ -91,7 +96,9 @@ export class DataCell extends BaseCell<ViewMeta> {
break;
// 单元格单选/多选
case CellTypes.DATA_CELL:
if (includeCell(cells, this)) {
if (shouldUpdateBySelectedCellsHighlight(this.spreadsheet)) {
updateBySelectedCellsHighlight(cells, this, this.spreadsheet);
} else if (includeCell(cells, this)) {
this.updateByState(InteractionStateName.SELECTED);
} else if (
this.spreadsheet.options.interaction.selectedCellsSpotlight
Expand Down Expand Up @@ -134,7 +141,7 @@ export class DataCell extends BaseCell<ViewMeta> {

public update() {
const stateName = this.spreadsheet.interaction.getCurrentStateName();
const cells = this.spreadsheet.interaction.getCells();
const cells = this.spreadsheet.interaction.getCells([CellTypes.DATA_CELL]);

if (stateName === InteractionStateName.ALL_SELECTED) {
this.updateByState(InteractionStateName.SELECTED);
Expand Down
8 changes: 6 additions & 2 deletions packages/s2-core/src/cell/header-cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,11 @@ export abstract class HeaderCell extends BaseCell<Node> {
}
}

protected handleSelect(cells: CellMeta[], nodes: Node[]) {
protected handleSelect(cells: Array<CellMeta>, nodes: Node[]) {
if (includeCell(cells, this)) {
this.updateByState(InteractionStateName.SELECTED);
}

const selectedNodeIds = map(nodes, 'id');
if (includes(selectedNodeIds, this.meta.id)) {
this.updateByState(InteractionStateName.SELECTED);
Expand Down Expand Up @@ -382,7 +383,10 @@ export abstract class HeaderCell extends BaseCell<Node> {
public update() {
const { interaction } = this.spreadsheet;
const stateInfo = interaction?.getState();
const cells = interaction?.getCells();
const cells = interaction?.getCells([
CellTypes.COL_CELL,
CellTypes.ROW_CELL,
]);

if (!first(cells)) {
return;
Expand Down
7 changes: 7 additions & 0 deletions packages/s2-core/src/common/constant/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,10 @@ export enum ScrollDirection {
LEADING = 'leading',
TRAILING = 'trailing',
}

export interface InteractionCellSelectedHighlightType {
rowHeader?: boolean; // 高亮行头
colHeader?: boolean; // 高亮列头
rowCells?: boolean; // 高亮选中单元格所在行
colCells?: boolean; // 高亮选中单元格所在列
}
2 changes: 2 additions & 0 deletions packages/s2-core/src/common/interface/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,5 @@ export interface GridInfo {
cols: number[];
rows: number[];
}

export type RowData = Data | DataType;
4 changes: 3 additions & 1 deletion packages/s2-core/src/common/interface/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
CellTypes,
InterceptType,
ScrollbarPositionType,
InteractionCellSelectedHighlightType,
} from '../constant';
import type {
BaseCell,
Expand Down Expand Up @@ -35,6 +36,7 @@ export interface CellMeta {
colIndex: number;
rowIndex: number;
type: CellTypes;
rowQuery?: Record<string, any>;
[key: string]: unknown;
}

Expand Down Expand Up @@ -169,7 +171,7 @@ export interface InteractionOptions {
// https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
eventListenerOptions?: boolean | AddEventListenerOptions;
// highlight col and row header for selected cell
selectedCellHighlight?: boolean;
selectedCellHighlight?: boolean | InteractionCellSelectedHighlightType;
// https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior
overscrollBehavior?: 'auto' | 'none' | 'contain';
/** ***********CUSTOM INTERACTION HOOKS**************** */
Expand Down
7 changes: 7 additions & 0 deletions packages/s2-core/src/data-set/base-data-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
getValueRangeState,
setValueRangeState,
} from '../utils/condition/state-controller';
import type { CellMeta, RowData } from '../common';
import type { CellDataParams, DataType } from './index';

export abstract class BaseDataSet {
Expand Down Expand Up @@ -177,4 +178,10 @@ export abstract class BaseDataSet {
public moreThanOneValue() {
return this.fields?.values?.length > 1;
}

/**
* get a row cells data including cell
* @param cells
*/
public abstract getRowData(cells: CellMeta): RowData;
}
6 changes: 6 additions & 0 deletions packages/s2-core/src/data-set/pivot-data-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import type {
PartDrillDownFieldInLevel,
S2DataConfig,
ViewMeta,
RowData,
} from '../common/interface';
import { Node } from '../facet/layout/node';
import {
Expand All @@ -56,6 +57,7 @@ import {
} from '../utils/dataset/pivot-data-set';
import { calcActionByType } from '../utils/number-calculate';
import { handleSortAction } from '../utils/sort-action';
import type { CellMeta } from '../common';
import { BaseDataSet } from './base-data-set';
import type {
CellDataParams,
Expand Down Expand Up @@ -618,4 +620,8 @@ export class PivotDataSet extends BaseDataSet {
private isCustomMeasuresPosition(customValueOrder?: number) {
return isNumber(customValueOrder);
}

public getRowData(cell: CellMeta): RowData {
return this.getMultiData(cell.rowQuery);
}
}
6 changes: 6 additions & 0 deletions packages/s2-core/src/data-set/table-data-set.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { each, orderBy, filter, includes, isFunction, isObject } from 'lodash';
import { isAscSort, isDescSort } from '..';
import type { S2DataConfig } from '../common/interface';
import type { CellMeta } from '../common';
import type { RowData } from '../common/interface/basic';
import type { CellDataParams, DataType } from './interface';
import { BaseDataSet } from './base-data-set';

Expand Down Expand Up @@ -167,4 +169,8 @@ export class TableDataSet extends BaseDataSet {
public getMultiData(query: DataType, isTotals?: boolean): DataType[] {
return this.displayData;
}

public getRowData(cell: CellMeta): RowData {
return this.getCellData({ query: { rowIndex: cell.rowIndex } });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import type {
} from '../../../common/interface';
import {
getCellMeta,
updateRowColCells,
getInteractionCells,
afterSelectDataCells,
} from '../../../utils/interaction/select-event';
import {
getTooltipOptions,
Expand All @@ -30,7 +31,7 @@ export class DataCellClick extends BaseEvent implements BaseEventImplement {
this.spreadsheet.on(S2Event.DATA_CELL_CLICK, (event: CanvasEvent) => {
event.stopPropagation();

const { interaction, options } = this.spreadsheet;
const { interaction } = this.spreadsheet;
interaction.clearHoverTimer();

if (interaction.hasIntercepts([InterceptType.CLICK])) {
Expand Down Expand Up @@ -60,15 +61,12 @@ export class DataCellClick extends BaseEvent implements BaseEventImplement {
}

interaction.changeState({
cells: [getCellMeta(cell)],
cells: getInteractionCells(getCellMeta(cell), this.spreadsheet),
stateName: InteractionStateName.SELECTED,
onUpdateCells: afterSelectDataCells,
});
this.spreadsheet.emit(S2Event.GLOBAL_SELECTED, [cell]);
this.showTooltip(event, meta);

if (options.interaction.selectedCellHighlight) {
updateRowColCells(meta);
}
});
}

Expand Down
Loading

1 comment on commit 3e11a37

@vercel
Copy link

@vercel vercel bot commented on 3e11a37 Dec 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

antvis-s2 – ./

antvis-s2-git-master-antv-s2.vercel.app
antvis-s2.vercel.app
antvis-s2-antv-s2.vercel.app

Please sign in to comment.