-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(interaction): support focus context (#4946)
* feat(interaction): focus and context * docs: add example * fix: ci * chore: lock gui to 0.5.0-alpha.17
- Loading branch information
Showing
26 changed files
with
418 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { chartOnBrushFilter as render } from '../plots/api/chart-on-brush-filter'; | ||
import { PLOT_CLASS_NAME } from '../../src'; | ||
import { dblclick, brush } from '../plots/interaction/penguins-point-brush'; | ||
import { createDOMGCanvas } from './utils/createDOMGCanvas'; | ||
import { createPromise, receiveExpectData } from './utils/event'; | ||
import { sleep } from './utils/sleep'; | ||
import './utils/useCustomFetch'; | ||
|
||
describe('chart.on', () => { | ||
const canvas = createDOMGCanvas(640, 480); | ||
const { finished, chart } = render({ canvas }); | ||
|
||
chart.off(); | ||
|
||
it('chart.on("brush:filter", callback) should provide selection when filtering', async () => { | ||
await finished; | ||
const { document } = canvas; | ||
const plot = document.getElementsByClassName(PLOT_CLASS_NAME)[0]; | ||
|
||
// Brush plot. | ||
const [filtered, resolve] = createPromise(); | ||
chart.on( | ||
'brush:filter', | ||
receiveExpectData(resolve, { | ||
selection: [ | ||
[34.99184225303586, 44.72635552737214], | ||
[15.877014192597635, 20.13017874955966], | ||
], | ||
}), | ||
); | ||
brush(plot, 100, 100, 300, 300); | ||
await filtered; | ||
await sleep(20); | ||
|
||
// Reset plot. | ||
const [rested, resolve1] = createPromise(); | ||
chart.off(); | ||
chart.on( | ||
'brush:filter', | ||
receiveExpectData(resolve1, { | ||
selection: [ | ||
[32.1, 59.6], | ||
[13.1, 21.5], | ||
], | ||
}), | ||
); | ||
setTimeout(() => dblclick(plot), 1000); | ||
await rested; | ||
// Wait for rerender over to close test. | ||
await sleep(20); | ||
}); | ||
|
||
afterAll(() => { | ||
canvas?.destroy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { chartOnFocusContext as render } from '../plots/api/chart-on-focus-context'; | ||
import { | ||
dblclick, | ||
brush, | ||
dragMask, | ||
} from '../plots/interaction/penguins-point-brush'; | ||
import { PLOT_CLASS_NAME } from '../../src'; | ||
import { createNodeGCanvas } from './utils/createNodeGCanvas'; | ||
import { kebabCase } from './utils/kebabCase'; | ||
import { sleep } from './utils/sleep'; | ||
import './utils/useSnapshotMatchers'; | ||
import './utils/useCustomFetch'; | ||
import { createPromise } from './utils/event'; | ||
|
||
function plotOf(canvas) { | ||
const { document } = canvas; | ||
const plot = document.getElementsByClassName(PLOT_CLASS_NAME)[0]; | ||
return plot; | ||
} | ||
|
||
describe('chart.on', () => { | ||
const dir = `${__dirname}/snapshots/api/${kebabCase(render.name)}`; | ||
const canvas1 = createNodeGCanvas(640, 360); | ||
const canvas2 = createNodeGCanvas(640, 80); | ||
const assetSnapshots = async (step) => { | ||
await sleep(500); | ||
await expect(canvas1).toMatchCanvasSnapshot(dir, step + '-focus', { | ||
maxError: 300, | ||
}); | ||
await expect(canvas2).toMatchCanvasSnapshot(dir, step + '-context', { | ||
maxError: 300, | ||
}); | ||
}; | ||
|
||
it('chart.on({...}) should enables different charts to communicate.', async () => { | ||
const { focused, contexted, focusView } = render({ | ||
canvas1, | ||
canvas2, | ||
container: document.createElement('div'), | ||
}); | ||
await focused; | ||
await contexted; | ||
|
||
const focusPlot = plotOf(canvas1); | ||
const contextPlot = plotOf(canvas2); | ||
|
||
// Brush context view. | ||
const [p1, r1] = createPromise(); | ||
brush(focusPlot, 100, 100, 300, 300); | ||
await assetSnapshots('step0'); | ||
|
||
// Brush context view again. | ||
const [p2, r2] = createPromise(); | ||
brush(focusPlot, 200, 200, 400, 400); | ||
await assetSnapshots('step1'); | ||
|
||
// Drag focus view. | ||
dragMask(contextPlot, 50, 50, 100, 100); | ||
await assetSnapshots('step2'); | ||
|
||
// Reset focus view. | ||
dblclick(contextPlot); | ||
await assetSnapshots('step3'); | ||
|
||
// Brush focus view. | ||
brush(focusPlot, 30, 30, 180, 180); | ||
await assetSnapshots('step4'); | ||
|
||
// Drag focus view. | ||
dragMask(contextPlot, 50, 50, 100, 100); | ||
await assetSnapshots('step5'); | ||
|
||
// Reset focus view. | ||
dblclick(contextPlot); | ||
await assetSnapshots('step6'); | ||
}); | ||
|
||
afterAll(() => { | ||
canvas1?.destroy(); | ||
canvas2?.destroy(); | ||
}); | ||
}); |
File renamed without changes.
File renamed without changes.
Binary file added
BIN
+5.85 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step0-context.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+7.82 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step0-focus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+5.82 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step1-context.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+5.39 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step1-focus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+5.74 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step2-context.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+4.49 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step2-focus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+4.66 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step3-context.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+13.3 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step3-focus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+5.83 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step4-context.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+6.86 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step4-focus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+5.88 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step5-context.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+7.34 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step5-focus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+4.66 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step6-context.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+13.3 KB
__tests__/integration/snapshots/api/chart-on-focus-context/step6-focus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { Chart } from '../../../src'; | ||
|
||
export function chartOnFocusContext(context) { | ||
const { container, canvas1, canvas2 } = context; | ||
|
||
// Render focus view. | ||
const focusContainer = document.createElement('div'); | ||
container.appendChild(focusContainer); | ||
|
||
const focusView = new Chart({ | ||
theme: 'classic', | ||
container: focusContainer, | ||
canvas: canvas1, | ||
}); | ||
|
||
focusView.options({ | ||
type: 'area', | ||
height: 360, | ||
data: { type: 'fetch', value: 'data/aapl.csv' }, | ||
encode: { x: 'date', y: 'close' }, | ||
axis: false, | ||
animate: false, | ||
interaction: { brushXFilter: true, tooltip: false }, | ||
}); | ||
|
||
const focused = focusView.render(); | ||
|
||
// Render context view. | ||
const contextContainer = document.createElement('div'); | ||
container.appendChild(contextContainer); | ||
|
||
const contextView = new Chart({ | ||
theme: 'classic', | ||
container: contextContainer, | ||
canvas: canvas2, | ||
}); | ||
|
||
contextView.options({ | ||
type: 'area', | ||
height: 120, | ||
data: { type: 'fetch', value: 'data/aapl.csv' }, | ||
encode: { x: 'date', y: 'close' }, | ||
axis: false, | ||
animate: false, | ||
state: { active: { fill: 'red' } }, | ||
interaction: { brushXHighlight: { series: true }, tooltip: false }, | ||
}); | ||
|
||
const contexted = contextView.render(); | ||
|
||
// Add event listeners. | ||
focusView.on('brush:filter', (e) => { | ||
const { nativeEvent } = e; | ||
if (!nativeEvent) return; | ||
const { selection } = e.data; | ||
const { x: scaleX } = focusView.getScale(); | ||
const [[x1, x2]] = selection; | ||
const domainX = scaleX.getOptions().domain; | ||
if (x1 === domainX[0] && x2 === domainX[1]) { | ||
contextView.emit('brush:remove'); | ||
} else { | ||
contextView.emit('brush:highlight', { data: { selection } }); | ||
} | ||
}); | ||
|
||
contextView.on('brush:highlight', (e) => { | ||
const { nativeEvent, data } = e; | ||
if (!nativeEvent) return; | ||
const { selection } = data; | ||
focusView.emit('brush:filter', { data: { selection } }); | ||
}); | ||
|
||
contextView.on('brush:end', () => { | ||
const { x: scaleX, y: scaleY } = contextView.getScale(); | ||
const selection = [scaleX.getOptions().domain, scaleY.getOptions().domain]; | ||
focusView.emit('brush:filter', { data: { selection } }); | ||
}); | ||
|
||
return { focusView, focused, contexted, contextView }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
site/examples/interaction/interaction/demo/focus-context.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { Chart } from '@antv/g2'; | ||
|
||
document.getElementById('container').innerHTML = ` | ||
<div id="focus" ></div> | ||
<div id="context"></div> | ||
`; | ||
|
||
// Render focus View. | ||
const focus = new Chart({ | ||
container: 'focus', | ||
theme: 'classic', | ||
height: 360, | ||
}); | ||
|
||
focus | ||
.area() | ||
.data({ | ||
type: 'fetch', | ||
value: | ||
'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv', | ||
}) | ||
.encode('x', 'date') | ||
.encode('y', 'close') | ||
.animate(false) | ||
.axis('x', { grid: false, title: false, tickCount: 5 }) | ||
.axis('y', { grid: false, tickCount: 5 }) | ||
.interaction('tooltip', false) | ||
.interaction('brushXFilter', true); | ||
|
||
focus.render(); | ||
|
||
// Render context View. | ||
const context = new Chart({ | ||
container: 'context', | ||
theme: 'classic', | ||
paddingLeft: 40, | ||
paddingTop: 0, | ||
paddingBottom: 0, | ||
height: 80, | ||
}); | ||
|
||
context | ||
.area() | ||
.data({ | ||
type: 'fetch', | ||
value: | ||
'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv', | ||
}) | ||
.encode('x', 'date') | ||
.encode('y', 'close') | ||
.animate(false) | ||
.axis(false) | ||
.interaction('tooltip', false) | ||
.interaction('brushXHighlight', true); | ||
|
||
context.render(); | ||
|
||
// Add event listeners to communicate. | ||
focus.on('brush:filter', (e) => { | ||
const { nativeEvent } = e; | ||
if (!nativeEvent) return; | ||
const { selection } = e.data; | ||
const { x: scaleX } = focus.getScale(); | ||
const [[x1, x2]] = selection; | ||
const domainX = scaleX.getOptions().domain; | ||
if (x1 === domainX[0] && x2 === domainX[1]) { | ||
context.emit('brush:remove'); | ||
} else { | ||
context.emit('brush:highlight', { data: { selection } }); | ||
} | ||
}); | ||
|
||
context.on('brush:highlight', (e) => { | ||
const { nativeEvent, data } = e; | ||
if (!nativeEvent) return; | ||
const { selection } = data; | ||
focus.emit('brush:filter', { data: { selection } }); | ||
}); | ||
|
||
context.on('brush:end', () => { | ||
const { x: scaleX, y: scaleY } = context.getScale(); | ||
const selection = [scaleX.getOptions().domain, scaleY.getOptions().domain]; | ||
focus.emit('brush:filter', { data: { selection } }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.