Skip to content

Commit

Permalink
feat(interaction): add scrollbarFilter (#5152)
Browse files Browse the repository at this point in the history
  • Loading branch information
pearmini committed Jun 2, 2023
1 parent 19d7f68 commit 72e0cc4
Show file tree
Hide file tree
Showing 19 changed files with 386 additions and 56 deletions.
76 changes: 76 additions & 0 deletions __tests__/integration/api-chart-emit-scrollbar-filter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { chartEmitScrollbarFilter as render } from '../plots/api/chart-emit-scrollbar-filter';
import { SCROLLBAR_CLASS_NAME } from '../../src/interaction/scrollbarFilter';
import { dispatchValueChange } from '../plots/tooltip/appl-line-slider-filter';
import { createNodeGCanvas } from './utils/createNodeGCanvas';
import { sleep } from './utils/sleep';
import { kebabCase } from './utils/kebabCase';
import { createPromise } from './utils/event';
import './utils/useSnapshotMatchers';

describe('chart.emit', () => {
const dir = `${__dirname}/snapshots/api/${kebabCase(render.name)}`;
const canvas = createNodeGCanvas(800, 500);

it('chart.on("scrollbar:filter") should receive expected data.', async () => {
const { chart, finished } = render({
canvas,
container: document.createElement('div'),
});
await finished;
await sleep(20);

const [SX, SY] = Array.from(
canvas.document.getElementsByClassName(SCROLLBAR_CLASS_NAME),
);

// chart.emit('scrollbarX:filter', options) should trigger scrollbar.
const X = ['2001-03'];
chart.emit('scrollbarX:filter', {
data: { selection: [X, undefined] },
});
await sleep(20);
await expect(canvas).toMatchCanvasSnapshot(dir, 'step0');

// chart.emit('scrollbarY:filter', options) should trigger scrollbar.
const Y = [50, 550];
chart.emit('scrollbarY:filter', {
data: { selection: [undefined, Y] },
});
await sleep(20);
await expect(canvas).toMatchCanvasSnapshot(dir, 'step1');

chart.off();

// chart.on("scrollbarX:filter") should receive expected data.
const [filteredX, resolveX] = createPromise();
chart.on('scrollbarX:filter', (event) => {
if (!event.nativeEvent) return;
expect(event.data.selection).toEqual([
['2001-05', '2002-03'],
[50, 500],
]);
resolveX();
});
dispatchValueChange(SX);
await sleep(20);
await filteredX;

// chart.on("scrollbarY:filter") should receive expected data.
const [filteredY, resolveY] = createPromise();
chart.on('scrollbarY:filter', (event) => {
if (!event.nativeEvent) return;
expect(event.data.selection).toEqual([
['2001-05', '2002-03'],
[150, 450],
]);
resolveY();
});
dispatchValueChange(SY);
await sleep(20);
await filteredY;
});

afterAll(() => {
canvas?.destroy();
});
});
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __tests__/integration/snapshots/static/aaplLineScrollbar.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
83 changes: 83 additions & 0 deletions __tests__/plots/api/chart-emit-scrollbar-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Chart } from '../../../src';

