Skip to content

Commit

Permalink
feat: add drag&drop support to vaadin-chart
Browse files Browse the repository at this point in the history
  • Loading branch information
bwajtr committed Jan 10, 2025
1 parent 4d47f73 commit 6794915
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 14 deletions.
24 changes: 24 additions & 0 deletions packages/charts/src/vaadin-chart.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,21 @@ export type ChartPointUnselectEvent = CustomEvent<{ point: Point; originalEvent:
*/
export type ChartPointUpdateEvent = CustomEvent<{ point: Point; originalEvent: ChartPointEvent }>;

/**
* Fired when starting to drag a point.
*/
export type ChartPointDragStartEvent = CustomEvent<{ point: Point; originalEvent: ChartPointEvent }>;

/**
* Fired when the point is dropped.
*/
export type ChartPointDropEvent = CustomEvent<{ point: Point; originalEvent: ChartPointEvent }>;

/**
* Fired while dragging a point.
*/
export type ChartPointDragEvent = CustomEvent<{ point: Point; originalEvent: ChartPointEvent }>;

/**
* Fired when when the minimum and maximum is set for the X axis.
*/
Expand Down Expand Up @@ -245,6 +260,12 @@ export interface ChartCustomEventMap {

'point-update': ChartPointUpdateEvent;

'point-drag-start': ChartPointDragStartEvent;

'point-drop': ChartPointDropEvent;

'point-drag': ChartPointDragEvent;

'xaxes-extremes-set': ChartXaxesExtremesSetEvent;

'yaxes-extremes-set': ChartYaxesExtremesSetEvent;
Expand Down Expand Up @@ -404,6 +425,9 @@ export type ChartEventMap = ChartCustomEventMap & HTMLElementEventMap;
* @fires {CustomEvent} point-select -Fired when the point is selected either programmatically or by clicking on the point.
* @fires {CustomEvent} point-unselect - Fired when the point is unselected either programmatically or by clicking on the point.
* @fires {CustomEvent} point-update - Fired when the point is updated programmatically through `.updateConfiguration()` method.
* @fires {CustomEvent} point-drag-start - Fired when starting to drag a point.
* @fires {CustomEvent} point-drop - Fired when the point is dropped.
* @fires {CustomEvent} point-drag - Fired while dragging a point.
* @fires {CustomEvent} xaxes-extremes-set - Fired when when the minimum and maximum is set for the X axis.
* @fires {CustomEvent} yaxes-extremes-set - Fired when when the minimum and maximum is set for the Y axis.
*/
Expand Down
63 changes: 49 additions & 14 deletions packages/charts/src/vaadin-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'highcharts/es-modules/masters/modules/organization.src.js';
import 'highcharts/es-modules/masters/modules/xrange.src.js';
import 'highcharts/es-modules/masters/modules/bullet.src.js';
import 'highcharts/es-modules/masters/modules/gantt.src.js';
import 'highcharts/es-modules/masters/modules/draggable-points.src.js';
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
import { beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js';
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
Expand Down Expand Up @@ -250,6 +251,9 @@ Highcharts.setOptions({ lang: { noData: '' } });
* @fires {CustomEvent} point-select -Fired when the point is selected either programmatically or by clicking on the point.
* @fires {CustomEvent} point-unselect - Fired when the point is unselected either programmatically or by clicking on the point.
* @fires {CustomEvent} point-update - Fired when the point is updated programmatically through `.updateConfiguration()` method.
* @fires {CustomEvent} point-drag-start - Fired when starting to drag a point.
* @fires {CustomEvent} point-drop - Fired when the point is dropped.
* @fires {CustomEvent} point-drag - Fired while dragging a point.
* @fires {CustomEvent} xaxes-extremes-set - Fired when when the minimum and maximum is set for the X axis.
* @fires {CustomEvent} yaxes-extremes-set - Fired when when the minimum and maximum is set for the Y axis.
*
Expand Down Expand Up @@ -885,6 +889,30 @@ class Chart extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
* @param {Object} point Point object where the event was sent from
*/
update: 'point-update',

/**
* Fired when starting to drag a point.
* @event point-drag-start
* @param {Object} detail.originalEvent object with details about the event sent
* @param {Object} point Point object where the event was sent from
*/
dragStart: 'point-drag-start',

/**
* Fired when the point is dropped.
* @event point-drop
* @param {Object} detail.originalEvent object with details about the event sent
* @param {Object} point Point object where the event was sent from
*/
drop: 'point-drop',

/**
* Fired while dragging a point.
* @event point-drag
* @param {Object} detail.originalEvent object with details about the event sent
* @param {Object} point Point object where the event was sent from
*/
drag: 'point-drag',
};
}

Expand Down Expand Up @@ -1310,11 +1338,12 @@ class Chart extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
/** @private */
__createEventListeners(eventList, configuration, pathToAdd, eventType) {
const eventObject = this.__ensureObjectPath(configuration, pathToAdd);
const self = this;

for (let keys = Object.keys(eventList), i = 0; i < keys.length; i++) {
const key = keys[i];
if (!eventObject[key]) {
eventObject[key] = (event) => {
eventObject[key] = function (event) {
const customEvent = {
bubbles: false,
composed: true,
Expand All @@ -1324,6 +1353,12 @@ class Chart extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
},
};

if (key === 'dragStart') {
// for dragStart there is no information about point in the
// event object. However, 'this' references the point being dragged
customEvent.detail[eventType] = this;
}

if (event.type === 'afterSetExtremes') {
if (event.min == null || event.max == null) {
return;
Expand Down Expand Up @@ -1355,17 +1390,17 @@ class Chart extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
if (['beforePrint', 'beforeExport'].indexOf(event.type) >= 0) {
// Guard against another print 'before print' event coming before
// the 'after print' event.
if (!this.tempBodyStyle) {
if (!self.tempBodyStyle) {
let effectiveCss = '';

[...this.shadowRoot.querySelectorAll('style')].forEach((style) => {
[...self.shadowRoot.querySelectorAll('style')].forEach((style) => {
effectiveCss += style.textContent;
});

// Strip off host selectors that target individual instances
effectiveCss = effectiveCss.replace(/:host\(.+?\)/gu, (match) => {
const selector = match.substr(6, match.length - 7);
return this.matches(selector) ? '' : match;
return self.matches(selector) ? '' : match;
});

// Zoom out a bit to avoid clipping the chart's edge on paper
Expand All @@ -1376,29 +1411,29 @@ class Chart extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
` zoom: 90%;` + // Webkit
`}`;

this.tempBodyStyle = document.createElement('style');
this.tempBodyStyle.textContent = effectiveCss;
document.body.appendChild(this.tempBodyStyle);
if (this.options.chart.styledMode) {
self.tempBodyStyle = document.createElement('style');
self.tempBodyStyle.textContent = effectiveCss;
document.body.appendChild(self.tempBodyStyle);
if (self.options.chart.styledMode) {
document.body.setAttribute('styled-mode', '');
}
}
}

// Hook into afterPrint and afterExport to revert changes made before
if (['afterPrint', 'afterExport'].indexOf(event.type) >= 0) {
if (this.tempBodyStyle) {
document.body.removeChild(this.tempBodyStyle);
delete this.tempBodyStyle;
if (this.options.chart.styledMode) {
if (self.tempBodyStyle) {
document.body.removeChild(self.tempBodyStyle);
delete self.tempBodyStyle;
if (self.options.chart.styledMode) {
document.body.removeAttribute('styled-mode');
}
}
}

this.dispatchEvent(new CustomEvent(eventList[key], customEvent));
self.dispatchEvent(new CustomEvent(eventList[key], customEvent));

if (event.type === 'legendItemClick' && this._visibilityTogglingDisabled) {
if (event.type === 'legendItemClick' && self._visibilityTogglingDisabled) {
return false;
}
};
Expand Down
28 changes: 28 additions & 0 deletions packages/charts/test/events.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,34 @@ describe('vaadin-chart events', () => {
});
});

describe('draggable', () => {
beforeEach(async () => {
chart = fixtureSync(`
<vaadin-chart type="line" additional-options='{"plotOptions": {"series": {"dragDrop": {"draggableX": true}}}}'>
<vaadin-chart-series values="[10, 20]"></vaadin-chart-series>
</vaadin-chart>
`);
await oneEvent(chart, 'chart-load');
});

it('should emit dragStart when point clicked', () => {
const spy = sinon.spy();
chart.addEventListener('point-drag-start', spy);
chart.configuration.hoverPoint = chart.configuration.series[0].points[0];
chart.$.chart.querySelector('.highcharts-container').dispatchEvent(new MouseEvent('mousedown'));
expect(spy.calledOnce).to.be.true;
});

it('should resolve point object on dragStart', () => {
const spy = sinon.spy();
chart.addEventListener('point-drag-start', spy);
chart.configuration.hoverPoint = chart.configuration.series[0].points[0];
chart.$.chart.querySelector('.highcharts-container').dispatchEvent(new MouseEvent('mousedown'));
const event = spy.firstCall.args[0];
expect(event.detail.point).to.be.deep.equal(chart.configuration.series[0].points[0]);
});
});

describe('timeline', () => {
let chart;

Expand Down
21 changes: 21 additions & 0 deletions packages/charts/test/typings/chart.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import type {
ChartDrillupEvent,
ChartLoadEvent,
ChartPointClickEvent,
ChartPointDragEvent,
ChartPointDragStartEvent,
ChartPointDropEvent,
ChartPointLegendItemClickEvent,
ChartPointMouseOutEvent,
ChartPointMouseOverEvent,
Expand Down Expand Up @@ -206,6 +209,24 @@ chart.addEventListener('point-update', (event) => {
assertType<Point>(event.detail.originalEvent.target);
});

chart.addEventListener('point-drag-start', (event) => {
assertType<ChartPointDragStartEvent>(event);
assertType<Point>(event.detail.point);
assertType<Point>(event.detail.originalEvent.target);
});

chart.addEventListener('point-drop', (event) => {
assertType<ChartPointDropEvent>(event);
assertType<Point>(event.detail.point);
assertType<Point>(event.detail.originalEvent.target);
});

chart.addEventListener('point-drag', (event) => {
assertType<ChartPointDragEvent>(event);
assertType<Point>(event.detail.point);
assertType<Point>(event.detail.originalEvent.target);
});

chart.addEventListener('xaxes-extremes-set', (event) => {
assertType<ChartXaxesExtremesSetEvent>(event);
assertType<Axis>(event.detail.axis);
Expand Down

0 comments on commit 6794915

Please sign in to comment.