diff --git a/src/assets/stylesheets/Project.scss b/src/assets/stylesheets/Project.scss index 207b2b318..5da294adb 100644 --- a/src/assets/stylesheets/Project.scss +++ b/src/assets/stylesheets/Project.scss @@ -1,4 +1,5 @@ @use "./ScratchContainer"; +@use "./sizes" as *; .proj { display: flex; @@ -34,7 +35,7 @@ flex-direction: row; overflow-y: hidden; - --resize-handle-size: 16px; + --resize-handle-size: #{$resize-handle-size}; gap: var(--resize-handle-size); margin-block-start: var(--editor-margin-block-start, var(--space-2)); diff --git a/src/assets/stylesheets/Sidebar.scss b/src/assets/stylesheets/Sidebar.scss index d2d5d3307..4b87f4b63 100644 --- a/src/assets/stylesheets/Sidebar.scss +++ b/src/assets/stylesheets/Sidebar.scss @@ -1,6 +1,12 @@ @use "@raspberrypifoundation/design-system-core/scss/mixins/typography"; @use "./rpf_design_system/mixins" as *; -@use "./breakpoints" as *; +@use "./sizes" as *; + +$sidebar-min-width: 350px; +$block-editor-min-width: 1024px; +$small-sidebar-breakpoint: calc( + $sidebar-min-width + $block-editor-min-width + $resize-handle-size +); /* Sidebar */ .sidebar { @@ -28,7 +34,7 @@ margin: var(--space-1-5); display: var(--editor-logo-display, inherit); - @include unique-max-width($stacked-editor-max-width) { + @include unique-max-width($small-sidebar-breakpoint) { block-size: var(--space-4); inline-size: var(--space-4); margin: var(--space-1) var(--space-0-5); @@ -87,7 +93,7 @@ padding: var(--space-1); border: 1px solid transparent; - @include unique-max-width($stacked-editor-max-width) { + @include unique-max-width($small-sidebar-breakpoint) { padding: var(--space-0-5); } @@ -108,7 +114,7 @@ block-size: 60px; background-color: var(--sidebar-panel-background); - @include unique-max-width($stacked-editor-max-width) { + @include unique-max-width($small-sidebar-breakpoint) { inset-block: -4px -1px; inset-inline-end: -8px; block-size: 40px; @@ -136,7 +142,7 @@ margin: 0; } - @include unique-max-width($stacked-editor-max-width) { + @include unique-max-width($small-sidebar-breakpoint) { &.btn { min-block-size: 32px; min-inline-size: 32px; diff --git a/src/assets/stylesheets/_breakpoints.scss b/src/assets/stylesheets/_breakpoints.scss deleted file mode 100644 index ac07cac02..000000000 --- a/src/assets/stylesheets/_breakpoints.scss +++ /dev/null @@ -1 +0,0 @@ -$stacked-editor-max-width: 1146px; diff --git a/src/assets/stylesheets/_sizes.scss b/src/assets/stylesheets/_sizes.scss new file mode 100644 index 000000000..f0d8f32a3 --- /dev/null +++ b/src/assets/stylesheets/_sizes.scss @@ -0,0 +1 @@ +$resize-handle-size: 16px; diff --git a/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx b/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx index 218233655..0ab6cd50c 100644 --- a/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx +++ b/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx @@ -558,6 +558,7 @@ describe("When instructions are not editable", () => { renderWithProviders(, { preloadedState: { editor: { + project: {}, instructionsEditable: false, }, instructions: { diff --git a/src/components/Menus/Sidebar/SettingsPanel/SettingsPanel.test.jsx b/src/components/Menus/Sidebar/SettingsPanel/SettingsPanel.test.jsx index dca618836..c69ba9863 100644 --- a/src/components/Menus/Sidebar/SettingsPanel/SettingsPanel.test.jsx +++ b/src/components/Menus/Sidebar/SettingsPanel/SettingsPanel.test.jsx @@ -13,6 +13,7 @@ describe("Settings panel", () => { const mockStore = configureStore(middlewares); const initialState = { editor: { + project: {}, isThemeable: true, }, }; @@ -46,6 +47,7 @@ describe("Settings panel", () => { const mockStore = configureStore(middlewares); const initialState = { editor: { + project: {}, isThemeable: false, }, }; diff --git a/src/components/Menus/Sidebar/SidebarPanel.jsx b/src/components/Menus/Sidebar/SidebarPanel.jsx index 4b4ff8b9b..b0c1712d1 100644 --- a/src/components/Menus/Sidebar/SidebarPanel.jsx +++ b/src/components/Menus/Sidebar/SidebarPanel.jsx @@ -1,11 +1,9 @@ import React, { useContext, useState } from "react"; import ResizableWithHandle from "../../../utils/ResizableWithHandle"; +import { useSelector } from "react-redux"; import classNames from "classnames"; import PropTypes from "prop-types"; -import { - MOBILE_MEDIA_QUERY, - SMALL_SCREEN_MEDIA_QUERY, -} from "../../../utils/mediaQueryBreakpoints"; +import { MOBILE_MEDIA_QUERY } from "../../../utils/mediaQueryBreakpoints"; import { useMediaQuery } from "react-responsive"; import { SidebarActiveOptionContext, @@ -15,6 +13,20 @@ import { const MIN_WIDTH = "280px"; +export const SMALL_SIDEBAR_PROJECT_TYPE_BREAKPOINTS = { + code_editor_scratch: "(max-width: 1430px)", +}; + +export const SMALL_SIDEBAR_PROJECT_TYPE = "(max-width: 1146px)"; + +export const sidebarStartingWidth = (projectType, defaultWidth) => { + const breakpoint = + SMALL_SIDEBAR_PROJECT_TYPE_BREAKPOINTS[projectType] || + SMALL_SIDEBAR_PROJECT_TYPE; + + return window.matchMedia(breakpoint).matches ? MIN_WIDTH : defaultWidth; +}; + const SidebarPanel = (props) => { const { children, @@ -26,6 +38,7 @@ const SidebarPanel = (props) => { panelRef, defaultWidth = "320px", } = props; + const projectType = useSelector((state) => state.editor.project.project_type); const activeOption = useContext(SidebarActiveOptionContext); const tabpanelProps = activeOption ? { @@ -36,10 +49,8 @@ const SidebarPanel = (props) => { } : {}; const isMobile = useMediaQuery({ query: MOBILE_MEDIA_QUERY }); - const [initialWidth] = useState(() => - window.matchMedia(SMALL_SCREEN_MEDIA_QUERY).matches - ? MIN_WIDTH - : defaultWidth, + const [initialWidth] = useState( + sidebarStartingWidth(projectType, defaultWidth), ); const buttonsIsEmptyArray = diff --git a/src/components/Menus/Sidebar/SidebarPanel.test.jsx b/src/components/Menus/Sidebar/SidebarPanel.test.jsx index 38b30ca3a..56e61635a 100644 --- a/src/components/Menus/Sidebar/SidebarPanel.test.jsx +++ b/src/components/Menus/Sidebar/SidebarPanel.test.jsx @@ -1,32 +1,38 @@ import React from "react"; -import { render, screen } from "@testing-library/react"; -import SidebarPanel from "./SidebarPanel"; -import { SMALL_SCREEN_MEDIA_QUERY } from "../../../utils/mediaQueryBreakpoints"; +import { screen } from "@testing-library/react"; +import { renderWithProviders } from "../../../utils/renderWithProviders"; +import SidebarPanel, { + SMALL_SIDEBAR_PROJECT_TYPE, + SMALL_SIDEBAR_PROJECT_TYPE_BREAKPOINTS, +} from "./SidebarPanel"; const Footer = () =>

footer

; -beforeEach(() => { - render( - - some content - , - ); -}); - test("Renders heading", () => { + renderWithProviders( + some content, + ); expect(screen.queryByText("heading")).toBeInTheDocument(); }); test("Renders content", () => { + renderWithProviders( + some content, + ); expect(screen.queryByText("some content")).toBeInTheDocument(); }); test("Renders footer", () => { + renderWithProviders( + + some content + , + ); expect(screen.queryByText("footer")).toBeInTheDocument(); }); test("Renders a single button", () => { - render( + renderWithProviders( button]}> some content , @@ -35,7 +41,7 @@ test("Renders a single button", () => { }); test("Renders arbitrary header content", () => { - render( + renderWithProviders( a link}> some content , @@ -44,7 +50,7 @@ test("Renders arbitrary header content", () => { }); test("Renders header content alongside buttons", () => { - render( + renderWithProviders( button]} @@ -58,7 +64,7 @@ test("Renders header content alongside buttons", () => { }); test("Renders multiple buttons", () => { - render( + renderWithProviders( { describe("Default width", () => { const originalMatchMedia = window.matchMedia; - const mockSmallScreen = (matches) => { + const mockMatchingMediaQuery = (matchingQuery) => { window.matchMedia = (query) => ({ ...originalMatchMedia(query), - matches: matches && query === SMALL_SCREEN_MEDIA_QUERY, + matches: query === matchingQuery, }); }; @@ -88,27 +94,68 @@ describe("Default width", () => { }); test("Uses the default width on larger screens", () => { - mockSmallScreen(false); - render(some content); - const panel = screen.getAllByTestId("sidebar__panel").at(-1); - expect(panel).toHaveStyle({ width: "320px" }); + mockMatchingMediaQuery(null); + renderWithProviders( + some content, + ); + expect(screen.getByTestId("sidebar__panel")).toHaveStyle({ + width: "320px", + }); }); test("Uses the minimum width on smaller screens", () => { - mockSmallScreen(true); - render(some content); - const panel = screen.getAllByTestId("sidebar__panel").at(-1); - expect(panel).toHaveStyle({ width: "280px" }); + mockMatchingMediaQuery(SMALL_SIDEBAR_PROJECT_TYPE); + renderWithProviders( + some content, + ); + expect(screen.getByTestId("sidebar__panel")).toHaveStyle({ + width: "280px", + }); + }); + + test("Uses the minimum width sooner for project types that need more room", () => { + mockMatchingMediaQuery( + SMALL_SIDEBAR_PROJECT_TYPE_BREAKPOINTS.code_editor_scratch, + ); + renderWithProviders( + some content, + { + preloadedState: { + editor: { project: { project_type: "code_editor_scratch" } }, + }, + }, + ); + expect(screen.getByTestId("sidebar__panel")).toHaveStyle({ + width: "280px", + }); + }); + + test("Uses the default width at that same size for other project types", () => { + mockMatchingMediaQuery( + SMALL_SIDEBAR_PROJECT_TYPE_BREAKPOINTS.code_editor_scratch, + ); + renderWithProviders( + some content, + { + preloadedState: { + editor: { project: { project_type: "python" } }, + }, + }, + ); + expect(screen.getByTestId("sidebar__panel")).toHaveStyle({ + width: "320px", + }); }); test("Keeps its width when the screen size changes", () => { - mockSmallScreen(true); - const { rerender } = render( + mockMatchingMediaQuery(SMALL_SIDEBAR_PROJECT_TYPE); + const { rerender } = renderWithProviders( some content, ); - mockSmallScreen(false); + mockMatchingMediaQuery(null); rerender(some content); - const panel = screen.getAllByTestId("sidebar__panel").at(-1); - expect(panel).toHaveStyle({ width: "280px" }); + expect(screen.getByTestId("sidebar__panel")).toHaveStyle({ + width: "280px", + }); }); }); diff --git a/src/utils/mediaQueryBreakpoints.js b/src/utils/mediaQueryBreakpoints.js index 6f60bb927..1da98634b 100644 --- a/src/utils/mediaQueryBreakpoints.js +++ b/src/utils/mediaQueryBreakpoints.js @@ -1,4 +1,2 @@ export const MOBILE_BREAKPOINT = "600px"; export const MOBILE_MEDIA_QUERY = `(max-width: ${MOBILE_BREAKPOINT})`; -export const SMALL_SCREEN_BREAKPOINT = "1146px"; -export const SMALL_SCREEN_MEDIA_QUERY = `(max-width: ${SMALL_SCREEN_BREAKPOINT})`;