export function chartEmitScrollbarFilter(context) {
const { container, canvas } = context;

// button
const buttonX = document.createElement('button');
buttonX.innerText = 'FilterX';
container.appendChild(buttonX);

const buttonY = document.createElement('button');
buttonY.innerText = 'FilterY';
container.appendChild(buttonY);

// wrapperDiv
const wrapperDiv = document.createElement('div');
container.appendChild(wrapperDiv);

const chart = new Chart({
theme: 'classic',
container: wrapperDiv,
paddingBottom: 120,
width: 400,
canvas,
clip: true,
});

chart
.interval()
.data([
{ date: '2001-01', value: 100 },
{ date: '2001-02', value: 400 },
{ date: '2001-03', value: 500 },
{ date: '2001-04', value: 600 },
{ date: '2001-05', value: 300 },
{ date: '2001-06', value: 600 },
{ date: '2001-07', value: 300 },
{ date: '2001-08', value: 600 },
{ date: '2001-09', value: 109 },
{ date: '2001-10', value: 100 },
{ date: '2001-11', value: 102 },
{ date: '2001-12', value: 103 },
{ date: '2002-01', value: 102 },
{ date: '2002-02', value: 101 },
{ date: '2002-03', value: 200 },
{ date: '2002-04', value: 500 },
{ date: '2002-05', value: 100 },
{ date: '2002-06', value: 100 },
{ date: '2002-07', value: 102 },
{ date: '2002-08', value: 109 },
])
.encode('x', 'date')
.encode('y', 'value')
.axis('x', { size: 80, style: { labelTransform: 'rotate(90deg)' } })
.scrollbar('x', { ratio: 0.25 })
.scrollbar('y', { ratio: 0.75 });

const finished = chart.render();

chart.on('scrollbarX:filter', (event) => {
const { data, nativeEvent } = event;
if (nativeEvent) console.log('scrollbarX:filter', data);
});

chart.on('scrollbarY:filter', (event) => {
const { data, nativeEvent } = event;
if (nativeEvent) console.log('scrollbarY:filter', data);
});

buttonX.onclick = () => {
chart.emit('scrollbarX:filter', {
data: { selection: [['2001-03'], undefined] },
});
};

buttonY.onclick = () => {
chart.emit('scrollbarY:filter', {
data: { selection: [undefined, [50]] },
});
};

return { chart, finished };
}
1 change: 1 addition & 0 deletions __tests__/plots/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ export { chartEmitLegendHighlight } from './chart-emit-legend-highlight';
export { chartEmitBrushHighlightAxisVertical } from './chart-emit-brush-highlight-axis-vertical';
export { chartEmitBrushHighlightAxisHorizontal } from './chart-emit-brush-highlight-axis-horizontal';
export { chartEmitBrushHighlightAxisCross } from './chart-emit-brush-highlight-axis-cross';
export { chartEmitScrollbarFilter } from './chart-emit-scrollbar-filter';
16 changes: 2 additions & 14 deletions __tests__/unit/stdlib/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,10 @@ import {
BrushYFilter,
BrushXFilter,
SliderFilter,
ScrollbarFilter,
LegendHighlight,
Poptip,
Event,
// ElementSelected,
// Tooltip,
// Fisheye as FisheyeInteraction,
// ElementHighlightByColor,
// ElementHighlightByX,
// ElementHighlight,
// ElementListHighlight,
// LegendActive,
// LegendHighlight,
// Brush,
// BrushHighlight,
// BrushVisible,
// ActiveRegion,
// EllipsisText,
} from '../../../src/interaction';
import {
SpaceLayer,
Expand Down Expand Up @@ -504,6 +491,7 @@ describe('stdlib', () => {
'interaction.brushXFilter': BrushXFilter,
'interaction.brushFilter': BrushFilter,
'interaction.sliderFilter': SliderFilter,
'interaction.scrollbarFilter': ScrollbarFilter,
'interaction.poptip': Poptip,
'interaction.event': Event,
'composition.spaceLayer': SpaceLayer,
Expand Down
5 changes: 5 additions & 0 deletions site/docs/spec/interaction/scrollbarFilter.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: scrollbarFilter
---

<embed src="@/docs/spec/interaction/scrollbarFilter.zh.md"></embed>
59 changes: 59 additions & 0 deletions site/docs/spec/interaction/scrollbarFilter.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: scrollbarFilter
order: 1
---

滚动条筛选是一个默认交互,当设置了 scrollbar 默认就会有这个交互。

## 开始使用

```js
import { Chart } from '@antv/g2';

const chart = new Chart({
container: 'container',
theme: 'classic',
});

chart
.line()
.data({
type: 'fetch',
value:
'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',
})
.encode('x', 'date')
.encode('y', 'close')
.scrollbar('y', { ratio: 0.3 }) // y domain 的比例
.scrollbar('x', { ratio: 0.5 }); // x domain 的比例

chart.render();
```

## 案例

### 触发事件

```js
chart.emit('scrollbarX:filter', {
data: { selection: [['2001-03'], undefined] },
});

chart.emit('scrollbarY:filter', {
data: { selection: [undefined, [50, 550]] },
});
```

### 监听数据

```js
chart.on('scrollbarX:filter', (event) => {
const { data, nativeEvent } = event;
if (nativeEvent) console.log('scrollbarX:filter', data.selection);
});

chart.on('scrollbarY:filter', (event) => {
const { data, nativeEvent } = event;
if (nativeEvent) console.log('scrollbarY:filter', data.selection);
});
```
19 changes: 11 additions & 8 deletions src/component/scrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,27 @@ export type ScrollbarOptions = {
export const Scrollbar: GCC<ScrollbarOptions> = (options) => {
const { orientation, labelFormatter, style, ...rest } = options;

return ({ value, theme }) => {
return ({ scales: [scale], value, theme }) => {
const { bbox } = value;
const { x, y, width, height } = bbox;
const { scrollbar: scrollbarTheme = {} } = theme;

const { ratio, range } = scale.getOptions();
const mainSize = orientation === 'horizontal' ? width : height;
const actualSize = mainSize / ratio;
const [r0, r1] = range;
const value1 = r1 > r0 ? 0 : 1;
return new ScrollbarComponent({
className: 'scrollbar',
className: 'g2-scrollbar',
style: Object.assign({}, scrollbarTheme, {
...style,
x,
y,
trackLength: orientation === 'horizontal' ? width : height,
trackLength: mainSize,
...rest,
orientation,
value: 0,
// @todo Get actual length of content.
contentLength: 1500,
viewportLength: orientation === 'horizontal' ? width : height,
value: value1,
contentLength: actualSize,
viewportLength: mainSize,
}),
});
};
Expand Down
1 change: 1 addition & 0 deletions src/interaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export { BrushFilter } from './brushFilter';
export { BrushXFilter } from './brushXFilter';
export { BrushYFilter } from './brushYFilter';
export { SliderFilter } from './sliderFilter';
export { ScrollbarFilter } from './scrollbarFilter';
export { Poptip } from './poptip';
export { Event } from './event';
32 changes: 32 additions & 0 deletions src/interaction/scrollbarFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { SliderFilter } from './sliderFilter';

export const SCROLLBAR_CLASS_NAME = 'g2-scrollbar';

export function ScrollbarFilter(options: any = {}) {
return (context, _, emitter) => {
const { view, container } = context;
const scrollbars = container.getElementsByClassName(SCROLLBAR_CLASS_NAME);
if (!scrollbars.length) return () => {};
const { scale } = view;
const { x: scaleX, y: scaleY } = scale;
const channelDomain = {
x: scaleX.getOptions().domain,
y: scaleY.getOptions().domain,
};
scaleX.update({
domain: scaleX.getOptions().expectedDomain,
});
scaleY.update({
domain: scaleY.getOptions().expectedDomain,
});
const interaction = SliderFilter({
...options,
channelDomain,
className: SCROLLBAR_CLASS_NAME,
prefix: 'scrollbar',
hasState: true,
setValue: (component, values) => component.setValue(values[0]),
});
return interaction(context, _, emitter);
};
}

0 comments on commit 72e0cc4

Please sign in to comment.