mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-12-29 02:11:28 -06:00
Merge pull request #1124 from psjostrom/SDA-2533-Annotate-BI
feat: SDA-2571, SDA-2721 Add annotate BI and fix window size on windows
This commit is contained in:
commit
128bb9c0e3
@ -2,6 +2,7 @@ import { mount } from 'enzyme';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import AnnotateArea from '../src/renderer/components/annotate-area';
|
import AnnotateArea from '../src/renderer/components/annotate-area';
|
||||||
import { Tool } from '../src/renderer/components/snipping-tool';
|
import { Tool } from '../src/renderer/components/snipping-tool';
|
||||||
|
import * as analyticsHandler from './../src/app/analytics-handler';
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
paths: [],
|
paths: [],
|
||||||
@ -124,4 +125,14 @@ describe('<AnnotateArea/>', () => {
|
|||||||
strokeWidth: 5,
|
strokeWidth: 5,
|
||||||
}]);
|
}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should send capture_taken BI event on component mount', () => {
|
||||||
|
const spy = jest.spyOn(analyticsHandler.analytics, 'track');
|
||||||
|
const expectedValue = { action_type: 'annotate_added', element: 'ScreenSnippet' };
|
||||||
|
const wrapper = mount(<AnnotateArea {...defaultProps} />);
|
||||||
|
const area = wrapper.find('[data-testid="annotate-area"]');
|
||||||
|
area.simulate('mousedown', { pageX: 2, pageY: 49 });
|
||||||
|
area.simulate('mouseup');
|
||||||
|
expect(spy).toBeCalledWith(expectedValue);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import { mount, shallow } from 'enzyme';
|
import { mount, shallow } from 'enzyme';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import SnippingTool from '../src/renderer/components/snipping-tool';
|
import SnippingTool from '../src/renderer/components/snipping-tool';
|
||||||
|
import * as analyticsHandler from './../src/app/analytics-handler';
|
||||||
import { ipcRenderer } from './__mocks__/electron';
|
import { ipcRenderer } from './__mocks__/electron';
|
||||||
|
|
||||||
const waitForPromisesToResolve = () => new Promise((resolve) => setTimeout(resolve));
|
const waitForPromisesToResolve = () => new Promise((resolve) => setTimeout(resolve));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
describe('Snipping Tool', () => {
|
describe('Snipping Tool', () => {
|
||||||
it('should render correctly', () => {
|
it('should render correctly', () => {
|
||||||
const wrapper = shallow(React.createElement(SnippingTool));
|
const wrapper = shallow(React.createElement(SnippingTool));
|
||||||
@ -17,6 +22,23 @@ describe('Snipping Tool', () => {
|
|||||||
expect(spy).toBeCalledWith('snipping-tool-data', expect.any(Function));
|
expect(spy).toBeCalledWith('snipping-tool-data', expect.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should send capture_taken BI event on component mount', () => {
|
||||||
|
const spy = jest.spyOn(analyticsHandler.analytics, 'track');
|
||||||
|
const expectedValue = { action_type: 'capture_taken', element: 'ScreenSnippet' };
|
||||||
|
mount(React.createElement(SnippingTool));
|
||||||
|
expect(spy).toBeCalledWith(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send capture_sent BI event when clicking done', async () => {
|
||||||
|
const spy = jest.spyOn(analyticsHandler.analytics, 'track');
|
||||||
|
const expectedValue = { action_type: 'capture_sent', element: 'ScreenSnippet' };
|
||||||
|
const wrapper = mount(React.createElement(SnippingTool));
|
||||||
|
wrapper.find('[data-testid="done-button"]').simulate('click');
|
||||||
|
wrapper.update();
|
||||||
|
await waitForPromisesToResolve();
|
||||||
|
expect(spy).toBeCalledWith(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
it('should render pen color picker when clicked on pen', () => {
|
it('should render pen color picker when clicked on pen', () => {
|
||||||
const wrapper = shallow(React.createElement(SnippingTool));
|
const wrapper = shallow(React.createElement(SnippingTool));
|
||||||
wrapper.find('[data-testid="pen-button"]').simulate('click');
|
wrapper.find('[data-testid="pen-button"]').simulate('click');
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export interface IAnalyticsData {
|
export interface IAnalyticsData {
|
||||||
element: string;
|
element: AnalyticsElements;
|
||||||
action_type: MenuActionTypes;
|
action_type: MenuActionTypes | ScreenSnippetActionTypes;
|
||||||
action_result: string;
|
action_result?: AnalyticsActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MenuActionTypes {
|
export enum MenuActionTypes {
|
||||||
@ -13,6 +13,12 @@ export enum MenuActionTypes {
|
|||||||
REFRESH_APP_IN_IDLE = 'refresh_app_in_idle',
|
REFRESH_APP_IN_IDLE = 'refresh_app_in_idle',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ScreenSnippetActionTypes {
|
||||||
|
CAPTURE_TAKEN = 'capture_taken',
|
||||||
|
ANNOTATE_ADDED = 'annotate_added',
|
||||||
|
CAPTURE_SENT = 'capture_sent',
|
||||||
|
}
|
||||||
|
|
||||||
export enum AnalyticsActions {
|
export enum AnalyticsActions {
|
||||||
ENABLED = 'ON',
|
ENABLED = 'ON',
|
||||||
DISABLED = 'OFF',
|
DISABLED = 'OFF',
|
||||||
@ -20,6 +26,7 @@ export enum AnalyticsActions {
|
|||||||
|
|
||||||
export enum AnalyticsElements {
|
export enum AnalyticsElements {
|
||||||
MENU = 'Menu',
|
MENU = 'Menu',
|
||||||
|
SCREEN_SNIPPET = 'ScreenSnippet',
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_EVENT_QUEUE_LENGTH = 50;
|
const MAX_EVENT_QUEUE_LENGTH = 50;
|
||||||
@ -61,7 +68,7 @@ class Analytics {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.analyticsEventQueue.push(eventData);
|
this.analyticsEventQueue.push(eventData);
|
||||||
// don't store more than 100 msgs. keep most recent log msgs.
|
// don't store more than 50 msgs. keep most recent log msgs.
|
||||||
if (this.analyticsEventQueue.length > MAX_EVENT_QUEUE_LENGTH) {
|
if (this.analyticsEventQueue.length > MAX_EVENT_QUEUE_LENGTH) {
|
||||||
this.analyticsEventQueue.shift();
|
this.analyticsEventQueue.shift();
|
||||||
}
|
}
|
||||||
|
@ -991,8 +991,8 @@ export class WindowHandler {
|
|||||||
|
|
||||||
const opts: ICustomBrowserWindowConstructorOpts = this.getWindowOpts(
|
const opts: ICustomBrowserWindowConstructorOpts = this.getWindowOpts(
|
||||||
{
|
{
|
||||||
width: annotateAreaWidth,
|
width: annotateAreaWidth < MIN_WIDTH ? MIN_WIDTH : annotateAreaWidth,
|
||||||
height: windowHeight,
|
height: windowHeight < MIN_HEIGHT ? MIN_HEIGHT : windowHeight,
|
||||||
minHeight: MIN_HEIGHT,
|
minHeight: MIN_HEIGHT,
|
||||||
minWidth: MIN_WIDTH,
|
minWidth: MIN_WIDTH,
|
||||||
modal: false,
|
modal: false,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { LazyBrush } from 'lazy-brush';
|
import { LazyBrush } from 'lazy-brush';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { analytics, AnalyticsElements, ScreenSnippetActionTypes } from './../../app/analytics-handler';
|
||||||
import { IDimensions, IPath, IPoint, Tool } from './snipping-tool';
|
import { IDimensions, IPath, IPoint, Tool } from './snipping-tool';
|
||||||
|
|
||||||
const { useState } = React;
|
const { useState } = React;
|
||||||
@ -224,6 +225,14 @@ const AnnotateArea: React.FunctionComponent<IAnnotateAreaProps> = ({
|
|||||||
addPathPoint(e);
|
addPathPoint(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
stopDrawing();
|
||||||
|
analytics.track({
|
||||||
|
element: AnalyticsElements.SCREEN_SNIPPET,
|
||||||
|
action_type: ScreenSnippetActionTypes.ANNOTATE_ADDED,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getAnnotateWrapperStyle = () => {
|
const getAnnotateWrapperStyle = () => {
|
||||||
const shouldShowScrollBars =
|
const shouldShowScrollBars =
|
||||||
imageDimensions.height > annotateAreaDimensions.height ||
|
imageDimensions.height > annotateAreaDimensions.height ||
|
||||||
@ -246,7 +255,7 @@ const AnnotateArea: React.FunctionComponent<IAnnotateAreaProps> = ({
|
|||||||
width={imageDimensions.width}
|
width={imageDimensions.width}
|
||||||
height={imageDimensions.height}
|
height={imageDimensions.height}
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onMouseUp={stopDrawing}
|
onMouseUp={handleMouseUp}
|
||||||
onMouseMove={handleMouseMove}
|
onMouseMove={handleMouseMove}
|
||||||
onMouseLeave={stopDrawing}
|
onMouseLeave={stopDrawing}
|
||||||
>
|
>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { i18n } from '../../common/i18n-preload';
|
import { i18n } from '../../common/i18n-preload';
|
||||||
|
import { analytics, AnalyticsElements, ScreenSnippetActionTypes } from './../../app/analytics-handler';
|
||||||
import AnnotateArea from './annotate-area';
|
import AnnotateArea from './annotate-area';
|
||||||
import ColorPickerPill, { IColor } from './color-picker-pill';
|
import ColorPickerPill, { IColor } from './color-picker-pill';
|
||||||
|
|
||||||
@ -89,9 +90,13 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
|
|||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
ipcRenderer.once('snipping-tool-data', getSnipImageData);
|
ipcRenderer.once('snipping-tool-data', getSnipImageData);
|
||||||
return () => {
|
analytics.track({
|
||||||
ipcRenderer.removeListener('snipping-tool-data', getSnipImageData);
|
element: AnalyticsElements.SCREEN_SNIPPET,
|
||||||
};
|
action_type: ScreenSnippetActionTypes.CAPTURE_TAKEN,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
ipcRenderer.removeListener('snipping-tool-data', getSnipImageData);
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Hook that alerts clicks outside of the passed refs
|
// Hook that alerts clicks outside of the passed refs
|
||||||
@ -176,6 +181,16 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
|
|||||||
return 'NO CANVAS';
|
return 'NO CANVAS';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const backgroundImage = document.getElementById('backgroundImage') as HTMLImageElement;
|
||||||
|
|
||||||
|
// Fast lane in case there is no drawn SVG paths
|
||||||
|
if (paths.length === 0) {
|
||||||
|
ctx.drawImage(backgroundImage, 0, 0);
|
||||||
|
// Extracts base 64 png img data from the canvas
|
||||||
|
const data = canvas.toDataURL('image/png');
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
// Creates an in memory img without adding it to the DOM
|
// Creates an in memory img without adding it to the DOM
|
||||||
const img = document.createElement('img');
|
const img = document.createElement('img');
|
||||||
|
|
||||||
@ -187,7 +202,6 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
|
|||||||
'src',
|
'src',
|
||||||
'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData))),
|
'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData))),
|
||||||
);
|
);
|
||||||
const backgroundImage = document.getElementById('backgroundImage') as HTMLImageElement;
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Listens to when the img is loaded in memory and adds the data from the SVG paths + screenSnippet to the canvas
|
// Listens to when the img is loaded in memory and adds the data from the SVG paths + screenSnippet to the canvas
|
||||||
@ -233,6 +247,10 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
|
|||||||
|
|
||||||
const done = async () => {
|
const done = async () => {
|
||||||
const base64PngData = await getBase64PngData();
|
const base64PngData = await getBase64PngData();
|
||||||
|
analytics.track({
|
||||||
|
element: AnalyticsElements.SCREEN_SNIPPET,
|
||||||
|
action_type: ScreenSnippetActionTypes.CAPTURE_SENT,
|
||||||
|
});
|
||||||
ipcRenderer.send('upload-snippet', { screenSnippetPath, base64PngData });
|
ipcRenderer.send('upload-snippet', { screenSnippetPath, base64PngData });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user