diff --git a/CHANGELOG.md b/CHANGELOG.md index bdac343e75..b47e55ff05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ ([#5084](https://github.com/mitmproxy/mitmproxy/pull/5084), @Speedlulu) * Scripts with relative paths are now loaded relative to the config file and not where the command is ran ([#4860](https://github.com/mitmproxy/mitmproxy/pull/4860), @Speedlulu) +* Fix `mitmweb` splitter becoming drag and drop. + ([#6492](https://github.com/mitmproxy/mitmproxy/pull/6492), @xBZZZZ) * Enhance documentation and add alert log messages when stream_large_bodies and modify_body are set ([#6514](https://github.com/mitmproxy/mitmproxy/pull/6514), @rosydawn6) diff --git a/web/src/js/__tests__/components/common/SplitterSpec.tsx b/web/src/js/__tests__/components/common/SplitterSpec.tsx index 488656d91e..fff51c7be6 100644 --- a/web/src/js/__tests__/components/common/SplitterSpec.tsx +++ b/web/src/js/__tests__/components/common/SplitterSpec.tsx @@ -4,87 +4,155 @@ import renderer from "react-test-renderer"; import Splitter from "../../../components/common/Splitter"; import TestUtils from "react-dom/test-utils"; -describe("Splitter Component", () => { - it("should render correctly", () => { - let splitter = renderer.create(), +describe.each([ + ["", ""], + ["x", "X"], + ["y", "Y"], +])("Splitter Component", (axisLower, axisUpper) => { + if (axisLower === "") { + it("should render correctly with default (x) axis", () => { + const splitter = renderer.create(), + tree = splitter.toJSON(); + expect(tree).toMatchInlineSnapshot(` +
+
+
+`); + }); + return; + } + + it("should render correctly with specified axis", () => { + const splitter = renderer.create(), tree = splitter.toJSON(); - expect(tree).toMatchSnapshot(); + expect(tree).toMatchInlineSnapshot(` +
+
+
+`); }); - let splitter = TestUtils.renderIntoDocument(), + const splitter = TestUtils.renderIntoDocument( + + ), dom = ReactDOM.findDOMNode(splitter), previousElementSibling = { - offsetHeight: 0, - offsetWidth: 0, + offsetWidth: 300, + offsetHeight: 500, style: { flex: "" }, }, nextElementSibling = { style: { flex: "" }, }; - it("should handle mouseDown ", () => { - window.addEventListener = jest.fn(); - splitter.onMouseDown({ pageX: 1, pageY: 2 }); - expect(splitter.state.startX).toEqual(1); - expect(splitter.state.startY).toEqual(2); - expect(window.addEventListener).toBeCalledWith( - "mousemove", - splitter.onMouseMove - ); - expect(window.addEventListener).toBeCalledWith( - "mouseup", - splitter.onMouseUp - ); - expect(window.addEventListener).toBeCalledWith( - "dragend", - splitter.onDragEnd - ); + Object.defineProperties(dom, { + previousElementSibling: { value: previousElementSibling }, + nextElementSibling: { value: nextElementSibling }, }); + dom.firstElementChild.setPointerCapture = jest.fn(); - it("should handle dragEnd", () => { - window.removeEventListener = jest.fn(); - splitter.onDragEnd(); - expect(dom.style.transform).toEqual(""); - expect(window.removeEventListener).toBeCalledWith( - "dragend", - splitter.onDragEnd - ); - expect(window.removeEventListener).toBeCalledWith( - "mouseup", - splitter.onMouseUp - ); - expect(window.removeEventListener).toBeCalledWith( - "mousemove", - splitter.onMouseMove + it("should handle pointerdown", () => { + const e = { + pageX: 13, + pageY: 22, + pointerId: -4618, + target: dom.firstElementChild, + }; + expect(splitter.state.dragPointer).toEqual(0.1); + splitter.onPointerDown(e); + expect(e.target.setPointerCapture).toBeCalledWith(-4618); + expect(splitter.state.dragPointer).toEqual(-4618); + expect(splitter.state.startPos).toEqual(e[`page${axisUpper}`]); + }); + + it("should handle pointermove", () => { + const e = { + pageX: 62, + pageY: 21, + pointerId: -4618, + target: dom.firstElementChild, + }; + splitter.onPointerMove(e); + expect(dom.style.transform).toEqual( + axisLower === "x" + ? `translateX(${62 - 13}px)` + : `translateY(${21 - 22}px)` ); }); - it("should handle mouseUp", () => { - Object.defineProperty(dom, "previousElementSibling", { - value: previousElementSibling, - }); - Object.defineProperty(dom, "nextElementSibling", { - value: nextElementSibling, - }); - splitter.onMouseUp({ pageX: 3, pageY: 4 }); - expect(splitter.state.applied).toBeTruthy(); + it("should handle lostpointercapture", () => { + const e = { + pageX: 56, + pageY: 82, + pointerId: -4618, + target: dom.firstElementChild, + }; + splitter.onLostPointerCapture(e); + expect(splitter.state.dragPointer).toEqual(0.1); + expect(dom.style.transform).toEqual(""); + expect(previousElementSibling.style.flex).toEqual( + `0 0 ${axisLower === "x" ? 300 + 56 - 13 : 500 + 82 - 22}px` + ); expect(nextElementSibling.style.flex).toEqual("1 1 auto"); - expect(previousElementSibling.style.flex).toEqual("0 0 2px"); }); - it("should handle mouseMove", () => { - splitter.onMouseMove({ pageX: 10, pageY: 10 }); - expect(dom.style.transform).toEqual("translate(9px, 0px)"); + it("should not resize previousElementSibling negative", () => { + const e = { + pageX: 56, + pageY: 82, + pointerId: 47, + target: dom.firstElementChild, + }; + splitter.onPointerDown(e); + e[`page${axisUpper}`] = -1234; + splitter.onLostPointerCapture(e); + expect(previousElementSibling.style.flex).toEqual("0 0 0px"); + }); - let splitterY = TestUtils.renderIntoDocument(); - splitterY.onMouseMove({ pageX: 10, pageY: 10 }); - expect(ReactDOM.findDOMNode(splitterY).style.transform).toEqual( - "translate(0px, 10px)" + it("should ignore other pointers", () => { + splitter.onPointerDown({ + pageX: 70, + pageY: 60, + pointerId: 47, + target: dom.firstElementChild, + }); + splitter.onPointerDown({ + pageX: 70, + pageY: 60, + pointerId: 46, + target: dom.firstElementChild, + }); + expect(splitter.state.dragPointer).toEqual(47); + splitter.onPointerMove({ pageX: 75, pageY: 55, pointerId: 46 }); + expect(dom.style.transform).toEqual(""); + splitter.onPointerMove({ + pageX: 74, + pageY: 54, + pointerId: 47, + target: dom.firstElementChild, + }); + splitter.onLostPointerCapture({ pageX: 76, pageY: 56, pointerId: 46 }); + expect(dom.style.transform).toEqual( + axisLower === "x" + ? `translateX(${74 - 70}px)` + : `translateY(${54 - 60}px)` ); }); it("should handle resize", () => { - let x = jest.spyOn(window, "setTimeout"); + const x = jest.spyOn(window, "setTimeout"); splitter.onResize(); expect(x).toHaveBeenCalled(); }); @@ -99,7 +167,6 @@ describe("Splitter Component", () => { it("should handle reset", () => { splitter.reset(false); expect(splitter.state.applied).toBeFalsy(); - expect(splitter.reset(true)).toEqual(undefined); }); }); diff --git a/web/src/js/__tests__/components/common/__snapshots__/SplitterSpec.tsx.snap b/web/src/js/__tests__/components/common/__snapshots__/SplitterSpec.tsx.snap deleted file mode 100644 index dd70ed7a7d..0000000000 --- a/web/src/js/__tests__/components/common/__snapshots__/SplitterSpec.tsx.snap +++ /dev/null @@ -1,12 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Splitter Component should render correctly 1`] = ` -
-
-
-`; diff --git a/web/src/js/components/common/Splitter.tsx b/web/src/js/components/common/Splitter.tsx index fd07f43b35..1db0d6a3ce 100644 --- a/web/src/js/components/common/Splitter.tsx +++ b/web/src/js/components/common/Splitter.tsx @@ -4,8 +4,9 @@ import classnames from "classnames"; type SplitterState = { applied: boolean; - startX: number; - startY: number; + startPos: number; + // .dragPointer === 0.1 means not dragging + dragPointer: number; }; type SplitterProps = { @@ -17,62 +18,51 @@ export default class Splitter extends Component { constructor(props, context) { super(props, context); - - this.state = { applied: false, startX: 0, startY: 0 }; - - this.onMouseMove = this.onMouseMove.bind(this); - this.onMouseDown = this.onMouseDown.bind(this); - this.onMouseUp = this.onMouseUp.bind(this); - this.onDragEnd = this.onDragEnd.bind(this); - } - - onMouseDown(e) { - this.setState({ startX: e.pageX, startY: e.pageY }); - - window.addEventListener("mousemove", this.onMouseMove); - window.addEventListener("mouseup", this.onMouseUp); - // Occasionally, only a dragEnd event is triggered, but no mouseUp. - window.addEventListener("dragend", this.onDragEnd); + this.state = { applied: false, startPos: 0, dragPointer: 0.1 }; + this.onLostPointerCapture = this.onLostPointerCapture.bind(this); + this.onPointerDown = this.onPointerDown.bind(this); + this.onPointerMove = this.onPointerMove.bind(this); } - onDragEnd() { - ReactDOM.findDOMNode(this).style.transform = ""; - - window.removeEventListener("dragend", this.onDragEnd); - window.removeEventListener("mouseup", this.onMouseUp); - window.removeEventListener("mousemove", this.onMouseMove); + onPointerDown(e) { + if (this.state.dragPointer !== 0.1) { + return; + } + e.target.setPointerCapture(e.pointerId); + this.setState({ + startPos: this.props.axis === "x" ? e.pageX : e.pageY, + dragPointer: e.pointerId, + }); } - onMouseUp(e) { - this.onDragEnd(); - - const node = ReactDOM.findDOMNode(this); - const prev = node.previousElementSibling; - - let flexBasis = prev.offsetHeight + e.pageY - this.state.startY; - - if (this.props.axis === "x") { - flexBasis = prev.offsetWidth + e.pageX - this.state.startX; + onLostPointerCapture(e) { + if (this.state.dragPointer !== e.pointerId) { + return; } + const node = e.target.parentNode; + const prev = node.previousElementSibling; - prev.style.flex = `0 0 ${Math.max(0, flexBasis)}px`; + node.style.transform = ""; + prev.style.flex = `0 0 ${Math.max( + 0, + (this.props.axis === "x" + ? prev.offsetWidth + e.pageX + : prev.offsetHeight + e.pageY) - this.state.startPos + )}px`; node.nextElementSibling.style.flex = "1 1 auto"; - this.setState({ applied: true }); + this.setState({ applied: true, dragPointer: 0.1 }); this.onResize(); } - onMouseMove(e) { - let dX = 0; - let dY = 0; - if (this.props.axis === "x") { - dX = e.pageX - this.state.startX; - } else { - dY = e.pageY - this.state.startY; + onPointerMove(e) { + if (this.state.dragPointer !== e.pointerId) { + return; } - ReactDOM.findDOMNode( - this - ).style.transform = `translate(${dX}px, ${dY}px)`; + e.target.parentNode.style.transform = + this.props.axis === "x" + ? `translateX(${e.pageX - this.state.startPos}px)` + : `translateY(${e.pageY - this.state.startPos}px)`; } onResize() { @@ -116,7 +106,11 @@ export default class Splitter extends Component { this.props.axis === "x" ? "splitter-x" : "splitter-y" )} > -
+
); }