Skip to content

Commit

Permalink
fix mitmweb splitter becoming drag and drop (mitmproxy#6492)
Browse files Browse the repository at this point in the history
  • Loading branch information
xBZZZZ committed Dec 12, 2023
1 parent 9787871 commit 13c976d
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 117 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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)

Expand Down
183 changes: 125 additions & 58 deletions web/src/js/__tests__/components/common/SplitterSpec.tsx
Expand Up @@ -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(<Splitter />),
describe.each([
["", ""],
["x", "X"],
["y", "Y"],
])("Splitter Component", (axisLower, axisUpper) => {
if (axisLower === "") {
it("should render correctly with default (x) axis", () => {
const splitter = renderer.create(<Splitter />),
tree = splitter.toJSON();
expect(tree).toMatchInlineSnapshot(`
<div
className="splitter splitter-x"
>
<div
onLostPointerCapture={[Function]}
onPointerDown={[Function]}
onPointerMove={[Function]}
/>
</div>
`);
});
return;
}

it("should render correctly with specified axis", () => {
const splitter = renderer.create(<Splitter axis={axisLower} />),
tree = splitter.toJSON();
expect(tree).toMatchSnapshot();
expect(tree).toMatchInlineSnapshot(`
<div
className="splitter splitter-${axisLower}"
>
<div
onLostPointerCapture={[Function]}
onPointerDown={[Function]}
onPointerMove={[Function]}
/>
</div>
`);
});

let splitter = TestUtils.renderIntoDocument(<Splitter />),
const splitter = TestUtils.renderIntoDocument(
<Splitter axis={axisLower} />
),
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(<Splitter axis="y" />);
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();
});
Expand All @@ -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);
});
});

This file was deleted.

88 changes: 41 additions & 47 deletions web/src/js/components/common/Splitter.tsx
Expand Up @@ -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 = {
Expand All @@ -17,62 +18,51 @@ export default class Splitter extends Component<SplitterProps, SplitterState> {

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() {
Expand Down Expand Up @@ -116,7 +106,11 @@ export default class Splitter extends Component<SplitterProps, SplitterState> {
this.props.axis === "x" ? "splitter-x" : "splitter-y"
)}
>
<div onMouseDown={this.onMouseDown} draggable="true" />
<div
onLostPointerCapture={this.onLostPointerCapture}
onPointerDown={this.onPointerDown}
onPointerMove={this.onPointerMove}
/>
</div>
);
}
Expand Down

0 comments on commit 13c976d

Please sign in to comment.