Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
"react": "^19.2.7",
"react-app-polyfill": "^2.0.0",
"react-confirm-alert": "^2.8.0",
"react-container-query": "^0.13.0",
"react-cookie": "^4.1.1",
"react-dom": "^19.2.7",
"react-i18next": "^12.0.0",
Expand Down
25 changes: 7 additions & 18 deletions src/components/Editor/Project/Project.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useEffect, useMemo, useState } from "react";
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import "react-tabs/style/react-tabs.css";
import "react-toastify/dist/ReactToastify.css";
import { useContainerQuery } from "react-container-query";
import classnames from "classnames";

import "../../../assets/stylesheets/Project.scss";
Expand All @@ -14,7 +12,7 @@ import ScratchProjectBar from "../../ProjectBar/ScratchProjectBar";
import Sidebar from "../../Menus/Sidebar/Sidebar";
import EditorInput from "../EditorInput/EditorInput";
import ResizableWithHandle from "../../../utils/ResizableWithHandle";
import { projContainer } from "../../../utils/containerQueries";
import { useContainerMinWidth } from "../../../hooks/useContainerMinWidth";
import ScratchContainer from "./ScratchContainer";

const Project = (props) => {
Expand Down Expand Up @@ -43,21 +41,12 @@ const Project = (props) => {
}
}, [autosave, isCodeEditorScratchProject, saving]);

const [params, containerRef] = useContainerQuery(projContainer);
const [defaultWidth, setDefaultWidth] = useState("auto");
const [defaultHeight, setDefaultHeight] = useState("auto");
const [maxWidth, setMaxWidth] = useState("100%");
const [handleDirection, setHandleDirection] = useState("right");
const [isDesktop, containerRef] = useContainerMinWidth(720);
const [loading, setLoading] = useState(true);

useMemo(() => {
const isDesktop = params["width-larger-than-720"];

setDefaultWidth(isDesktop ? "50%" : "100%");
setDefaultHeight(isDesktop ? "100%" : "50%");
setMaxWidth(isDesktop ? "75%" : "100%");
setHandleDirection(isDesktop ? "right" : "bottom");
}, [params["width-larger-than-720"]]);
const defaultWidth = isDesktop ? "50%" : "100%";
const defaultHeight = isDesktop ? "100%" : "50%";
const maxWidth = isDesktop ? "75%" : "100%";
const handleDirection = isDesktop ? "right" : "bottom";

