fix: add listener to repaint on visibility change for canvas (#28568)
This commit is contained in:
parent
0d5aec12d4
commit
62a0336425
|
|
@ -250,6 +250,7 @@
|
|||
"ignore-styles": "^5.0.1",
|
||||
"imports-loader": "^3.1.1",
|
||||
"jest": "^26.6.3",
|
||||
"jest-canvas-mock": "^2.5.2",
|
||||
"jest-environment-enzyme": "^7.1.2",
|
||||
"jest-enzyme": "^7.1.2",
|
||||
"jest-websocket-mock": "^2.2.0",
|
||||
|
|
@ -31171,6 +31172,12 @@
|
|||
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
||||
"integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4="
|
||||
},
|
||||
"node_modules/cssfontparser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz",
|
||||
"integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cssnano": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.3.tgz",
|
||||
|
|
@ -41175,6 +41182,16 @@
|
|||
"node": ">= 10.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-canvas-mock": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz",
|
||||
"integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cssfontparser": "^1.2.1",
|
||||
"moo-color": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-changed-files": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz",
|
||||
|
|
@ -52539,6 +52556,21 @@
|
|||
"resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz",
|
||||
"integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw=="
|
||||
},
|
||||
"node_modules/moo-color": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz",
|
||||
"integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/moo-color/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mousetrap": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz",
|
||||
|
|
@ -96422,6 +96454,12 @@
|
|||
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
||||
"integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4="
|
||||
},
|
||||
"cssfontparser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz",
|
||||
"integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==",
|
||||
"dev": true
|
||||
},
|
||||
"cssnano": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.3.tgz",
|
||||
|
|
@ -104272,6 +104310,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"jest-canvas-mock": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz",
|
||||
"integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cssfontparser": "^1.2.1",
|
||||
"moo-color": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jest-changed-files": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz",
|
||||
|
|
@ -112794,6 +112842,23 @@
|
|||
"resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz",
|
||||
"integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw=="
|
||||
},
|
||||
"moo-color": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz",
|
||||
"integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "^1.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"mousetrap": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz",
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@
|
|||
"ignore-styles": "^5.0.1",
|
||||
"imports-loader": "^3.1.1",
|
||||
"jest": "^26.6.3",
|
||||
"jest-canvas-mock": "^2.5.2",
|
||||
"jest-environment-enzyme": "^7.1.2",
|
||||
"jest-enzyme": "^7.1.2",
|
||||
"jest-websocket-mock": "^2.2.0",
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ class Dashboard extends React.PureComponent {
|
|||
this.appliedFilters = props.activeFilters ?? {};
|
||||
this.appliedOwnDataCharts = props.ownDataCharts ?? {};
|
||||
this.onVisibilityChange = this.onVisibilityChange.bind(this);
|
||||
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
||||
this.repaintCanvas = this.repaintCanvas.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -192,6 +194,24 @@ class Dashboard extends React.PureComponent {
|
|||
this.props.actions.clearDataMaskState();
|
||||
}
|
||||
|
||||
repaintCanvas(canvas, ctx, imageBitmap) {
|
||||
// Clear the canvas
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw the copied content
|
||||
ctx.drawImage(imageBitmap, 0, 0);
|
||||
}
|
||||
|
||||
handleVisibilityChange() {
|
||||
this.canvases.forEach(canvas => {
|
||||
const ctx = canvas.getContext('2d');
|
||||
createImageBitmap(canvas).then(imageBitmap => {
|
||||
// Call the repaintCanvas function with canvas, ctx, and imageBitmap
|
||||
this.repaintCanvas(canvas, ctx, imageBitmap);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onVisibilityChange() {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
// from visible to hidden
|
||||
|
|
@ -199,6 +219,7 @@ class Dashboard extends React.PureComponent {
|
|||
start_offset: Logger.getTimestamp(),
|
||||
ts: new Date().getTime(),
|
||||
};
|
||||
this.canvases = document.querySelectorAll('canvas');
|
||||
} else if (document.visibilityState === 'visible') {
|
||||
// from hidden to visible
|
||||
const logStart = this.visibilityEventData.start_offset;
|
||||
|
|
@ -206,6 +227,8 @@ class Dashboard extends React.PureComponent {
|
|||
...this.visibilityEventData,
|
||||
duration: Logger.getTimestamp() - logStart,
|
||||
});
|
||||
// for chrome to ensure that the canvas doesn't disappear
|
||||
this.handleVisibilityChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import 'jest-canvas-mock';
|
||||
|
||||
import Dashboard from 'src/dashboard/components/Dashboard';
|
||||
import { CHART_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
|
@ -38,6 +39,7 @@ import { dashboardLayout } from 'spec/fixtures/mockDashboardLayout';
|
|||
import dashboardState from 'spec/fixtures/mockDashboardState';
|
||||
import { sliceEntitiesForChart as sliceEntities } from 'spec/fixtures/mockSliceEntities';
|
||||
import { getAllActiveFilters } from 'src/dashboard/util/activeAllDashboardFilters';
|
||||
import { Logger, LOG_ACTIONS_HIDE_BROWSER_TAB } from '../../logger/LogUtils';
|
||||
|
||||
describe('Dashboard', () => {
|
||||
const props = {
|
||||
|
|
@ -245,5 +247,136 @@ describe('Dashboard', () => {
|
|||
expect(refreshSpy.callCount).toBe(1);
|
||||
expect(refreshSpy.getCall(0).args[0]).toEqual([]);
|
||||
});
|
||||
|
||||
// The canvas is cleared using the clearRect method.
|
||||
it('should clear the canvas using clearRect method', () => {
|
||||
// Arrange
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const imageBitmap = new ImageBitmap(100, 100);
|
||||
|
||||
// Act
|
||||
wrapper.instance().repaintCanvas(canvas, ctx, imageBitmap);
|
||||
|
||||
// Assert
|
||||
expect(ctx.clearRect).toHaveBeenCalledWith(
|
||||
0,
|
||||
0,
|
||||
canvas.width,
|
||||
canvas.height,
|
||||
);
|
||||
});
|
||||
|
||||
// The canvas width and height are 0.
|
||||
it('should recreate the canvas with the same dimensions', () => {
|
||||
// Arrange
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const imageBitmap = new ImageBitmap(100, 100);
|
||||
|
||||
// Act
|
||||
wrapper.instance().repaintCanvas(canvas, ctx, imageBitmap);
|
||||
|
||||
// Assert
|
||||
const { width, height } = ctx.canvas;
|
||||
expect(canvas.width).toBe(width);
|
||||
expect(canvas.height).toBe(height);
|
||||
});
|
||||
|
||||
// When the document visibility state changes to 'hidden', the method sets the 'visibilityEventData' object with a 'start_offset' and 'ts' properties, and queries all canvas elements on the page and stores them in the 'canvases' property.
|
||||
it('should set visibilityEventData and canvases when document visibility state changes to "hidden"', () => {
|
||||
// Initialize the class object with props
|
||||
const props = {
|
||||
activeFilters: {},
|
||||
ownDataCharts: {},
|
||||
actions: {
|
||||
logEvent: jest.fn(),
|
||||
},
|
||||
layout: {},
|
||||
dashboardInfo: {},
|
||||
dashboardState: {
|
||||
editMode: false,
|
||||
isPublished: false,
|
||||
hasUnsavedChanges: false,
|
||||
},
|
||||
chartConfiguration: {},
|
||||
};
|
||||
|
||||
const DATE_TO_USE = new Date('2020');
|
||||
const OrigDate = Date;
|
||||
global.Date = jest.fn(() => DATE_TO_USE);
|
||||
global.Date.UTC = OrigDate.UTC;
|
||||
global.Date.parse = OrigDate.parse;
|
||||
global.Date.now = OrigDate.now;
|
||||
|
||||
// Your test code here
|
||||
|
||||
const dashboard = new Dashboard(props);
|
||||
|
||||
// Mock the return value of document.visibilityState
|
||||
jest.spyOn(document, 'visibilityState', 'get').mockReturnValue('hidden');
|
||||
// mock Logger.getTimestamp() to return a fixed value
|
||||
jest.spyOn(Logger, 'getTimestamp').mockReturnValue(1234567890);
|
||||
|
||||
// Invoke the method
|
||||
dashboard.onVisibilityChange();
|
||||
|
||||
// Assert that visibilityEventData is set correctly
|
||||
expect(dashboard.visibilityEventData).toEqual({
|
||||
start_offset: 1234567890,
|
||||
ts: DATE_TO_USE.getTime(),
|
||||
});
|
||||
|
||||
// Assert that canvases are queried correctly
|
||||
expect(dashboard.canvases).toEqual(expect.any(NodeList));
|
||||
|
||||
// Restore the original implementation of document.visibilityState
|
||||
jest.restoreAllMocks();
|
||||
// After your test
|
||||
global.Date = OrigDate;
|
||||
});
|
||||
|
||||
// When the document visibility state changes to 'visible', the method logs an event and calls the 'handleVisibilityChange' method.
|
||||
it('should log an event and call handleVisibilityChange when document visibility state changes to "visible"', () => {
|
||||
// Initialize the class object
|
||||
const dashboard = new Dashboard({ activeFilters: {} });
|
||||
|
||||
// Mock the props and actions
|
||||
dashboard.props = {
|
||||
actions: {
|
||||
logEvent: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
// Mock the visibilityEventData
|
||||
dashboard.visibilityEventData = {
|
||||
start_offset: 123,
|
||||
ts: 456,
|
||||
};
|
||||
|
||||
// Mock the handleVisibilityChange method
|
||||
dashboard.handleVisibilityChange = jest.fn();
|
||||
|
||||
// Mock the document.visibilityState property
|
||||
jest.spyOn(document, 'visibilityState', 'get').mockReturnValue('visible');
|
||||
|
||||
// Invoke the method
|
||||
dashboard.onVisibilityChange();
|
||||
|
||||
// Assert that logEvent is called with the correct arguments
|
||||
expect(dashboard.props.actions.logEvent).toHaveBeenCalledWith(
|
||||
LOG_ACTIONS_HIDE_BROWSER_TAB,
|
||||
{
|
||||
...dashboard.visibilityEventData,
|
||||
duration: expect.any(Number),
|
||||
},
|
||||
);
|
||||
|
||||
// Assert that handleVisibilityChange is called
|
||||
expect(dashboard.handleVisibilityChange).toHaveBeenCalled();
|
||||
|
||||
// Restore the original implementation of document.visibilityState
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue