diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e74e609cc0..5a23e716a1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## [next]
+- tests(): migrate target hit tests to jest and drag and drop test to playwright [#9333](https://github.com/fabricjs/fabric.js/pull/9333)
- fix(SVGParser): avoid crashing on SVG that use @import css feature [#9602](https://github.com/fabricjs/fabric.js/pull/9602)
- fix(): compositionEnd event handler is not registered correctly. (regression from f91362c ) [#9610](https://github.com/fabricjs/fabric.js/pull/9610)
- ci(): Add a test case from the multiple selection use case for groups [#9599](https://github.com/fabricjs/fabric.js/pull/9599)
diff --git a/e2e/tests/text/drag&drop/index.html b/e2e/tests/text/drag&drop/index.html
new file mode 100644
index 00000000000..a7a979605e1
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.html
@@ -0,0 +1,2 @@
+
+
diff --git a/e2e/tests/text/drag&drop/index.spec.ts b/e2e/tests/text/drag&drop/index.spec.ts
index bcebbfbf3a4..db7c957e305 100644
--- a/e2e/tests/text/drag&drop/index.spec.ts
+++ b/e2e/tests/text/drag&drop/index.spec.ts
@@ -1,16 +1,19 @@
import type { Locator, Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
+import type { IText } from '../../../..';
import setup from '../../../setup';
+import { TextUtil } from '../../../utils/TextUtil';
import { binaryToBuffer } from '../../../utils/binaryToBuffer';
+import { CanvasUtil } from '../../../utils/CanvasUtil';
setup();
const dragA = 'fabric';
const dragB = 'em ipsum\ndolor\nsit Amet2\nconsectge';
-test('Drag & Drop', async ({ page }) => {
+const selectFabricInA = (page: Page) => {
const canvas = page.locator('canvas').nth(1);
- await test.step(`select "${dragA}" in A`, async () => {
+ return test.step(`select "${dragA}" in A`, async () => {
await canvas.click({
position: {
x: 130,
@@ -25,17 +28,65 @@ test('Drag & Drop', async ({ page }) => {
},
});
});
- await test.step('drag & drop to end', async () => {
+};
+
+const readEventStream = async (page: Page) => {
+ const data = await new CanvasUtil(page).executeInBrowser((canvas) =>
+ canvas.readEventStream()
+ );
+ return JSON.stringify(data, null, 2);
+};
+
+test('Drag & Drop', async ({ page }) => {
+ const canvas = page.locator('canvas').nth(1);
+ const textarea = page.locator('#textarea');
+ const a = new TextUtil(page, 'a');
+ const b = new TextUtil(page, 'b');
+
+ await selectFabricInA(page);
+
+ await test.step('click sets the cursor', async () => {
+ await page.mouse.move(130, 50);
await page.mouse.down();
- await page.mouse.move(0, 140, { steps: 40 });
- await page.mouse.move(435, 55, { steps: 40 });
+ expect(
+ await a.executeInBrowser((text) => [
+ text['draggableTextDelegate'].isActive(),
+ text.shouldStartDragging(),
+ ])
+ ).toEqual([true, true]);
+ expect(await a.isCursorActive()).toBeFalsy();
+ await page.mouse.up();
+ expect(
+ await a.executeInBrowser((text) => [
+ text['draggableTextDelegate'].isActive(),
+ text.shouldStartDragging(),
+ ])
+ ).toEqual([false, false]);
+ await a.expectObjectToMatch({
+ selectionStart: 3,
+ selectionEnd: 3,
+ });
+ expect(await a.isCursorActive()).toBeTruthy();
+ });
+
+ await selectFabricInA(page);
+
+ // clean the stream
+ await readEventStream(page);
+
+ await test.step('drag A & drop on self at end', async () => {
+ await page.mouse.down();
+ await page.mouse.move(0, 140, { steps: 10 });
+ await page.mouse.move(435, 55, { steps: 10 });
expect(
await canvas.screenshot(),
`1. drag "${dragA}" over "lor|em" (A => B)`
).toMatchSnapshot({
name: '1.drag-fabric-over-lor|em.png',
});
- await page.mouse.move(240, 140, { steps: 40 });
+ await page.mouse.move(400, 70, { steps: 10 });
+ await page.mouse.move(250, 130, { steps: 10 });
+ await page.mouse.move(240, 140, { steps: 10 });
expect(
await canvas.screenshot(),
`2. before dropping "${dragA}" => "sandbox|" (A => A)`
@@ -49,6 +100,9 @@ test('Drag & Drop', async ({ page }) => {
).toMatchSnapshot({
name: '3.drop-fabric-after-sandbox.png',
});
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: '3.events.json',
+ });
});
await test.step(`drag & drop to B(3) = "lor|${dragA}|em"`, async () => {
@@ -62,12 +116,18 @@ test('Drag & Drop', async ({ page }) => {
y: 55,
},
});
+ expect(await page.evaluate(() => document.activeElement)).toBe(
+ await b.executeInBrowser((text) => text.hiddenTextarea)
+ );
expect(
await canvas.screenshot(),
`4. drag & drop "${dragA}" => "lor|${dragA}|em" (A => B(3))`
).toMatchSnapshot({
name: '4.drop--lor|fabric|em.png',
});
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: '4.events.json',
+ });
});
await test.step('select B', async () => {
@@ -80,6 +140,7 @@ test('Drag & Drop', async ({ page }) => {
await page.mouse.down();
await page.mouse.move(580, 300, { steps: 40 });
await page.mouse.up();
+ await readEventStream(page);
});
await test.step(`drag & drop to A(4) = ".js |${dragB}|sandbox"`, async () => {
@@ -93,16 +154,166 @@ test('Drag & Drop', async ({ page }) => {
y: 55,
},
});
+ expect(await page.evaluate(() => document.activeElement)).toBe(
+ await a.executeInBrowser((text) => text.hiddenTextarea)
+ );
expect(
await canvas.screenshot(),
`5. drag & drop "${dragB}" => ".js |${dragB}|sandbox" (B => A(4))`
).toMatchSnapshot({
name: '5..js |em ips.png',
});
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: '5.events.json',
+ });
+ });
+
+ await test.step(`drag & drop to textarea = ".js |${dragB}|sandbox"`, async () => {
+ await canvas.dragTo(textarea, {
+ sourcePosition: {
+ x: 120,
+ y: 55,
+ },
+ });
+ // expect(await page.evaluate(() => document.activeElement)).toBe(
+ // await textarea.elementHandle()
+ // );
+ expect(
+ await page.screenshot(),
+ `6. drag & drop ".js |${dragB}|sandbox" to textarea (A => textarea)`
+ ).toMatchSnapshot({
+ name: '6.drop-textarea.png',
+ });
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: '6.events.json',
+ });
+ });
+
+ await test.step(`drag & drop "dolor" from textarea to B = "lor|dolor|fabrictur"`, async () => {
+ await page.mouse.move(25, 527);
+ await page.mouse.dblclick(25, 527);
+ await page.mouse.down();
+ await page.mouse.move(25, 527);
+ await page.mouse.move(25, 550, { steps: 10 });
+ await page.mouse.move(435, 55, { steps: 10 });
+ await canvas.hover({ position: { x: 435, y: 55 } });
+ await page.mouse.up();
+ expect(await page.evaluate(() => document.activeElement)).toBe(
+ await b.executeInBrowser((text) => text.hiddenTextarea)
+ );
+ expect(
+ await page.screenshot(),
+ `7. drag & drop "dolor" to B = "lor|dolor|fabrictur" (textarea => B)`
+ ).toMatchSnapshot({
+ name: '7.drop-textarea-to-B-lor|dolor|fabrictur.png',
+ });
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: '7.events.json',
+ });
+ });
+});
+
+for (const options of [
+ {
+ disabled: 'onDragStart',
+ exec: (text: IText) => (text.onDragStart = () => false),
+ expected: {
+ text: 'fabric.js sandbox',
+ selectionStart: 0,
+ selectionEnd: 6,
+ },
+ },
+ {
+ disabled: 'draggableTextDelegate#start',
+ exec: (text: IText) => (text['draggableTextDelegate'].start = () => false),
+ expected: {
+ text: 'fabric.js sandbox',
+ selectionStart: 3,
+ selectionEnd: 17,
+ },
+ },
+ {
+ disabled: 'draggableTextDelegate#isActive',
+ exec: (text: IText) =>
+ (text['draggableTextDelegate'].isActive = () => false),
+ expected: {
+ text: 'fabric.js sandbox',
+ selectionStart: 0,
+ selectionEnd: 6,
+ },
+ },
+] as const) {
+ test(`Disabling Drag & Drop by disabling ${options.disabled}`, async ({
+ page,
+ }) => {
+ const a = new TextUtil(page, 'a');
+ await test.step('disable dragging', () => a.executeInBrowser(options.exec));
+ await selectFabricInA(page);
+ await readEventStream(page);
+
+ await test.step('drag to end of text', async () => {
+ await page.mouse.down();
+ await page.mouse.move(240, 140, { steps: 40 });
+ a.expectObjectToMatch(options.expected);
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: `disabling-drag-${options.disabled}.events.json`,
+ });
+ });
+ });
+}
+
+test('Disabling Drop', async ({ page }) => {
+ const canvas = page.locator('canvas').nth(1);
+ const a = new TextUtil(page, 'a');
+ const b = new TextUtil(page, 'b');
+ await test.step('disable dropping', () => {
+ a.executeInBrowser((text) => (text.canDrop = () => false));
+ b.executeInBrowser((text) => (text.canDrop = () => false));
+ });
+
+ await test.step('drop A on self', async () => {
+ await selectFabricInA(page);
+ await readEventStream(page);
+ await canvas.dragTo(canvas, {
+ sourcePosition: {
+ x: 130,
+ y: 40,
+ },
+ targetPosition: {
+ x: 240,
+ y: 140,
+ },
+ });
+ await a.expectObjectToMatch({ text: 'fabric.js sandbox' });
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: 'disabling-drop-A.events.json',
+ });
+ });
+
+ await test.step('drop A on B', async () => {
+ await selectFabricInA(page);
+ await readEventStream(page);
+ await canvas.dragTo(canvas, {
+ sourcePosition: {
+ x: 130,
+ y: 40,
+ },
+ targetPosition: {
+ x: 435,
+ y: 55,
+ },
+ });
+ await a.expectObjectToMatch({ text: 'fabric.js sandbox' });
+ await b.expectObjectToMatch({
+ text: 'lorem ipsum\ndolor\nsit Amet2\nconsectgetur',
+ });
+ expect(await readEventStream(page)).toMatchSnapshot({
+ name: 'disabling-drop-B.events.json',
+ });
});
});
-async function waitForDragImage(
+async function waitForDataTransfer(
page: Page,
canvas: Locator,
{ x, y }: { x: number; y: number }
@@ -116,11 +327,28 @@ async function waitForDragImage(
});
await page.mouse.down();
const dataTransfer = await page.evaluateHandle(() =>
- Object.defineProperty(new DataTransfer(), 'setDragImage', {
- value: (image, x, y) =>
- window.dispatchEvent(
- new CustomEvent('drag:image', { detail: { image, x, y } })
- ),
+ Object.defineProperties(new DataTransfer(), {
+ __data: {
+ value: {},
+ },
+ setDragImage: {
+ value(image, x, y) {
+ window.dispatchEvent(
+ new CustomEvent('drag:data', {
+ detail: { image, x, y, data: this.__data },
+ })
+ );
+ },
+ },
+ setData: {
+ value(type, value) {
+ let out = value;
+ try {
+ out = JSON.parse(value);
+ } catch (error) {}
+ this.__data[type] = out;
+ },
+ },
})
);
return [
@@ -128,21 +356,25 @@ async function waitForDragImage(
page
.evaluate(
() =>
- new Promise<{ image: string; x: number; y: number }>((resolve) =>
+ new Promise<{
+ image: string;
+ x: number;
+ y: number;
+ data: Record;
+ }>((resolve) =>
window.addEventListener(
- 'drag:image',
- ({ detail: { image, x, y } }) =>
+ 'drag:data',
+ ({ detail: { image, ...rest } }) =>
resolve({
image: image.toDataURL(`image/png`, 1),
- x,
- y,
+ ...rest,
}),
{ once: true }
)
)
)
- .then(({ x, y, image }) => {
- return [binaryToBuffer(image), { x, y }] as const;
+ .then(({ x, y, image, ...data }) => {
+ return [binaryToBuffer(image), { x, y }, data] as const;
}),
] as const;
});
@@ -162,18 +394,18 @@ test('Drag Image A', async ({ page }) => {
});
await test.step('start dragging', async () => {
- const [dragEvent, trigger] = await waitForDragImage(page, canvas, {
+ const [dragEvent, trigger] = await waitForDataTransfer(page, canvas, {
x: 130,
y: 40,
});
await canvas.dispatchEvent('dragstart', dragEvent);
- const [image, position] = await trigger;
+ const [image, position, data] = await trigger;
expect(image, `drag image A: "${dragA}"`).toMatchSnapshot({
name: 'drag-image-fabric.png',
maxDiffPixelRatio: 0.03,
});
expect(
- JSON.stringify(position, null, 2),
+ JSON.stringify({ position, ...data }, null, 2),
`drag image A position: "${dragA}"`
).toMatchSnapshot({
name: 'drag-image-fabric.json',
@@ -199,17 +431,17 @@ test('Drag Image B', async ({ page }) => {
});
await test.step('start dragging', async () => {
- const [dragEvent, trigger] = await waitForDragImage(page, canvas, {
+ const [dragEvent, trigger] = await waitForDataTransfer(page, canvas, {
x: 500,
y: 280,
});
await canvas.dispatchEvent('dragstart', dragEvent);
- const [image, position] = await trigger;
+ const [image, position, data] = await trigger;
expect(image, `drag image B: "${dragB}"`).toMatchSnapshot({
name: 'drag-image-em---tge.png',
});
expect(
- JSON.stringify(position, null, 2),
+ JSON.stringify({ position, ...data }, null, 2),
`drag image B position: "${dragB}"`
).toMatchSnapshot({
name: 'drag-image-em---tge.json',
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/3-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/3-events.json
new file mode 100644
index 00000000000..c349f283871
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/3-events.json
@@ -0,0 +1,338 @@
+[
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 117,
+ "y": 50
+ },
+ "scenePoint": {
+ "x": 117,
+ "y": 50
+ },
+ "pointer": {
+ "x": 117,
+ "y": 50
+ },
+ "absolutePointer": {
+ "x": 117,
+ "y": 50
+ }
+ },
+ "a"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 39,
+ "y": 110
+ },
+ "scenePoint": {
+ "x": 39,
+ "y": 110
+ },
+ "pointer": {
+ "x": 39,
+ "y": 110
+ },
+ "absolutePointer": {
+ "x": 39,
+ "y": 110
+ }
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 87,
+ "y": 123
+ },
+ "scenePoint": {
+ "x": 87,
+ "y": 123
+ },
+ "pointer": {
+ "x": 87,
+ "y": 123
+ },
+ "absolutePointer": {
+ "x": 87,
+ "y": 123
+ }
+ },
+ "a"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 261,
+ "y": 89
+ },
+ "scenePoint": {
+ "x": 261,
+ "y": 89
+ },
+ "pointer": {
+ "x": 261,
+ "y": 89
+ },
+ "absolutePointer": {
+ "x": 261,
+ "y": 89
+ }
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "b"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "viewportPoint": {
+ "x": 385,
+ "y": 76
+ },
+ "scenePoint": {
+ "x": 385,
+ "y": 76
+ },
+ "pointer": {
+ "x": 385,
+ "y": 76
+ },
+ "absolutePointer": {
+ "x": 385,
+ "y": 76
+ }
+ },
+ "b"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 250,
+ "y": 130
+ },
+ "scenePoint": {
+ "x": 250,
+ "y": 130
+ },
+ "pointer": {
+ "x": 250,
+ "y": 130
+ },
+ "absolutePointer": {
+ "x": 250,
+ "y": 130
+ }
+ },
+ "a"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "viewportPoint": {
+ "x": 240,
+ "y": 140
+ },
+ "scenePoint": {
+ "x": 240,
+ "y": 140
+ },
+ "didDrop": false,
+ "pointer": {
+ "x": 240,
+ "y": 140
+ },
+ "absolutePointer": {
+ "x": 240,
+ "y": 140
+ }
+ },
+ "canvas"
+ ],
+ [
+ "changed",
+ {
+ "index": 11,
+ "action": "drop"
+ },
+ "a"
+ ],
+ [
+ "text:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "viewportPoint": {
+ "x": 240,
+ "y": 140
+ },
+ "scenePoint": {
+ "x": 240,
+ "y": 140
+ },
+ "didDrop": true,
+ "dropTarget": "a",
+ "pointer": {
+ "x": 240,
+ "y": 140
+ },
+ "absolutePointer": {
+ "x": 240,
+ "y": 140
+ }
+ },
+ "a"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": true,
+ "dropTarget": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": true,
+ "dropTarget": "a"
+ },
+ "a"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/4-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/4-events.json
new file mode 100644
index 00000000000..103c7bc437e
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/4-events.json
@@ -0,0 +1,221 @@
+[
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "dragSource": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "nextTarget": "b",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "previousTarget": "a",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "b"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "dragSource": "a",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "didDrop": false,
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "b"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "b"
+ },
+ "canvas"
+ ],
+ [
+ "changed",
+ {
+ "index": 3,
+ "action": "drop"
+ },
+ "b"
+ ],
+ [
+ "text:changed",
+ {
+ "target": "b"
+ },
+ "canvas"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "dragSource": "a",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "didDrop": true,
+ "dropTarget": "b",
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "b"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": true,
+ "dropTarget": "b"
+ },
+ "canvas"
+ ],
+ [
+ "changed",
+ {
+ "index": 11,
+ "action": "dragend"
+ },
+ "a"
+ ],
+ [
+ "text:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": true,
+ "dropTarget": "b"
+ },
+ "a"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/5-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/5-events.json
new file mode 100644
index 00000000000..4688bb9b7fc
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/5-events.json
@@ -0,0 +1,221 @@
+[
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b"
+ },
+ "canvas"
+ ],
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b"
+ },
+ "b"
+ ],
+ [
+ "dragenter",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "b"
+ },
+ "canvas"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "dragSource": "b",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "nextTarget": "a",
+ "viewportPoint": {
+ "x": 120,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 120,
+ "y": 55
+ },
+ "pointer": {
+ "x": 120,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 120,
+ "y": 55
+ }
+ },
+ "b"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "b",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "previousTarget": "b",
+ "viewportPoint": {
+ "x": 120,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 120,
+ "y": 55
+ },
+ "pointer": {
+ "x": 120,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 120,
+ "y": 55
+ }
+ },
+ "a"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "b",
+ "viewportPoint": {
+ "x": 120,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 120,
+ "y": 55
+ },
+ "didDrop": false,
+ "pointer": {
+ "x": 120,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 120,
+ "y": 55
+ }
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "a"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "changed",
+ {
+ "index": 4,
+ "action": "drop"
+ },
+ "a"
+ ],
+ [
+ "text:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "b",
+ "viewportPoint": {
+ "x": 120,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 120,
+ "y": 55
+ },
+ "didDrop": true,
+ "dropTarget": "a",
+ "pointer": {
+ "x": 120,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 120,
+ "y": 55
+ }
+ },
+ "a"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "dragSource": "b",
+ "didDrop": true,
+ "dropTarget": "a"
+ },
+ "canvas"
+ ],
+ [
+ "changed",
+ {
+ "index": 9,
+ "action": "dragend"
+ },
+ "b"
+ ],
+ [
+ "text:changed",
+ {
+ "target": "b"
+ },
+ "canvas"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "dragSource": "b",
+ "didDrop": true,
+ "dropTarget": "a"
+ },
+ "b"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/6-drop-textarea.png b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/6-drop-textarea.png
new file mode 100644
index 00000000000..d0c22431fab
Binary files /dev/null and b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/6-drop-textarea.png differ
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/6-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/6-events.json
new file mode 100644
index 00000000000..a32dee8a559
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/6-events.json
@@ -0,0 +1,50 @@
+[
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "a"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": true,
+ "dropTarget": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": true,
+ "dropTarget": "a"
+ },
+ "a"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/7-drop-textarea-to-B-lor-dolor-fabrictur.png b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/7-drop-textarea-to-B-lor-dolor-fabrictur.png
new file mode 100644
index 00000000000..2727eaa1eee
Binary files /dev/null and b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/7-drop-textarea-to-B-lor-dolor-fabrictur.png differ
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/7-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/7-events.json
new file mode 100644
index 00000000000..fd6d197556b
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/7-events.json
@@ -0,0 +1,207 @@
+[
+ [
+ "dragenter",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "subTargets": []
+ },
+ "canvas"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 107,
+ "y": 451
+ },
+ "scenePoint": {
+ "x": 107,
+ "y": 451
+ },
+ "pointer": {
+ "x": 107,
+ "y": 451
+ },
+ "absolutePointer": {
+ "x": 107,
+ "y": 451
+ }
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 189,
+ "y": 352
+ },
+ "scenePoint": {
+ "x": 189,
+ "y": 352
+ },
+ "pointer": {
+ "x": 189,
+ "y": 352
+ },
+ "absolutePointer": {
+ "x": 189,
+ "y": 352
+ }
+ },
+ "a"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 271,
+ "y": 253
+ },
+ "scenePoint": {
+ "x": 271,
+ "y": 253
+ },
+ "pointer": {
+ "x": 271,
+ "y": 253
+ },
+ "absolutePointer": {
+ "x": 271,
+ "y": 253
+ }
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "canDrop": false,
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "b"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "didDrop": false,
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "b"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "b"
+ },
+ "canvas"
+ ],
+ [
+ "changed",
+ {
+ "index": 3,
+ "action": "drop"
+ },
+ "b"
+ ],
+ [
+ "text:changed",
+ {
+ "target": "b"
+ },
+ "canvas"
+ ],
+ [
+ "drop",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "didDrop": true,
+ "dropTarget": "b",
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "b"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-draggableTextDelegate-isActive-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-draggableTextDelegate-isActive-events.json
new file mode 100644
index 00000000000..0637a088a01
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-draggableTextDelegate-isActive-events.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-draggableTextDelegate-start-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-draggableTextDelegate-start-events.json
new file mode 100644
index 00000000000..94c5979446d
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-draggableTextDelegate-start-events.json
@@ -0,0 +1,74 @@
+[
+ [
+ "selection:changed",
+ {},
+ "a"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "a"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "a"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "a"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "a"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "selection:changed",
+ {},
+ "a"
+ ],
+ [
+ "text:selection:changed",
+ {
+ "target": "a"
+ },
+ "canvas"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-onDragStart-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-onDragStart-events.json
new file mode 100644
index 00000000000..0637a088a01
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drag-onDragStart-events.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drop-A-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drop-A-events.json
new file mode 100644
index 00000000000..268b0f549d1
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drop-A-events.json
@@ -0,0 +1,128 @@
+[
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 240,
+ "y": 140
+ },
+ "scenePoint": {
+ "x": 240,
+ "y": 140
+ },
+ "pointer": {
+ "x": 240,
+ "y": 140
+ },
+ "absolutePointer": {
+ "x": 240,
+ "y": 140
+ }
+ },
+ "a"
+ ],
+ [
+ "dragleave",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "viewportPoint": {
+ "x": 240,
+ "y": 140
+ },
+ "scenePoint": {
+ "x": 240,
+ "y": 140
+ },
+ "pointer": {
+ "x": 240,
+ "y": 140
+ },
+ "absolutePointer": {
+ "x": 240,
+ "y": 140
+ }
+ },
+ "a"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": false
+ },
+ "canvas"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": false
+ },
+ "a"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drop-B-events.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drop-B-events.json
new file mode 100644
index 00000000000..7e274f07829
--- /dev/null
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/disabling-drop-B-events.json
@@ -0,0 +1,128 @@
+[
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragstart",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a"
+ },
+ "a"
+ ],
+ [
+ "dragenter",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "dragSource": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragenter",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "b"
+ ],
+ [
+ "dragleave",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "subTargets": [],
+ "dragSource": "a"
+ },
+ "canvas"
+ ],
+ [
+ "dragleave",
+ {
+ "subTargets": [],
+ "dragSource": "a",
+ "e": {
+ "isTrusted": true
+ },
+ "target": "b",
+ "viewportPoint": {
+ "x": 435,
+ "y": 55
+ },
+ "scenePoint": {
+ "x": 435,
+ "y": 55
+ },
+ "pointer": {
+ "x": 435,
+ "y": 55
+ },
+ "absolutePointer": {
+ "x": 435,
+ "y": 55
+ }
+ },
+ "b"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": false
+ },
+ "canvas"
+ ],
+ [
+ "dragend",
+ {
+ "e": {
+ "isTrusted": true
+ },
+ "target": "a",
+ "subTargets": [],
+ "dragSource": "a",
+ "didDrop": false
+ },
+ "a"
+ ]
+]
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-em---tge.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-em---tge.json
index c6bae5b5a0a..f0147530031 100644
--- a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-em---tge.json
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-em---tge.json
@@ -1,4 +1,508 @@
{
- "x": -400,
- "y": -20
+ "position": {
+ "x": -400,
+ "y": -20
+ },
+ "data": {
+ "text/plain": "em ipsum\ndolor\nsit Amet2\nconsectge",
+ "application/fabric": {
+ "value": "em ipsum\ndolor\nsit Amet2\nconsectge",
+ "styles": [
+ {
+ "fontSize": 50,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "red",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 60,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "red",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "yellow"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": true,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "yellow",
+ "textDecoration": " line-through"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": true,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "yellow",
+ "textDecoration": " line-through"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "yellow"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "italic",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "green",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "italic",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "green",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "italic",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "green",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "bold",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "blue",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "bold",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "blue",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 63,
+ "fontWeight": "bold",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "blue",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": true,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": " underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": true,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": " underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": true,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": " overline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": true,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": " overline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": true,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": " overline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "#666",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "line-through"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "#666",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "line-through"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "#666",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "line-through"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "#666",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "line-through"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "#666",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": "line-through"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": true,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": "",
+ "textDecoration": " underline"
+ },
+ {
+ "fontSize": 40,
+ "fontWeight": "normal",
+ "fontFamily": "Arial",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": "#ff1e15",
+ "strokeWidth": 2,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ }
+ ]
+ }
+ }
}
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-fabric.json b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-fabric.json
index a6e1452e1b0..5ef7acfb8bb 100644
--- a/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-fabric.json
+++ b/e2e/tests/text/drag&drop/index.spec.ts-snapshots/drag-image-fabric.json
@@ -1,4 +1,98 @@
{
- "x": -44.5,
- "y": -20
+ "position": {
+ "x": -44.5,
+ "y": -20
+ },
+ "data": {
+ "text/plain": "fabric",
+ "application/fabric": {
+ "value": "fabric",
+ "styles": [
+ {
+ "fontSize": 64,
+ "fontWeight": "bold",
+ "fontFamily": "Times New Roman",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 64,
+ "fontWeight": "bold",
+ "fontFamily": "Times New Roman",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 64,
+ "fontWeight": "bold",
+ "fontFamily": "Times New Roman",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 64,
+ "fontWeight": "bold",
+ "fontFamily": "Times New Roman",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 64,
+ "fontWeight": "bold",
+ "fontFamily": "Times New Roman",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ },
+ {
+ "fontSize": 64,
+ "fontWeight": "bold",
+ "fontFamily": "Times New Roman",
+ "fontStyle": "normal",
+ "underline": false,
+ "overline": false,
+ "linethrough": false,
+ "stroke": null,
+ "strokeWidth": 1,
+ "fill": "rgb(0,0,0)",
+ "deltaY": 0,
+ "textBackgroundColor": ""
+ }
+ ]
+ }
+ }
}
\ No newline at end of file
diff --git a/e2e/tests/text/drag&drop/index.ts b/e2e/tests/text/drag&drop/index.ts
index 5bc16e0845b..2c9dc950f46 100644
--- a/e2e/tests/text/drag&drop/index.ts
+++ b/e2e/tests/text/drag&drop/index.ts
@@ -1,5 +1,5 @@
import * as fabric from 'fabric';
-import { beforeAll } from '../../test';
+import { before } from '../../test';
const styles = {
0: {
@@ -77,41 +77,100 @@ const styles = {
},
};
-beforeAll(
- (canvas) => {
- const textValue = 'fabric.js sandbox';
- const a = new fabric.Textbox(textValue, {
- originX: 'center',
- width: 210,
- left: 150,
- top: 20,
- splitByGrapheme: true,
- styles: fabric.util.stylesFromArray(
- [
- {
- style: {
- fontWeight: 'bold',
- fontSize: 64,
- },
- start: 0,
- end: 9,
+const roundPoint = (point: fabric.Point) => ({
+ x: Math.round(point.x),
+ y: Math.round(point.y),
+});
+
+const parseEvent = (
+ type: string,
+ { pointer, absolutePointer, ...ev } = {},
+ caller: fabric.Textbox | fabric.Canvas
+) =>
+ JSON.parse(
+ JSON.stringify([
+ type,
+ {
+ ...ev,
+ ...(pointer ? { pointer: roundPoint(pointer) } : {}),
+ ...(absolutePointer
+ ? { absolutePointer: roundPoint(absolutePointer) }
+ : {}),
+ },
+ caller,
+ ])
+ );
+
+class TestCanvas extends fabric.Canvas {
+ eventStream = [];
+ readEventStream() {
+ const data = this.eventStream;
+ this.eventStream = [];
+ return data;
+ }
+ toJSON() {
+ return 'canvas';
+ }
+}
+
+const commonEvents = [
+ 'dragstart',
+ // 'dragover',
+ // 'drag',
+ 'dragenter',
+ 'dragleave',
+ 'drop',
+ 'dragend',
+] as const;
+
+before('#canvas', (el) => {
+ const canvas = new TestCanvas(el, { width: 800, height: 500 });
+ const textValue = 'fabric.js sandbox';
+ const a = new fabric.Textbox(textValue, {
+ originX: 'center',
+ width: 210,
+ left: 150,
+ top: 20,
+ splitByGrapheme: true,
+ styles: fabric.util.stylesFromArray(
+ [
+ {
+ style: {
+ fontWeight: 'bold',
+ fontSize: 64,
},
- ],
- textValue
- ),
+ start: 0,
+ end: 9,
+ },
+ ],
+ textValue
+ ),
+ });
+ const b = new fabric.Textbox('lorem ipsum\ndolor\nsit Amet2\nconsectgetur', {
+ left: 400,
+ top: 20,
+ objectCaching: false,
+ fontFamily: 'Arial',
+ styles,
+ });
+
+ (
+ [...commonEvents, 'text:selection:changed', 'text:changed'] as const
+ ).forEach((type) => {
+ canvas.on(type, (ev) => {
+ canvas.eventStream.push(parseEvent(type, ev, canvas));
});
- const b = new fabric.Textbox(
- 'lorem ipsum\ndolor\nsit Amet2\nconsectgetur',
- {
- left: 400,
- top: 20,
- objectCaching: false,
- fontFamily: 'Arial',
- styles,
- }
- );
- canvas.add(a, b);
- return { a, b };
- },
- { width: 800, height: 500 }
-);
+ });
+ ([...commonEvents, 'selection:changed', 'changed'] as const).forEach(
+ (type) => {
+ a.on(type, (ev) => canvas.eventStream.push(parseEvent(type, ev, a)));
+ b.on(type, (ev) => canvas.eventStream.push(parseEvent(type, ev, b)));
+ }
+ );
+ Object.entries({ a, b }).forEach(
+ ([key, object]) => (object.toJSON = () => key)
+ );
+
+ canvas.add(a, b);
+ return { canvas, objects: { a, b } };
+});
diff --git a/e2e/utils/ObjectUtil.ts b/e2e/utils/ObjectUtil.ts
index 99a32e905fd..0203cb93a31 100644
--- a/e2e/utils/ObjectUtil.ts
+++ b/e2e/utils/ObjectUtil.ts
@@ -44,7 +44,7 @@ export class ObjectUtil {
);
}
- async expectObjectToMatch>(expected: T) {
+ async expectObjectToMatch(expected: Partial) {
const snapshot = await this.executeInBrowser((object) => object);
expect(snapshot).toMatchObject(expected);
}
diff --git a/e2e/utils/TextUtil.ts b/e2e/utils/TextUtil.ts
index 2f1f454b213..058e86a3bd4 100644
--- a/e2e/utils/TextUtil.ts
+++ b/e2e/utils/TextUtil.ts
@@ -19,4 +19,13 @@ export class TextUtil extends ObjectUtil {
{ index }
);
}
+
+ isCursorActive() {
+ return this.executeInBrowser((object) => {
+ return [
+ object['_currentTickState'],
+ object['_currentTickCompleteState'],
+ ].some((cursorAnimation) => cursorAnimation && !cursorAnimation.isDone());
+ });
+ }
}
diff --git a/src/canvas/__tests__/__snapshots__/eventData.test.ts.snap b/src/canvas/__tests__/__snapshots__/eventData.test.ts.snap
index cdf36d9eeea..f0e81fa324d 100644
--- a/src/canvas/__tests__/__snapshots__/eventData.test.ts.snap
+++ b/src/canvas/__tests__/__snapshots__/eventData.test.ts.snap
@@ -700,7 +700,7 @@ exports[`Canvas event data HTML event "wheel" should fire a corresponding canvas
]
`;
-exports[`should fire mouse over/out events on target 1`] = `
+exports[`Event targets should fire mouse over/out events on target 1`] = `
[
[
"mouseover",
@@ -784,7 +784,7 @@ exports[`should fire mouse over/out events on target 1`] = `
]
`;
-exports[`should fire mouse over/out events on target 2`] = `
+exports[`Event targets should fire mouse over/out events on target 2`] = `
[
[
"mouse:move:before",
diff --git a/src/canvas/__tests__/eventData.test.ts b/src/canvas/__tests__/eventData.test.ts
index 611549538d5..3d32723cdc6 100644
--- a/src/canvas/__tests__/eventData.test.ts
+++ b/src/canvas/__tests__/eventData.test.ts
@@ -1,15 +1,24 @@
/* eslint-disable no-restricted-globals */
import '../../../jest.extend';
-import type { TPointerEvent } from '../../EventTypeDefs';
import { Point } from '../../Point';
+import { ActiveSelection } from '../../shapes/ActiveSelection';
+import { Circle } from '../../shapes/Circle';
import { Group } from '../../shapes/Group';
import { IText } from '../../shapes/IText/IText';
import { FabricObject } from '../../shapes/Object/FabricObject';
+import { Rect } from '../../shapes/Rect';
+import { Triangle } from '../../shapes/Triangle';
import type { TMat2D } from '../../typedefs';
import { Canvas } from '../Canvas';
const genericVpt = [2.3, 0, 0, 2.3, 120, 80] as TMat2D;
+const registerTestObjects = (objects: Record) => {
+ Object.entries(objects).forEach(([key, object]) => {
+ jest.spyOn(object, 'toJSON').mockReturnValue(key);
+ });
+};
+
describe('Canvas event data', () => {
let canvas: Canvas;
let spy: jest.SpyInstance;
@@ -131,38 +140,755 @@ describe('Canvas event data', () => {
});
});
-it('A selected subtarget should not fire an event twice', () => {
- const target = new FabricObject();
- const group = new Group([target], {
- subTargetCheck: true,
- interactive: true,
+describe('Event targets', () => {
+ it('A selected subtarget should not fire an event twice', () => {
+ const target = new FabricObject();
+ const group = new Group([target], {
+ subTargetCheck: true,
+ interactive: true,
+ });
+ const canvas = new Canvas();
+ canvas.add(group);
+ const targetSpy = jest.fn();
+ target.on('mousedown', targetSpy);
+ jest.spyOn(canvas, '_checkTarget').mockReturnValue(true);
+ canvas.getSelectionElement().dispatchEvent(
+ new MouseEvent('mousedown', {
+ clientX: 50,
+ clientY: 50,
+ })
+ );
+ expect(targetSpy).toHaveBeenCalledTimes(1);
});
- const canvas = new Canvas();
- canvas.add(group);
- const targetSpy = jest.fn();
- target.on('mousedown', targetSpy);
- jest.spyOn(canvas, '_checkTarget').mockReturnValue(true);
- canvas.__onMouseDown({
- target: canvas.getSelectionElement(),
- clientX: 0,
- clientY: 0,
- } as unknown as TPointerEvent);
- expect(targetSpy).toHaveBeenCalledTimes(1);
-});
-it('should fire mouse over/out events on target', () => {
- const target = new FabricObject({ width: 10, height: 10 });
- const canvas = new Canvas();
- canvas.add(target);
-
- jest.spyOn(target, 'toJSON').mockReturnValue('target');
-
- const targetSpy = jest.spyOn(target, 'fire');
- const canvasSpy = jest.spyOn(canvas, 'fire');
- const enter = new MouseEvent('mousemove', { clientX: 5, clientY: 5 });
- const exit = new MouseEvent('mousemove', { clientX: 20, clientY: 20 });
- canvas._onMouseMove(enter);
- canvas._onMouseMove(exit);
- expect(targetSpy.mock.calls).toMatchSnapshot();
- expect(canvasSpy.mock.calls).toMatchSnapshot();
+ test('mouseover and mouseout with subTargetCheck', () => {
+ const rect1 = new FabricObject({
+ width: 5,
+ height: 5,
+ left: 5,
+ top: 0,
+ strokeWidth: 0,
+ });
+ const rect2 = new FabricObject({
+ width: 5,
+ height: 5,
+ left: 5,
+ top: 5,
+ strokeWidth: 0,
+ });
+ const rect3 = new FabricObject({
+ width: 5,
+ height: 5,
+ left: 0,
+ top: 5,
+ strokeWidth: 0,
+ });
+ const rect4 = new FabricObject({
+ width: 5,
+ height: 5,
+ left: 0,
+ top: 0,
+ strokeWidth: 0,
+ });
+ const rect5 = new FabricObject({
+ width: 5,
+ height: 5,
+ left: 2.5,
+ top: 2.5,
+ strokeWidth: 0,
+ });
+ const group1 = new Group([rect1, rect2], {
+ subTargetCheck: true,
+ });
+ const group2 = new Group([rect3, rect4], {
+ subTargetCheck: true,
+ });
+ // a group with 2 groups, with 2 rects each, one group left one group right
+ // each with 2 rects vertically aligned
+ const group = new Group([group1, group2], {
+ subTargetCheck: true,
+ });
+
+ const enter = jest.fn();
+ const exit = jest.fn();
+
+ const getTargetsFromEventStream = (mock: jest.Mock) =>
+ mock.mock.calls.map((args) => args[0].target);
+
+ registerTestObjects({
+ rect1,
+ rect2,
+ rect3,
+ rect4,
+ rect5,
+ group1,
+ group2,
+ group,
+ });
+
+ Object.values({
+ rect1,
+ rect2,
+ rect3,
+ rect4,
+ rect5,
+ group1,
+ group2,
+ group,
+ }).forEach((object) => {
+ object.on('mouseover', enter);
+ object.on('mouseout', exit);
+ });
+
+ const canvas = new Canvas();
+ canvas.add(group, rect5);
+
+ const fire = (x: number, y: number) => {
+ enter.mockClear();
+ exit.mockClear();
+ canvas
+ .getSelectionElement()
+ .dispatchEvent(new MouseEvent('mousemove', { clientX: x, clientY: y }));
+ };
+
+ fire(1, 1);
+ expect(getTargetsFromEventStream(enter)).toEqual([group, rect4, group2]);
+ expect(getTargetsFromEventStream(exit)).toEqual([]);
+
+ fire(5, 5);
+ expect(getTargetsFromEventStream(enter)).toEqual([rect5]);
+ expect(getTargetsFromEventStream(exit)).toEqual([group, rect4, group2]);
+
+ fire(9, 9);
+ expect(getTargetsFromEventStream(enter)).toEqual([group, rect2, group1]);
+ expect(getTargetsFromEventStream(exit)).toEqual([rect5]);
+
+ fire(9, 1);
+ expect(getTargetsFromEventStream(enter)).toEqual([rect1]);
+ expect(getTargetsFromEventStream(exit)).toEqual([rect2]);
+ });
+
+ describe('findTarget', () => {
+ const mockEvent = ({
+ canvas,
+ ...init
+ }: MouseEventInit & { canvas: Canvas }) => {
+ const e = new MouseEvent('mousedown', {
+ ...init,
+ });
+ jest
+ .spyOn(e, 'target', 'get')
+ .mockReturnValue(canvas.getSelectionElement());
+ return e;
+ };
+
+ const findTarget = (canvas: Canvas, ev?: MouseEventInit) => {
+ const target = canvas.findTarget(
+ mockEvent({ canvas, clientX: 0, clientY: 0, ...ev })
+ );
+ const targets = canvas.targets;
+ canvas.targets = [];
+ return { target, targets };
+ };
+
+ test.skip.each([true, false])(
+ 'findTargetsTraversal: search all is %s',
+ (searchAll) => {
+ const subTarget1 = new FabricObject();
+ const target1 = new Group([subTarget1], {
+ subTargetCheck: true,
+ interactive: true,
+ });
+ const subTarget2 = new FabricObject();
+ const target2 = new Group([subTarget2], {
+ subTargetCheck: true,
+ });
+ const parent = new Group([target1, target2], {
+ subTargetCheck: true,
+ interactive: true,
+ });
+ registerTestObjects({
+ subTarget1,
+ target1,
+ subTarget2,
+ target2,
+ parent,
+ });
+
+ const canvas = new Canvas();
+ canvas.add(parent);
+
+ jest.spyOn(canvas, '_checkTarget').mockReturnValue(true);
+ const found = canvas['findTargetsTraversal']([parent], new Point(), {
+ searchStrategy: searchAll ? 'search-all' : 'first-hit',
+ });
+ expect(found).toEqual(
+ searchAll
+ ? [subTarget2, target2, subTarget1, target1, parent]
+ : [subTarget2, target2, parent]
+ );
+ }
+ );
+
+ test.failing('searchPossibleTargets', () => {
+ const subTarget = new FabricObject();
+ const target = new Group([subTarget], {
+ subTargetCheck: true,
+ });
+ const parent = new Group([target], {
+ subTargetCheck: true,
+ interactive: true,
+ });
+ registerTestObjects({ subTarget, target, parent });
+
+ const canvas = new Canvas();
+ canvas.add(parent);
+
+ jest.spyOn(canvas, '_checkTarget').mockReturnValue(true);
+ const found = canvas.searchPossibleTargets([parent], new Point());
+ expect(found).toBe(target);
+ expect(canvas.targets).toEqual([subTarget, target, parent]);
+ });
+
+ test('searchPossibleTargets with selection', () => {
+ const subTarget = new FabricObject();
+ const target = new Group([subTarget], {
+ subTargetCheck: true,
+ });
+ const other = new FabricObject();
+ const activeSelection = new ActiveSelection();
+ registerTestObjects({ subTarget, target, other, activeSelection });
+
+ const canvas = new Canvas(undefined, { activeSelection });
+ canvas.add(other, target);
+ activeSelection.add(target, other);
+ canvas.setActiveObject(activeSelection);
+
+ jest.spyOn(canvas, '_checkTarget').mockReturnValue(true);
+ const found = canvas.searchPossibleTargets(
+ [activeSelection],
+ new Point()
+ );
+ expect(found).toBe(activeSelection);
+ expect(canvas.targets).toEqual([]);
+ });
+
+ test('findTarget clears prev targets', () => {
+ const canvas = new Canvas();
+ canvas.targets = [new FabricObject()];
+ expect(findTarget(canvas, { clientX: 0, clientY: 0 })).toEqual({
+ target: undefined,
+ targets: [],
+ });
+ });
+
+ test('findTarget preserveObjectStacking false', () => {
+ const rect = new FabricObject({
+ left: 0,
+ top: 0,
+ width: 10,
+ height: 10,
+ controls: {},
+ });
+ const rectOver = new FabricObject({
+ left: 0,
+ top: 0,
+ width: 10,
+ height: 10,
+ controls: {},
+ });
+ registerTestObjects({ rect, rectOver });
+
+ const canvas = new Canvas(undefined, { preserveObjectStacking: false });
+ canvas.add(rect, rectOver);
+ canvas.setActiveObject(rect);
+
+ expect(findTarget(canvas, { clientX: 5, clientY: 5 })).toEqual({
+ target: rect,
+ targets: [],
+ });
+ });
+
+ test('findTarget preserveObjectStacking true', () => {
+ const rect = new FabricObject({ left: 0, top: 0, width: 30, height: 30 });
+ const rectOver = new FabricObject({
+ left: 0,
+ top: 0,
+ width: 30,
+ height: 30,
+ });
+ registerTestObjects({ rect, rectOver });
+
+ const canvas = new Canvas(undefined, { preserveObjectStacking: true });
+ canvas.add(rect, rectOver);
+
+ const e = {
+ clientX: 15,
+ clientY: 15,
+ shiftKey: true,
+ };
+ const e2 = { clientX: 4, clientY: 4 };
+
+ expect(findTarget(canvas, e)).toEqual(
+ { target: rectOver, targets: [] }
+ // 'Should return the rectOver, rect is not considered'
+ );
+
+ canvas.setActiveObject(rect);
+ expect(findTarget(canvas, e)).toEqual(
+ { target: rectOver, targets: [] }
+ // 'Should still return rectOver because is above active object'
+ );
+
+ expect(findTarget(canvas, e2)).toEqual(
+ { target: rect, targets: [] }
+ // 'Should rect because a corner of the activeObject has been hit'
+ );
+
+ canvas.altSelectionKey = 'shiftKey';
+ expect(findTarget(canvas, e)).toEqual(
+ { target: rect, targets: [] }
+ // 'Should rect because active and altSelectionKey is pressed'
+ );
+ });
+
+ test('findTarget with subTargetCheck', () => {
+ const canvas = new Canvas();
+ const rect = new FabricObject({ left: 0, top: 0, width: 10, height: 10 });
+ const rect2 = new FabricObject({
+ left: 30,
+ top: 30,
+ width: 10,
+ height: 10,
+ });
+ const group = new Group([rect, rect2]);
+ registerTestObjects({ rect, rect2, group });
+ canvas.add(group);
+
+ expect(findTarget(canvas, { clientX: 5, clientY: 5 })).toEqual({
+ target: group,
+ targets: [],
+ });
+
+ expect(findTarget(canvas, { clientX: 35, clientY: 35 })).toEqual({
+ target: group,
+ targets: [],
+ });
+
+ group.subTargetCheck = true;
+ group.setCoords();
+
+ expect(findTarget(canvas, { clientX: 5, clientY: 5 })).toEqual({
+ target: group,
+ targets: [rect],
+ });
+
+ expect(findTarget(canvas, { clientX: 15, clientY: 15 })).toEqual({
+ target: group,
+ targets: [],
+ });
+
+ expect(findTarget(canvas, { clientX: 35, clientY: 35 })).toEqual({
+ target: group,
+ targets: [rect2],
+ });
+ });
+
+ test('findTarget with subTargetCheck and canvas zoom', () => {
+ const nested1 = new FabricObject({
+ width: 100,
+ height: 100,
+ fill: 'yellow',
+ });
+ const nested2 = new FabricObject({
+ width: 100,
+ height: 100,
+ left: 100,
+ top: 100,
+ fill: 'purple',
+ });
+ const nestedGroup = new Group([nested1, nested2], {
+ scaleX: 0.5,
+ scaleY: 0.5,
+ top: 100,
+ left: 0,
+ subTargetCheck: true,
+ });
+ const rect1 = new FabricObject({
+ width: 100,
+ height: 100,
+ fill: 'red',
+ });
+ const rect2 = new FabricObject({
+ width: 100,
+ height: 100,
+ left: 100,
+ top: 100,
+ fill: 'blue',
+ });
+ const group = new Group([rect1, rect2, nestedGroup], {
+ top: -150,
+ left: -50,
+ subTargetCheck: true,
+ });
+ registerTestObjects({
+ rect1,
+ rect2,
+ nested1,
+ nested2,
+ nestedGroup,
+ group,
+ });
+
+ const canvas = new Canvas(undefined, {
+ viewportTransform: [0.1, 0, 0, 0.1, 100, 200],
+ });
+ canvas.add(group);
+
+ expect(findTarget(canvas, { clientX: 96, clientY: 186 })).toEqual({
+ target: group,
+ targets: [rect1],
+ });
+
+ expect(findTarget(canvas, { clientX: 98, clientY: 188 })).toEqual({
+ target: group,
+ targets: [rect1],
+ });
+
+ expect(findTarget(canvas, { clientX: 100, clientY: 190 })).toEqual({
+ target: group,
+ targets: [rect1],
+ });
+
+ expect(findTarget(canvas, { clientX: 102, clientY: 192 })).toEqual({
+ target: group,
+ targets: [rect1],
+ });
+
+ expect(findTarget(canvas, { clientX: 104, clientY: 194 })).toEqual({
+ target: group,
+ targets: [rect1],
+ });
+
+ expect(findTarget(canvas, { clientX: 106, clientY: 196 })).toEqual({
+ target: group,
+ targets: [rect2],
+ });
+ });
+
+ test.each([true, false])(
+ 'findTarget on activeObject with subTargetCheck and preserveObjectStacking %s',
+ (preserveObjectStacking) => {
+ const rect = new FabricObject({
+ left: 0,
+ top: 0,
+ width: 10,
+ height: 10,
+ });
+ const rect2 = new FabricObject({
+ left: 30,
+ top: 30,
+ width: 10,
+ height: 10,
+ });
+ const group = new Group([rect, rect2], { subTargetCheck: true });
+ registerTestObjects({ rect, rect2, group });
+
+ const canvas = new Canvas(undefined, { preserveObjectStacking });
+ canvas.add(group);
+ canvas.setActiveObject(group);
+
+ expect(findTarget(canvas, { clientX: 9, clientY: 9 })).toEqual({
+ target: group,
+ targets: [rect],
+ });
+ }
+ );
+
+ test('findTarget with perPixelTargetFind', () => {
+ const triangle = new Triangle({ width: 30, height: 30 });
+ registerTestObjects({ triangle });
+
+ const canvas = new Canvas();
+ canvas.add(triangle);
+
+ expect(findTarget(canvas, { clientX: 5, clientY: 5 })).toEqual({
+ target: triangle,
+ targets: [],
+ });
+
+ canvas.perPixelTargetFind = true;
+
+ expect(findTarget(canvas, { clientX: 5, clientY: 5 })).toEqual({
+ target: undefined,
+ targets: [],
+ });
+ expect(findTarget(canvas, { clientX: 15, clientY: 15 })).toEqual({
+ target: triangle,
+ targets: [],
+ });
+ });
+
+ describe('findTarget with perPixelTargetFind in nested group', () => {
+ const prepareTest = () => {
+ const deepTriangle = new Triangle({
+ left: 0,
+ top: 0,
+ width: 30,
+ height: 30,
+ fill: 'yellow',
+ });
+ const triangle2 = new Triangle({
+ left: 100,
+ top: 120,
+ width: 30,
+ height: 30,
+ angle: 100,
+ fill: 'pink',
+ });
+ const deepCircle = new Circle({
+ radius: 30,
+ top: 0,
+ left: 30,
+ fill: 'blue',
+ });
+ const circle2 = new Circle({
+ scaleX: 2,
+ scaleY: 2,
+ radius: 10,
+ top: 120,
+ left: -20,
+ fill: 'purple',
+ });
+ const deepRect = new Rect({
+ width: 50,
+ height: 30,
+ top: 10,
+ left: 110,
+ fill: 'red',
+ skewX: 40,
+ skewY: 20,
+ });
+ const rect2 = new Rect({
+ width: 100,
+ height: 80,
+ top: 50,
+ left: 60,
+ fill: 'green',
+ });
+ const deepGroup = new Group([deepTriangle, deepCircle, deepRect], {
+ subTargetCheck: true,
+ });
+ const group2 = new Group([deepGroup, circle2, rect2, triangle2], {
+ subTargetCheck: true,
+ });
+ const group3 = new Group([group2], { subTargetCheck: true });
+
+ registerTestObjects({
+ deepTriangle,
+ triangle2,
+ deepCircle,
+ circle2,
+ rect2,
+ deepRect,
+ deepGroup,
+ group2,
+ group3,
+ });
+
+ const canvas = new Canvas(undefined, { perPixelTargetFind: true });
+ canvas.add(group3);
+
+ return {
+ canvas,
+ deepTriangle,
+ triangle2,
+ deepCircle,
+ circle2,
+ rect2,
+ deepRect,
+ deepGroup,
+ group2,
+ group3,
+ };
+ };
+
+ test.each([
+ { x: 5, y: 5 },
+ { x: 21, y: 9 },
+ { x: 37, y: 7 },
+ { x: 89, y: 47 },
+ { x: 16, y: 122 },
+ { x: 127, y: 37 },
+ { x: 87, y: 139 },
+ ])('transparent hit on %s', ({ x: clientX, y: clientY }) => {
+ const { canvas } = prepareTest();
+ expect(findTarget(canvas, { clientX, clientY })).toEqual({
+ target: undefined,
+ targets: [],
+ });
+ });
+
+ test('findTarget with perPixelTargetFind in nested group', () => {
+ const {
+ canvas,
+ deepTriangle,
+ triangle2,
+ deepCircle,
+ circle2,
+ rect2,
+ deepRect,
+ deepGroup,
+ group2,
+ group3,
+ } = prepareTest();
+
+ expect(findTarget(canvas, { clientX: 15, clientY: 15 })).toEqual({
+ target: group3,
+ targets: [deepTriangle, deepGroup, group2],
+ });
+
+ expect(findTarget(canvas, { clientX: 50, clientY: 20 })).toEqual({
+ target: group3,
+ targets: [deepCircle, deepGroup, group2],
+ });
+
+ expect(findTarget(canvas, { clientX: 117, clientY: 16 })).toEqual({
+ target: group3,
+ targets: [deepRect, deepGroup, group2],
+ });
+
+ expect(findTarget(canvas, { clientX: 100, clientY: 90 })).toEqual({
+ target: group3,
+ targets: [rect2, group2],
+ });
+
+ expect(findTarget(canvas, { clientX: 9, clientY: 145 })).toEqual({
+ target: group3,
+ targets: [circle2, group2],
+ });
+
+ expect(findTarget(canvas, { clientX: 66, clientY: 143 })).toEqual({
+ target: group3,
+ targets: [triangle2, group2],
+ });
+ });
+ });
+
+ test('findTarget on active selection', () => {
+ const rect1 = new FabricObject({
+ left: 0,
+ top: 0,
+ width: 10,
+ height: 10,
+ });
+ const rect2 = new FabricObject({
+ left: 20,
+ top: 20,
+ width: 10,
+ height: 10,
+ });
+ const rect3 = new FabricObject({
+ left: 20,
+ top: 0,
+ width: 10,
+ height: 10,
+ });
+ const activeSelection = new ActiveSelection([rect1, rect2], {
+ subTargetCheck: true,
+ cornerSize: 2,
+ });
+ registerTestObjects({ rect1, rect2, rect3, activeSelection });
+
+ const canvas = new Canvas(undefined, { activeSelection });
+ canvas.add(rect1, rect2, rect3);
+ canvas.setActiveObject(activeSelection);
+
+ expect(findTarget(canvas, { clientX: 5, clientY: 5 })).toEqual({
+ target: activeSelection,
+ targets: [rect1],
+ });
+
+ expect(findTarget(canvas, { clientX: 40, clientY: 15 })).toEqual({
+ target: undefined,
+ targets: [],
+ });
+ expect(activeSelection.__corner).toBeUndefined();
+
+ expect(findTarget(canvas, { clientX: 0, clientY: 0 })).toEqual({
+ target: activeSelection,
+ targets: [],
+ });
+ expect(activeSelection.__corner).toBe('tl');
+
+ expect(findTarget(canvas, { clientX: 25, clientY: 5 })).toEqual(
+ {
+ target: activeSelection,
+ targets: [],
+ }
+ // 'Should not return the rect behind active selection'
+ );
+
+ canvas.discardActiveObject();
+ expect(findTarget(canvas, { clientX: 25, clientY: 5 })).toEqual(
+ {
+ target: rect3,
+ targets: [],
+ }
+ // 'Should return the rect after clearing selection'
+ );
+ });
+
+ test('findTarget on active selection with perPixelTargetFind', () => {
+ const rect1 = new Rect({
+ left: 0,
+ top: 0,
+ width: 10,
+ height: 10,
+ });
+ const rect2 = new Rect({
+ left: 20,
+ top: 20,
+ width: 10,
+ height: 10,
+ });
+ const activeSelection = new ActiveSelection([rect1, rect2]);
+ registerTestObjects({ rect1, rect2, activeSelection });
+
+ const canvas = new Canvas(undefined, {
+ activeSelection,
+ perPixelTargetFind: true,
+ preserveObjectStacking: true,
+ });
+ canvas.add(rect1, rect2);
+ canvas.setActiveObject(activeSelection);
+
+ expect(findTarget(canvas, { clientX: 8, clientY: 8 })).toEqual({
+ target: activeSelection,
+ targets: [],
+ });
+
+ expect(findTarget(canvas, { clientX: 15, clientY: 15 })).toEqual({
+ target: undefined,
+ targets: [],
+ });
+ });
+ });
+
+ it('should fire mouse over/out events on target', () => {
+ const target = new FabricObject({ width: 10, height: 10 });
+ const canvas = new Canvas();
+ canvas.add(target);
+
+ jest.spyOn(target, 'toJSON').mockReturnValue('target');
+
+ const targetSpy = jest.spyOn(target, 'fire');
+ const canvasSpy = jest.spyOn(canvas, 'fire');
+ const enter = new MouseEvent('mousemove', { clientX: 5, clientY: 5 });
+ const exit = new MouseEvent('mousemove', { clientX: 20, clientY: 20 });
+ canvas._onMouseMove(enter);
+ canvas._onMouseMove(exit);
+ expect(targetSpy.mock.calls).toMatchSnapshot();
+ expect(canvasSpy.mock.calls).toMatchSnapshot();
+ });
});
diff --git a/test/unit/canvas.js b/test/unit/canvas.js
index a5737d76b52..3ac67d4e7b3 100644
--- a/test/unit/canvas.js
+++ b/test/unit/canvas.js
@@ -771,392 +771,6 @@
canvas.remove(rect);
});
- QUnit.test('findTarget preserveObjectStacking false', function(assert) {
- canvas.preserveObjectStacking = false;
- var rect = makeRect({ left: 0, top: 0 }),
- rectOver = makeRect({ left: 0, top: 0 }),
- target,
- e = { clientX: 5, clientY: 5, target: canvas.upperCanvasEl };
- canvas.add(rect);
- canvas.add(rectOver);
- canvas.setActiveObject(rect);
- canvas.renderAll();
- target = canvas.findTarget(e);
- assert.equal(target, rect, 'Should return the rect');
- });
-
- QUnit.test('findTarget preserveObjectStacking true', function(assert) {
- assert.ok(typeof canvas.findTarget === 'function');
- canvas.preserveObjectStacking = true;
- var rect = makeRect({ left: 0, top: 0, width: 30, height: 30 }),
- rectOver = makeRect({ left: 0, top: 0, width: 30, height: 30 }),
- target,
- e1 = { clientX: 15, clientY: 15, shiftKey: true, target: canvas.upperCanvasEl },
- e2 = { clientX: 4, clientY: 4, target: canvas.upperCanvasEl };
- canvas.add(rect);
- canvas.add(rectOver);
- target = canvas.findTarget(e1);
- assert.equal(target, rectOver, 'Should return the rectOver, rect is not considered');
- canvas.setActiveObject(rect);
- target = canvas.findTarget(e1);
- assert.equal(target, rectOver, 'Should still return rectOver because is above active object');
- target = canvas.findTarget(e2);
- assert.equal(target, rect, 'Should rect because a corner of the activeObject has been hit');
- canvas.altSelectionKey = 'shiftKey';
- target = canvas.findTarget(e1);
- assert.equal(target, rect, 'Should rect because active and altSelectionKey is pressed');
- canvas.preserveObjectStacking = false;
- });
-
- QUnit.test('findTarget with subTargetCheck', function(assert) {
- var rect = makeRect({ left: 0, top: 0 }),
- rect2 = makeRect({ left: 30, top: 30}), target,
- group = new fabric.Group([rect, rect2]);
-
- canvas.add(group);
-
- target = canvas.findTarget({
- clientX: 5, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the group');
- assert.equal(canvas.targets[0], undefined, 'no subtarget should return');
-
- target = canvas.findTarget({
- clientX: 30, clientY: 30, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the group');
- group.subTargetCheck = true;
- group.setCoords();
- target = canvas.findTarget({
- clientX: 5, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the group');
- assert.equal(canvas.targets[0], rect, 'should return the rect');
-
- target = canvas.findTarget({
- clientX: 15, clientY: 15, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the group');
- assert.equal(canvas.targets[0], undefined, 'no subtarget should return');
-
- target = canvas.findTarget({
- clientX: 32, clientY: 32, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the group');
- assert.equal(canvas.targets[0], rect2, 'should return the rect2');
- canvas.remove(group);
- });
-
- QUnit.test('findTarget with subTargetCheck and canvas zoom', function(assert) {
- var rect3 = new fabric.Rect({
- width: 100,
- height: 100,
- fill: 'yellow'
- });
- var rect4 = new fabric.Rect({
- width: 100,
- height: 100,
- left: 100,
- top: 100,
- fill: 'purple'
- });
- var group3 = new fabric.Group(
- [rect3, rect4],
- { scaleX: 0.5, scaleY: 0.5, top: 100, left: 0 });
- group3.subTargetCheck = true;
- group3.setCoords();
- var rect1 = new fabric.Rect({
- width: 100,
- height: 100,
- fill: 'red'
- });
- var rect2 = new fabric.Rect({
- width: 100,
- height: 100,
- left: 100,
- top: 100,
- fill: 'blue'
- });
- var g = new fabric.Group([rect1, rect2, group3], { top: -150, left: -50 });
- g.subTargetCheck = true;
- canvas.viewportTransform = [0.1, 0, 0, 0.1, 100, 200];
- canvas.add(g);
-
- var target = canvas.findTarget({
- clientX: 96, clientY: 186, target: canvas.upperCanvasEl
- });
- assert.equal(target, g, 'Should return the group 96');
- assert.equal(canvas.targets[0], rect1, 'should find the target rect 96');
- canvas.targets = [];
-
- target = canvas.findTarget({
- clientX: 98, clientY: 188, target: canvas.upperCanvasEl
- });
- assert.equal(target, g, 'Should return the group 98');
- assert.equal(canvas.targets[0], rect1, 'should find the target rect1 98');
- canvas.targets = [];
-
- target = canvas.findTarget({
- clientX: 100, clientY: 190, target: canvas.upperCanvasEl
- });
- assert.equal(target, g, 'Should return the group 100');
- assert.equal(canvas.targets[0], rect1, 'should find the target rect1 100');
- canvas.targets = [];
-
- target = canvas.findTarget({
- clientX: 102, clientY: 192, target: canvas.upperCanvasEl
- });
- assert.equal(target, g, 'Should return the group 102');
- assert.equal(canvas.targets[0], rect1, 'should find the target rect 102');
- canvas.targets = [];
-
- target = canvas.findTarget({
- clientX: 104, clientY: 194, target: canvas.upperCanvasEl
- });
- assert.equal(target, g, 'Should return the group 104');
- assert.equal(canvas.targets[0], rect1, 'should find the target rect 104');
- canvas.targets = [];
-
- target = canvas.findTarget({
- clientX: 106, clientY: 196, target: canvas.upperCanvasEl
- });
- assert.equal(target, g, 'Should return the group 106');
- assert.equal(canvas.targets[0], rect2, 'should find the target rect2 106');
- canvas.targets = [];
- });
-
- QUnit.test('findTarget with subTargetCheck on activeObject', function(assert) {
- var rect = makeRect({ left: 0, top: 0 }),
- rect2 = makeRect({ left: 30, top: 30}), target,
- group = new fabric.Group([rect, rect2]);
-
-
- group.subTargetCheck = true;
- canvas.add(group);
- canvas.setActiveObject(group);
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the group');
- assert.equal(canvas.targets[0], rect, 'should return the rect');
-
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
-
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
-
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
-
- assert.equal(canvas.targets.length, 1, 'multiple calls to subtarget should not add more to targets');
-
- canvas.remove(group);
- });
-
- QUnit.test('findTarget with subTargetCheck on activeObject and preserveObjectStacking true', function(assert) {
- var rect = makeRect({ left: 0, top: 0 }),
- rect2 = makeRect({ left: 30, top: 30}), target,
- group = new fabric.Group([rect, rect2]);
- canvas.preserveObjectStacking = true;
- group.subTargetCheck = true;
- canvas.add(group);
- canvas.setActiveObject(group);
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the group');
- assert.equal(canvas.targets[0], rect, 'should return the rect');
-
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
-
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
-
- target = canvas.findTarget({
- clientX: 9, clientY: 9, target: canvas.upperCanvasEl
- });
-
- assert.equal(canvas.targets.length, 1, 'multiple calls to subtarget should not add more to targets');
-
- canvas.remove(group);
- });
-
- QUnit.test('findTarget with perPixelTargetFind', function(assert) {
- assert.ok(typeof canvas.findTarget === 'function');
- var triangle = makeTriangle({ left: 0, top: 0 }), target;
- canvas.add(triangle);
- target = canvas.findTarget({
- clientX: 5, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, triangle, 'Should return the triangle by bounding box');
- //TODO find out why this stops the tests
- canvas.perPixelTargetFind = true;
- target = canvas.findTarget({
- clientX: 5, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks');
- target = canvas.findTarget({
- clientX: 15, clientY: 15, target: canvas.upperCanvasEl
- });
- assert.equal(target, triangle, 'Should return the triangle now');
- canvas.perPixelTargetFind = false;
- canvas.remove(triangle);
- });
-
- QUnit.test('findTarget with perPixelTargetFind in nested group', function(assert) {
- assert.ok(typeof canvas.findTarget === 'function');
- var triangle = makeTriangle({ left: 0, top: 0, width: 30, height: 30, fill: 'yellow' }),
- triangle2 = makeTriangle({ left: 100, top: 120, width: 30, height: 30, angle: 100, fill: 'pink' }),
- circle = new fabric.Circle({ radius: 30, top: 0, left: 30, fill: 'blue' }),
- circle2 = new fabric.Circle({ scaleX: 2, scaleY: 2, radius: 10, top: 120, left: -20, fill: 'purple' }),
- rect = new fabric.Rect({ width: 100, height: 80, top: 50, left: 60, fill: 'green' }),
- rect2 = new fabric.Rect({ width: 50, height: 30, top: 10, left: 110, fill: 'red', skewX: 40, skewY: 20 }),
- group1 = new fabric.Group([triangle, circle, rect2], { subTargetCheck: true }),
- group2 = new fabric.Group([group1, circle2, rect, triangle2], { subTargetCheck: true }),
- group3 = new fabric.Group([group2], { subTargetCheck: true }),
- target;
-
- canvas.add(group3);
- canvas.perPixelTargetFind = true;
- target = canvas.findTarget({
- clientX: 5, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks case 1');
- target = canvas.findTarget({
- clientX: 21, clientY: 9, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks case 2');
- target = canvas.findTarget({
- clientX: 37, clientY: 7, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks case 3');
- target = canvas.findTarget({
- clientX: 89, clientY: 47, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks case 4');
- target = canvas.findTarget({
- clientX: 16, clientY: 122, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks case 5');
- target = canvas.findTarget({
- clientX: 127, clientY: 37, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks case 6');
- target = canvas.findTarget({
- clientX: 87, clientY: 139, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should return null because of transparency checks case 7');
- target = canvas.findTarget({
- clientX: 15, clientY: 15, target: canvas.upperCanvasEl
- });
- assert.equal(target, group3, 'Should return the group3 now');
- assert.equal(canvas.targets.length, 3, 'Subtargets length should be 3');
- assert.equal(canvas.targets[0], triangle, 'The deepest target should be triangle');
- target = canvas.findTarget({
- clientX: 50, clientY: 20, target: canvas.upperCanvasEl
- });
- assert.equal(target, group3, 'Should return the group3 now');
- assert.equal(canvas.targets.length, 3, 'Subtargets length should be 3');
- assert.equal(canvas.targets[0], circle, 'The deepest target should be circle');
- target = canvas.findTarget({
- clientX: 117, clientY: 16, target: canvas.upperCanvasEl
- });
- assert.equal(target, group3, 'Should return the group3 now');
- assert.equal(canvas.targets.length, 3, 'Subtargets length should be 2');
- assert.equal(canvas.targets[0], rect2, 'The deepest target should be rect2');
- target = canvas.findTarget({
- clientX: 100, clientY: 90, target: canvas.upperCanvasEl
- });
- assert.equal(target, group3, 'Should return the group3 now');
- assert.equal(canvas.targets.length, 2, 'Subtargets length should be 2');
- assert.equal(canvas.targets[0], rect, 'The deepest target should be rect');
- target = canvas.findTarget({
- clientX: 9, clientY: 145, target: canvas.upperCanvasEl
- });
- assert.equal(target, group3, 'Should return the group3 now');
- assert.equal(canvas.targets.length, 2, 'Subtargets length should be 2');
- assert.equal(canvas.targets[0], circle2, 'The deepest target should be circle2');
- target = canvas.findTarget({
- clientX: 66, clientY: 143, target: canvas.upperCanvasEl
- });
- assert.equal(target, group3, 'Should return the group3 now');
- assert.equal(canvas.targets.length, 2, 'Subtargets length should be 2');
- assert.equal(canvas.targets[0], triangle2, 'The deepest target should be triangle2');
- canvas.remove(group3);
- });
-
- QUnit.test('findTarget on active selection', function(assert) {
- var rect1 = makeRect({ left: 0, top: 0 }), target;
- var rect2 = makeRect({ left: 20, top: 20 });
- var rect3 = makeRect({ left: 20, top: 0 });
- canvas.add(rect1);
- canvas.add(rect2);
- canvas.add(rect3);
- const group = new fabric.ActiveSelection();
- group.subTargetCheck = true;
- group.add(rect1, rect2);
- group.cornerSize = 2;
- group.setCoords();
- canvas.setActiveObject(group);
- target = canvas.findTarget({
- clientX: 5, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the activegroup');
- target = canvas.findTarget({
- clientX: 40, clientY: 15, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should miss the activegroup');
- assert.ok(!group.__corner, 'not over control');
- target = canvas.findTarget({
- clientX: 0, clientY: 0, target: canvas.upperCanvasEl
- });
- assert.equal(group.__corner, 'tl', 'over control');
- assert.ok(target, group, 'should return active selection if over control');
- target = canvas.findTarget({
- clientX: 5, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.ok(target, group, 'should return active selection');
- assert.equal(canvas.targets[0], rect1, 'Should return the rect inside active selection');
- target = canvas.findTarget({
- clientX: 25, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the active selection');
- assert.deepEqual(canvas.targets, [], 'Should not return the rect behind active selection');
- canvas.discardActiveObject();
- target = canvas.findTarget({
- clientX: 25, clientY: 5, target: canvas.upperCanvasEl
- });
- assert.equal(target, rect3, 'Should return the rect3 now that active selection has been cleared');
- });
-
- QUnit.test('findTarget on active selection with perPixelTargetFind', function(assert) {
- var rect1 = makeRect({ left: 0, top: 0 }), target;
- var rect2 = makeRect({ left: 20, top: 20 });
- canvas.perPixelTargetFind = true;
- canvas.preserveObjectStacking = true;
- canvas.add(rect1);
- canvas.add(rect2);
- const group = new fabric.ActiveSelection();
- group.add(rect1, rect2);
- canvas.setActiveObject(group);
- target = canvas.findTarget({
- clientX: 8, clientY: 8, target: canvas.upperCanvasEl
- });
- assert.equal(target, group, 'Should return the activegroup');
-
- target = canvas.findTarget({
- clientX: 15, clientY: 15, target: canvas.upperCanvasEl
- });
- assert.equal(target, null, 'Should miss the activegroup');
- });
-
QUnit.test('toDataURL', function(assert) {
assert.ok(typeof canvas.toDataURL === 'function');
var dataURL = canvas.toDataURL();
diff --git a/test/unit/canvas_events.js b/test/unit/canvas_events.js
index 48c14d79b34..c7b6d260868 100644
--- a/test/unit/canvas_events.js
+++ b/test/unit/canvas_events.js
@@ -630,70 +630,6 @@
assert.deepEqual(targetControl, [], 'no target should be referenced');
});
- QUnit.test('mouseover and mouseout with subtarget check', function(assert) {
- var rect1 = new fabric.Rect({ width: 5, height: 5, left: 5, top: 0, strokeWidth: 0, name: 'rect1' });
- var rect2 = new fabric.Rect({ width: 5, height: 5, left: 5, top: 5, strokeWidth: 0, name: 'rect2' });
- var rect3 = new fabric.Rect({ width: 5, height: 5, left: 0, top: 5, strokeWidth: 0, name: 'rect3' });
- var rect4 = new fabric.Rect({ width: 5, height: 5, left: 0, top: 0, strokeWidth: 0, name: 'rect4' });
- var rect5 = new fabric.Rect({ width: 5, height: 5, left: 2.5, top: 2.5, strokeWidth: 0, name: 'rect5' });
- var group1 = new fabric.Group([rect1, rect2], { subTargetCheck: true, name: 'group1' });
- var group2 = new fabric.Group([rect3, rect4], { subTargetCheck: true, name: 'group2' });
- // a group with 2 groups, with 2 rects each, one group left one group right
- // each with 2 rects vertically aligned
- var group = new fabric.Group([group1, group2], { subTargetCheck: true, name: 'group' });
- var c = new fabric.Canvas();
- var targetArray = [];
- var targetOutArray = [];
- [rect1, rect2, rect3, rect4, rect5, group1, group2, group].forEach(function(t) {
- t.on('mouseover', function(opt) {
- targetArray.push(opt.target);
- });
- t.on('mouseout', function(opt) {
- targetOutArray.push(opt.target);
- });
- });
- c.add(group, rect5);
- simulateEvent(c.upperCanvasEl, 'mousemove', {
- pointerX: 1, pointerY: 1
- });
- assert.equal(targetArray[0], group, 'first hit is group');
- assert.equal(targetArray[2], group2, 'then hit group2');
- assert.equal(targetArray[1], rect4, 'then hit rect4');
- assert.equal(targetOutArray.length, 0, 'no target out');
-
- targetArray = [];
- targetOutArray = [];
- simulateEvent(c.upperCanvasEl, 'mousemove', {
- pointerX: 5, pointerY: 5
- });
- assert.equal(targetArray[0], rect5, 'first hit is target5');
- assert.equal(targetArray.length, 1, 'only one target');
- assert.equal(targetOutArray[0], group, 'first targetOutArray is group');
- assert.equal(targetOutArray[2], group2, 'then targetOutArray group2');
- assert.equal(targetOutArray[1], rect4, 'then targetOutArray rect4');
-
- targetArray = [];
- targetOutArray = [];
- simulateEvent(c.upperCanvasEl, 'mousemove', {
- pointerX: 9, pointerY: 9
- });
- assert.equal(targetArray[0], group, 'first hit is group');
- assert.equal(targetArray[2], group1, 'then hit group1');
- assert.equal(targetArray[1], rect2, 'then hit rect2');
- assert.equal(targetOutArray.length, 1, 'only one target out when moving away from rect 5');
- assert.equal(targetOutArray[0], rect5, 'rect5 fires out');
-
- targetArray = [];
- targetOutArray = [];
- simulateEvent(c.upperCanvasEl, 'mousemove', {
- pointerX: 9, pointerY: 1
- });
- assert.equal(targetArray[0], rect1, 'the only target changing is rect1');
- assert.equal(targetArray.length, 1, 'only one target entering ');
- assert.equal(targetOutArray.length, 1, 'one target out');
- assert.equal(targetOutArray[0], rect2, 'the only target out is rect2');
- });
-
QUnit.test('Fabric mouseover, mouseout events fire for subTargets when subTargetCheck is enabled', function(assert){
var done = assert.async();
var counterOver = 0, counterOut = 0, canvas = new fabric.Canvas();
diff --git a/test/unit/draggable_text.js b/test/unit/draggable_text.js
deleted file mode 100644
index 8f9cf427f7a..00000000000
--- a/test/unit/draggable_text.js
+++ /dev/null
@@ -1,613 +0,0 @@
-function assertDragEventStream(name, a, b) {
- QUnit.assert.equal(a.length, b.length, `${name} event stream should be same in size`);
- a.forEach(({ target, dragSource, dropTarget, ..._a }, index) => {
- const { target: targetB, dragSource: dragSourceB, dropTarget: dropTargetB, ..._b } = b[index];
- QUnit.assert.equal(target, targetB, `target should match ${index}`);
- QUnit.assert.equal(dragSource, dragSourceB, `dragSource should match ${index}`);
- QUnit.assert.equal(dropTarget, dropTargetB, `dropTarget should match ${index}`);
- QUnit.assert.deepEqual(_a, _b, `event ${index} should match`);
- });
-}
-
-(isNode() ? QUnit.module.skip : QUnit.module)('draggable text', function (hooks) {
- let canvas;
- hooks.before(function () {
- canvas = new fabric.Canvas(null, {
- enableRetinaScaling: false
- });
- });
- hooks.after(() => canvas.dispose());
- hooks.afterEach(function () {
- canvas.clear();
- canvas.cancelRequestedRender();
- });
-
- function assertCursorAnimation(assert, text, active = false) {
- const cursorState = [text._currentTickState, text._currentTickCompleteState].some(
- (cursorAnimation) => cursorAnimation && !cursorAnimation.isDone()
- );
- assert.equal(cursorState, active, `cursor animation state should be ${active}`);
- }
-
- function wait(ms = 32) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
- [true, false].forEach(enableRetinaScaling => {
- QUnit.module(`enableRetinaScaling = ${enableRetinaScaling}`, function (hooks) {
- let canvas, eventData, iText, iText2, eventStream, renderEffects;
- let count = 0, countCanvas = 0;
- hooks.before(() => {
- fabric.config.configure({ devicePixelRatio: 2 });
- });
- hooks.after(() => {
- fabric.config.restoreDefaults();
- });
- hooks.beforeEach(() => {
- canvas = new fabric.Canvas(null, {
- enableRetinaScaling,
- });
- eventData = {
- which: 1,
- target: canvas.upperCanvasEl,
- preventDefault() {
- this.defaultPrevented = true;
- },
- stopPropagation() {
- this.propagationStopped = true;
- },
- dataTransfer: {
- data: {},
- get types() {
- return Object.keys(this.data);
- },
- dropEffect: 'none',
- getData(type) {
- return this.data[type];
- },
- setData(type, value) {
- this.data[type] = value;
- },
- setDragImage(img, x, y) {
- this.dragImageData = { img, x, y };
- },
- },
- ...(enableRetinaScaling ? {
- clientX: 60,
- clientY: 30
- } : {
- clientX: 30,
- clientY: 15
- })
- };
- iText = new fabric.IText('test test');
- iText2 = new fabric.IText('test2 test2', { left: 200 });
- canvas.add(iText, iText2);
- canvas.setActiveObject(iText);
- iText.enterEditing();
- iText.selectionStart = 0;
- iText.selectionEnd = 4;
- count = 0;
- countCanvas = 0;
- canvas.on('text:selection:changed', () => {
- countCanvas++;
- });
- iText.on('selection:changed', () => {
- count++;
- });
- eventStream = {
- canvas: [],
- source: [],
- target: [],
- };
- ['dragstart', 'dragover', 'drag', 'dragenter', 'dragleave', 'drop', 'dragend', 'changed', 'text:changed'].forEach(type => {
- canvas.on(type, (ev) => {
- eventStream.canvas.push({ ...ev, type });
- });
- iText.on(type, (ev) => {
- eventStream.source.push({ ...ev, type });
- });
- iText2.on(type, (ev) => {
- eventStream.target.push({ ...ev, type });
- });
- });
- renderEffects = [];
- canvas._renderDragEffects = (e, source, target) => renderEffects.push({ e, source, target });
- });
- hooks.afterEach(() => canvas.dispose());
-
- function startDragging(eventData) {
- const e = { ...eventData };
- canvas._onMouseDown({ ...eventData });
- canvas._onDragStart(e);
- return e;
- }
-
- function createDragEvent(x = eventData.clientX, y = eventData.clientY, dataTransfer = {}) {
- return {
- ...eventData,
- defaultPrevented: false,
- clientX: x,
- clientY: y,
- dataTransfer: {
- ...eventData.dataTransfer,
- ...dataTransfer
- }
- };
- }
-
- QUnit.test('click sets cursor', async function (assert) {
- assert.equal(count, 0, 'selection:changed fired');
- assert.equal(countCanvas, 0, 'text:selection:changed fired');
- let called = 0;
- // sinon spy!!
- // iText.setCursorByClick = () => called++;
- canvas._onMouseDown(eventData);
- assert.ok(iText.draggableTextDelegate.isActive(), 'flagged as dragging');
- assert.ok(iText.shouldStartDragging(), 'flagged as dragging');
-
- await wait();
- assertCursorAnimation(assert, iText);
- // assert.equal(called, 0, 'should not set cursor on mouse up');
- canvas._onMouseUp(eventData);
- assert.ok(!iText.draggableTextDelegate.isActive(), 'unflagged as dragging');
- assert.ok(!iText.shouldStartDragging(), 'unflagged as dragging');
- // assert.equal(called, 1, 'should set cursor on mouse up');
- assert.equal(iText.selectionStart, 2, 'set the selectionStart');
- assert.equal(iText.selectionEnd, 2, 'set the selectionend');
- assertCursorAnimation(assert, iText, true);
- assert.equal(count, 1, 'selection:changed fired');
- assert.equal(countCanvas, 1, 'text:selection:changed fired');
- });
-
- QUnit.test('drag end over selection focuses hiddenTextarea', function (assert) {
- startDragging(eventData);
- iText.hiddenTextarea.blur();
- canvas._onDragEnd(createDragEvent());
- assert.equal(fabric.getFabricDocument().activeElement, iText.hiddenTextarea, 'should have focused hiddenTextarea');
- });
-
- QUnit.test('drag start', function (assert) {
- const e = startDragging(eventData);
- const charStyle = {
- "stroke": null,
- "strokeWidth": 1,
- "fill": "rgb(0,0,0)",
- "fontFamily": "Times New Roman",
- "fontSize": 40,
- "fontWeight": "normal",
- "fontStyle": "normal",
- "underline": false,
- "overline": false,
- "linethrough": false,
- "deltaY": 0,
- "textBackgroundColor": ""
- };
- assert.equal(e.dataTransfer.data['text/plain'], 'test', 'should set text/plain');
- assert.deepEqual(JSON.parse(e.dataTransfer.data['application/fabric']), {
- value: 'test',
- styles: [charStyle, charStyle, charStyle, charStyle]
- }, 'should set application/fabric');
- assert.equal(e.dataTransfer.effectAllowed, 'copyMove', 'should set effectAllowed');
- assert.ok(e.dataTransfer.dragImageData.img instanceof HTMLCanvasElement, 'drag image was set');
- assert.equal(e.dataTransfer.dragImageData.x, 30, 'drag image position');
- assert.equal(e.dataTransfer.dragImageData.y, 15, 'drag image position');
- assert.deepEqual(renderEffects, [], 'not rendered effects yet');
- canvas._onDragEnd(eventData);
- assert.deepEqual(eventStream.source, [
- {
- e,
- target: iText,
- type: 'dragstart'
- }, {
- e: eventData,
- target: iText,
- type: 'dragend',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- didDrop: false
- }
- ], 'events should match');
- assert.deepEqual(eventStream.canvas, eventStream.source, 'events should match');
- });
-
- QUnit.test('disable drag start: onDragStart', async function (assert) {
- iText.onDragStart = () => false;
- const e = startDragging(eventData);
- assert.equal(iText.shouldStartDragging(), true, 'should flag dragging');
- assert.equal(iText.selectionStart, 0, 'selectionStart is kept');
- assert.equal(iText.selectionEnd, 4, 'selectionEnd is kept');
- assert.deepEqual(e.dataTransfer.data, {}, 'should not set dataTransfer');
- assert.equal(e.dataTransfer.effectAllowed, undefined, 'should not set effectAllowed');
- assert.deepEqual(e.dataTransfer.dragImageData, undefined, 'should not set dataTransfer');
- });
-
- QUnit.test('disable drag start: start', async function (assert) {
- iText.draggableTextDelegate.start = () => false;
- const e = startDragging(eventData);
- assert.equal(iText.shouldStartDragging(), false, 'should not flag dragging');
- assert.equal(iText.selectionStart, 2, 'selectionStart is set');
- assert.equal(iText.selectionEnd, 2, 'selectionEnd is set');
- assert.deepEqual(e.dataTransfer.data, {}, 'should not set dataTransfer');
- assert.equal(e.dataTransfer.effectAllowed, undefined, 'should not set effectAllowed');
- assert.deepEqual(e.dataTransfer.dragImageData, undefined, 'should not set dataTransfer');
- });
-
- QUnit.test('disable drag start: isActive', async function (assert) {
- iText.draggableTextDelegate.isActive = () => false;
- const e = startDragging(eventData);
- assert.equal(iText.shouldStartDragging(), false, 'should not flag dragging');
- assert.equal(iText.selectionStart, 0, 'selectionStart is kept');
- assert.equal(iText.selectionEnd, 4, 'selectionEnd is kept');
- assertCursorAnimation(assert, iText);
- assert.deepEqual(e.dataTransfer.data, {}, 'should not set dataTransfer');
- assert.equal(e.dataTransfer.effectAllowed, undefined, 'should not set effectAllowed');
- assert.deepEqual(e.dataTransfer.dragImageData, undefined, 'should not set dataTransfer');
- });
-
- QUnit.test('drag over: source', function (assert) {
- const e = startDragging(eventData);
- const dragEvents = [];
- let index;
- for (index = 0; index < 100; index++) {
- const dragOverEvent = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragOver(dragOverEvent);
- dragEvents.push(dragOverEvent);
- }
- const dragEnd = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragEnd(dragEnd);
- assertDragEventStream('source', eventStream.source, [
- { e, target: iText, type: 'dragstart' },
- {
- e: dragEvents[0],
- target: iText,
- type: 'dragenter',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false,
- pointer: new fabric.Point(30, 15),
- absolutePointer: new fabric.Point(30, 15),
- viewportPoint: new fabric.Point(30, 15),
- scenePoint: new fabric.Point(30, 15),
- previousTarget: undefined
- },
- ...dragEvents.slice(0, 32).map(e => ({
- e,
- target: iText,
- type: 'dragover',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false
- })),
- ...dragEvents.slice(32, 93).map(e => ({
- e,
- target: iText,
- type: 'dragover',
- subTargets: [],
- dragSource: iText,
- dropTarget: iText,
- canDrop: true
- })),
- {
- e: dragEvents[93],
- target: iText,
- type: 'dragleave',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false,
- pointer: new fabric.Point(123, 15),
- absolutePointer: new fabric.Point(123, 15),
- viewportPoint: new fabric.Point(123, 15),
- scenePoint: new fabric.Point(123, 15),
- nextTarget: undefined
- },
- {
- e: dragEnd,
- target: iText,
- type: 'dragend',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- didDrop: false,
- }
- ]);
- assert.deepEqual(renderEffects, [
- ...dragEvents.slice(0, 32).map(e => ({ e, source: iText, target: undefined })),
- ...dragEvents.slice(32, 93).map(e => ({ e, source: iText, target: iText })),
- ...dragEvents.slice(93).map(e => ({ e, source: iText, target: undefined })),
- ], 'render effects');
- assert.equal(fabric.getFabricDocument().activeElement, iText.hiddenTextarea, 'should have focused hiddenTextarea');
- });
-
- QUnit.test('drag over: target', function (assert) {
- const e = startDragging(eventData);
- const dragEvents = [];
- let index;
- for (index = 180; index < 190; index = index + 5) {
- const dragOverEvent = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragOver(dragOverEvent);
- dragEvents.push(dragOverEvent);
- }
- for (index = 0; index <= 20; index = index + 5) {
- const dragOverEvent = createDragEvent(eventData.clientX + 190 * canvas.getRetinaScaling(), eventData.clientY - index * canvas.getRetinaScaling());
- canvas._onDragOver(dragOverEvent);
- dragEvents.push(dragOverEvent);
- }
- const dragEnd = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragEnd(dragEnd);
- assertDragEventStream('source in target test', eventStream.source, [
- { e, target: iText, type: 'dragstart' },
- {
- e: dragEnd,
- target: iText,
- type: 'dragend',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- didDrop: false,
- }
- ]);
- assertDragEventStream('target', eventStream.target, [
- {
- e: dragEvents[0],
- target: iText2,
- type: 'dragenter',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false,
- pointer: new fabric.Point(210, 15),
- absolutePointer: new fabric.Point(210, 15),
- viewportPoint: new fabric.Point(210, 15),
- scenePoint: new fabric.Point(210, 15),
- previousTarget: undefined
- },
- ...dragEvents.slice(0, 6).map(e => ({
- e,
- target: iText2,
- type: 'dragover',
- subTargets: [],
- dragSource: iText,
- dropTarget: iText2,
- canDrop: true
- })),
- {
- e: dragEvents[6],
- target: iText2,
- type: 'dragleave',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false,
- pointer: new fabric.Point(220, -5),
- absolutePointer: new fabric.Point(220, -5),
- viewportPoint: new fabric.Point(220, -5),
- scenePoint: new fabric.Point(220, -5),
- nextTarget: undefined
- },
- ]);
- assert.deepEqual(renderEffects, [
- ...dragEvents.slice(0, 6).map(e => ({ e, source: iText, target: iText2 })),
- ...dragEvents.slice(6).map(e => ({ e, source: iText, target: undefined })),
- ], 'render effects');
- assert.equal(fabric.getFabricDocument().activeElement, iText.hiddenTextarea, 'should have focused hiddenTextarea');
- });
-
- QUnit.test('drag over: canvas', function (assert) {
- const e = startDragging(eventData);
- const dragEvents = [];
- let index;
- for (index = 0; index < 10; index++) {
- const dragOverEvent = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragOver(dragOverEvent);
- dragEvents.push(dragOverEvent);
- }
- // canvas._onDrop(dragEvent);
- const dragEnd = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragEnd(dragEnd);
- assertDragEventStream('canvas', eventStream.canvas, [
- { e, target: iText, type: 'dragstart' },
- ...dragEvents.map(e => ({
- e,
- target: iText,
- type: 'dragover',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false
- })),
- {
- e: dragEnd,
- target: iText,
- type: 'dragend',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- didDrop: false,
- }
- ]);
- assert.equal(fabric.getFabricDocument().activeElement, iText.hiddenTextarea, 'should have focused hiddenTextarea');
- });
-
- QUnit.test('drop on drag source', function (assert) {
- const e = startDragging(eventData);
- const dragEvents = [];
- let index;
- for (index = 70; index < 80; index = index + 5) {
- const dragOverEvent = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragOver(dragOverEvent);
- dragEvents.push(dragOverEvent);
- }
- const drop = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling(), undefined, { dropEffect: 'move' });
- canvas._onDrop(drop);
- canvas._onDragEnd(drop);
- assert.equal(iText.text, ' testestt', 'text after drop');
- assert.equal(iText.selectionStart, 4, 'selection after drop');
- assert.equal(iText.selectionEnd, 8, 'selection after drop');
- assertDragEventStream('drop on drag source', eventStream.source, [
- { e, target: iText, type: 'dragstart' },
- {
- e: dragEvents[0],
- target: iText,
- type: 'dragenter',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false,
- pointer: new fabric.Point(100, 15),
- absolutePointer: new fabric.Point(100, 15),
- viewportPoint: new fabric.Point(100, 15),
- scenePoint: new fabric.Point(100, 15),
- previousTarget: undefined
- },
- ...dragEvents.slice(0, 2).map(e => ({
- e,
- target: iText,
- type: 'dragover',
- subTargets: [],
- dragSource: iText,
- dropTarget: iText,
- canDrop: true
- })),
- {
- action: 'drop',
- index: 4,
- type: 'changed'
- },
- {
- e: drop,
- target: iText,
- type: 'drop',
- subTargets: [],
- dragSource: iText,
- dropTarget: iText,
- didDrop: true,
- pointer: new fabric.Point(110, 15),
- absolutePointer: new fabric.Point(110, 15),
- viewportPoint: new fabric.Point(110, 15),
- scenePoint: new fabric.Point(110, 15),
- },
- {
- e: drop,
- target: iText,
- type: 'dragend',
- subTargets: [],
- dragSource: iText,
- dropTarget: iText,
- didDrop: true,
- }
- ]);
- assert.equal(fabric.getFabricDocument().activeElement, iText.hiddenTextarea, 'should have focused hiddenTextarea');
- });
-
- QUnit.test('drop', function (assert) {
- const e = startDragging(eventData);
- const dragEvents = [];
- let index;
- for (index = 200; index < 210; index = index + 5) {
- const dragOverEvent = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragOver(dragOverEvent);
- dragEvents.push(dragOverEvent);
- }
- const drop = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling(), undefined, { dropEffect: 'move' });
- canvas._onDrop(drop);
- canvas._onDragEnd(drop);
- assert.equal(iText2.text, 'testestt2 test2', 'text after drop');
- assert.equal(iText2.selectionStart, 3, 'selection after drop');
- assert.equal(iText2.selectionEnd, 7, 'selection after drop');
- assertDragEventStream('drop', eventStream.target, [
- {
- e: dragEvents[0],
- target: iText2,
- type: 'dragenter',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false,
- pointer: new fabric.Point(230, 15),
- absolutePointer: new fabric.Point(230, 15),
- viewportPoint: new fabric.Point(230, 15),
- scenePoint: new fabric.Point(230, 15),
- previousTarget: undefined
- },
- ...dragEvents.slice(0, 2).map(e => ({
- e,
- target: iText2,
- type: 'dragover',
- subTargets: [],
- dragSource: iText,
- dropTarget: iText2,
- canDrop: true
- })),
- {
- action: 'drop',
- index: 3,
- type: 'changed'
- },
- {
- e: drop,
- target: iText2,
- type: 'drop',
- subTargets: [],
- dragSource: iText,
- dropTarget: iText2,
- didDrop: true,
- absolutePointer: new fabric.Point(240, 15),
- pointer: new fabric.Point(240, 15),
- viewportPoint: new fabric.Point(240, 15),
- scenePoint: new fabric.Point(240, 15),
- },
- ]);
- assert.equal(fabric.getFabricDocument().activeElement, iText2.hiddenTextarea, 'should have focused hiddenTextarea');
- });
-
- QUnit.test('disable drop', function (assert) {
- iText2.canDrop = () => false;
- const e = startDragging(eventData);
- const dragEvents = [];
- let index;
- for (index = 200; index < 210; index = index + 5) {
- const dragOverEvent = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling());
- canvas._onDragOver(dragOverEvent);
- dragEvents.push(dragOverEvent);
- }
- const drop = createDragEvent(eventData.clientX + index * canvas.getRetinaScaling(), undefined, { dropEffect: 'none' });
- // the window will not invoke a drop event so we call drag end to simulate correctly
- canvas._onDragEnd(drop);
- assert.equal(iText2.text, 'test2 test2', 'text after drop');
- assert.equal(iText2.selectionStart, 0, 'selection after drop');
- assert.equal(iText2.selectionEnd, 0, 'selection after drop');
- assertDragEventStream('drop', eventStream.target, [
- {
- e: dragEvents[0],
- target: iText2,
- type: 'dragenter',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false,
- pointer: new fabric.Point(230, 15),
- absolutePointer: new fabric.Point(230, 15),
- viewportPoint: new fabric.Point(230, 15),
- scenePoint: new fabric.Point(230, 15),
- previousTarget: undefined
- },
- ...dragEvents.slice(0, 2).map(e => ({
- e,
- target: iText2,
- type: 'dragover',
- subTargets: [],
- dragSource: iText,
- dropTarget: undefined,
- canDrop: false
- })),
- ]);
- assert.equal(fabric.getFabricDocument().activeElement, iText.hiddenTextarea, 'should have focused hiddenTextarea');
- });
- });
- });
-});
\ No newline at end of file