useEffect(() => {
setLoading(false);
Expand Down
48 changes: 48 additions & 0 deletions src/components/Editor/Project/Project.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import configureStore from "redux-mock-store";
import Project from "./Project";
import { showSavedMessage } from "../../../utils/Notifications";
import { MemoryRouter } from "react-router-dom";
import { useContainerMinWidth } from "../../../hooks/useContainerMinWidth";

window.HTMLElement.prototype.scrollIntoView = jest.fn();

Expand All @@ -15,9 +16,16 @@ jest.mock("react-router-dom", () => ({
}));

jest.mock("../../../utils/Notifications");
jest.mock("../../../hooks/useContainerMinWidth", () => ({
useContainerMinWidth: jest.fn(),
}));

jest.useFakeTimers();

beforeEach(() => {
useContainerMinWidth.mockReturnValue([false, jest.fn()]);
});

const user1 = {
access_token: "myAccessToken",
profile: {
Expand Down Expand Up @@ -204,6 +212,46 @@ describe("When not logged in and falling on default container width", () => {
.length,
).toBe(1);
});

test("Shows right drag bar with expected params on desktop containers", () => {
useContainerMinWidth.mockReturnValue([true, jest.fn()]);

const middlewares = [];
const mockStore = configureStore(middlewares);
const initialState = {
editor: {
project: project,
openFiles: [["main.py"]],
focussedFileIndices: [0],
webComponent: false,
},
auth: {},
instructions: {},
};
const mockedStore = mockStore(initialState);
const { getByTestId } = render(
<Provider store={mockedStore}>
<MemoryRouter>
<div id="app">
<Project />
</div>
</MemoryRouter>
</Provider>,
);

const container = getByTestId("proj-editor-container");
expect(container).toHaveStyle({
"min-width": "25%",
"max-width": "75%",
width: "50%",
height: "100%",
});

expect(
container.getElementsByClassName("resizable-with-handle__handle--right")
.length,
).toBe(1);
});
});

test("Successful manual save prompts project saved message", async () => {
Expand Down
38 changes: 38 additions & 0 deletions src/hooks/useContainerMinWidth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useCallback, useLayoutEffect, useState } from "react";

const getObservedWidth = (entry) => {
const contentBoxSize = Array.isArray(entry.contentBoxSize)
? entry.contentBoxSize[0]
: entry.contentBoxSize;

return contentBoxSize?.inlineSize ?? entry.contentRect?.width;
};

export const useContainerMinWidth = (minWidth) => {
const [container, setContainer] = useState(null);
const [matches, setMatches] = useState(false);
const containerRef = useCallback((node) => setContainer(node), []);

useLayoutEffect(() => {
if (!container) return undefined;

const update = (width) => {
if (typeof width === "number") {
setMatches(width >= minWidth);
}
};

update(container.getBoundingClientRect().width);

if (!window.ResizeObserver) return undefined;

const observer = new window.ResizeObserver((entries) => {
if (entries[0]) update(getObservedWidth(entries[0]));
});
observer.observe(container);

return () => observer.disconnect();
Comment thread
abcampo-iry marked this conversation as resolved.
}, [container, minWidth]);

return [matches, containerRef];
};
84 changes: 84 additions & 0 deletions src/hooks/useContainerMinWidth.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { act, renderHook } from "@testing-library/react";

import { useContainerMinWidth } from "./useContainerMinWidth";

const originalResizeObserver = window.ResizeObserver;
let observers;

class MockResizeObserver {
constructor(callback) {
this.callback = callback;
this.observe = jest.fn();
this.disconnect = jest.fn();
observers.push(this);
}
}

const makeElement = (width) => ({
getBoundingClientRect: jest.fn(() => ({ width })),
});

const renderObservedHook = (width) => {
const hook = renderHook(() => useContainerMinWidth(720));
const element = makeElement(width);

act(() => {
hook.result.current[1](element);
});

return { ...hook, element };
};

describe("useContainerMinWidth", () => {
beforeEach(() => {
observers = [];
window.ResizeObserver = MockResizeObserver;
});

afterEach(() => {
window.ResizeObserver = originalResizeObserver;
});

test("reports whether the container is at least the minimum width", () => {
const { result } = renderObservedHook(600);

expect(result.current[0]).toBe(false);
expect(observers[0].observe).toHaveBeenCalled();

act(() => {
observers[0].callback([{ contentRect: { width: 720 } }]);
});

expect(result.current[0]).toBe(true);
});

test("uses contentBoxSize when available", () => {
const { result } = renderObservedHook(600);

act(() => {
observers[0].callback([
{ contentBoxSize: [{ inlineSize: 721 }], contentRect: { width: 600 } },
]);
});

expect(result.current[0]).toBe(true);
});

test("disconnects the observer on unmount", () => {
const { unmount } = renderObservedHook(800);
const observer = observers[0];

unmount();

expect(observer.disconnect).toHaveBeenCalled();
});

test("falls back to a one-time measurement without ResizeObserver", () => {
window.ResizeObserver = undefined;

const { result } = renderObservedHook(800);

expect(result.current[0]).toBe(true);
expect(observers).toEqual([]);
});
});
8 changes: 0 additions & 8 deletions src/utils/containerQueries.js

This file was deleted.

46 changes: 0 additions & 46 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4222,7 +4222,6 @@ __metadata:
react: "npm:^19.2.7"
react-app-polyfill: "npm:^2.0.0"
react-confirm-alert: "npm:^2.8.0"
react-container-query: "npm:^0.13.0"
react-cookie: "npm:^4.1.1"
react-dev-utils: "npm:^11.0.3"
react-dom: "npm:^19.2.7"
Expand Down Expand Up @@ -7086,13 +7085,6 @@ __metadata:
languageName: node
linkType: hard

"batch-processor@npm:^1.0.0":
version: 1.0.0
resolution: "batch-processor@npm:1.0.0"
checksum: 10/59452655203eeb94101770a4c31a3aa81a60f6403ef4e66870f2970f0873ebc795c442610aa420be34535f1e51d644a12f0c5a37fb3bde08bf5c00109ee67d97
languageName: node
linkType: hard

"batch@npm:0.6.1":
version: 0.6.1
resolution: "batch@npm:0.6.1"
Expand Down Expand Up @@ -8251,13 +8243,6 @@ __metadata:
languageName: node
linkType: hard

"container-query-toolkit@npm:0.1.3":
version: 0.1.3
resolution: "container-query-toolkit@npm:0.1.3"
checksum: 10/ac9931c73fa66d80eb25a61b374c8b90b6424d0205b7d5b81d8b208ab03c34d1c69e10561250ca142e1a9f2194e07876c290c06110d58a681bdb613faff150bb
languageName: node
linkType: hard

"content-disposition@npm:0.5.4":
version: 0.5.4
resolution: "content-disposition@npm:0.5.4"
Expand Down Expand Up @@ -9904,15 +9889,6 @@ __metadata:
languageName: node
linkType: hard

"element-resize-detector@npm:1.1.13":
version: 1.1.13
resolution: "element-resize-detector@npm:1.1.13"
dependencies:
batch-processor: "npm:^1.0.0"
checksum: 10/206f82f91f2fde29bb9f484f38012b184beb55d9abaaa25372b9e76930d0d917e446ac4302da161824d1ba4d7dc1a620bc8f50fe9a35fefb39274546b9293e10
languageName: node
linkType: hard

"element-size@npm:^1.1.1":
version: 1.1.1
resolution: "element-size@npm:1.1.1"
Expand Down Expand Up @@ -17968,19 +17944,6 @@ __metadata:
languageName: node
linkType: hard

"react-container-query@npm:^0.13.0":
version: 0.13.0
resolution: "react-container-query@npm:0.13.0"
dependencies:
container-query-toolkit: "npm:0.1.3"
resize-observer-lite: "npm:0.2.3"
peerDependencies:
react: ^0.14.0 || ^15.0.0-0 || ^16.0.0-0 || ^17 || ^18
react-dom: ^0.14.0 || ^15.0.0-0 || ^16.0.0-0 || ^17 || ^18
checksum: 10/2c310dd6f232c2ed9d5fbd6b9fd72e5a7892a4bcbba667db07046388206a5a4928f5b4c61342528c2761d29cb228a1e2ee09fd541d30fd4a0a8875de03ab4573
languageName: node
linkType: hard

"react-cookie@npm:^4.1.1":
version: 4.1.1
resolution: "react-cookie@npm:4.1.1"
Expand Down Expand Up @@ -18894,15 +18857,6 @@ __metadata:
languageName: node
linkType: hard

"resize-observer-lite@npm:0.2.3":
version: 0.2.3
resolution: "resize-observer-lite@npm:0.2.3"
dependencies:
element-resize-detector: "npm:1.1.13"
checksum: 10/4e947e2df787b39e3608ef779b5af31aafe16138b82322cedb8249106d607299f0d58fee4c375ca97dd689f63685b46ae00a02a8e5a08b1ff9d72308e0638e94
languageName: node
linkType: hard

"resolve-cwd@npm:^3.0.0":
version: 3.0.0
resolution: "resolve-cwd@npm:3.0.0"
Expand Down
Loading