From 46b159dce6de1dea3b09ac91901107dfe578e711 Mon Sep 17 00:00:00 2001 From: TallblokeUK Date: Tue, 29 Sep 2026 12:02:30 +0100 Subject: [PATCH 1/4] fix: keep a long line of code from widening the editor page --- CHANGELOG.md | 1 + src/css/edit/_form.scss | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e94b17f42..073eba5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ * Fixed featured Community Cloud snippets failing to load with some cloud API responses. (PRO) * Fixed bulk actions in Community Cloud running against an empty selection, so selected snippets were never downloaded. (PRO) +* Fixed a long unbroken line of code widening the snippet editor page and pushing the sidebar out of view. ## [3.10.2] (2026-09-01) diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss index 99aebec60..165507559 100644 --- a/src/css/edit/_form.scss +++ b/src/css/edit/_form.scss @@ -90,7 +90,12 @@ .snippet-form { display: grid; gap: var(--cs-sidebar-gap); - grid-template-columns: 1fr var(--cs-sidebar-width); + + // The content column is bounded at zero rather than left as a bare 1fr, + // whose automatic minimum is its content width. A single long unbroken line + // of code would otherwise widen the track to fit, taking the sidebar off + // the side of the page with it. + grid-template-columns: minmax(0, 1fr) var(--cs-sidebar-width); grid-template-areas: 'upper sidebar' 'lower sidebar'; transition: all 700ms; @@ -115,6 +120,7 @@ display: flex; flex-flow: column; gap: 24px; + min-inline-size: 0; } .snippet-editor-sidebar { @@ -134,7 +140,7 @@ } @media (width <= 1024px) { - grid-template-columns: 1fr; + grid-template-columns: minmax(0, 1fr); } h2, h3 { From eac99add41d9e958648ad62525e69d63b54c17fc Mon Sep 17 00:00:00 2001 From: TallblokeUK Date: Tue, 29 Sep 2026 12:02:33 +0100 Subject: [PATCH 2/4] test: cover editor layout with a long unbroken line --- tests/e2e/code-snippets-edit.spec.ts | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/e2e/code-snippets-edit.spec.ts b/tests/e2e/code-snippets-edit.spec.ts index 2257461bb..22a21f32b 100644 --- a/tests/e2e/code-snippets-edit.spec.ts +++ b/tests/e2e/code-snippets-edit.spec.ts @@ -1,6 +1,7 @@ import { expect, test } from '@playwright/test' import { DEFAULT_E2E_SNIPPET_BASE_NAME, SnippetsTestHelper } from './helpers/SnippetsTestHelper' import { MESSAGES, SELECTORS, TIMEOUTS } from './helpers/constants' +import { wpCli } from './helpers/wpCli' test.describe('Code Snippets Admin', () => { let helper: SnippetsTestHelper @@ -15,6 +16,45 @@ test.describe('Code Snippets Admin', () => { await helper.expectToBeOnSnippetsAdminPage() }) + test('A long unbroken line does not widen the editor page', async ({ page }) => { + const snippetName = SnippetsTestHelper.makeUniqueSnippetName('E2E Long Line') + const code = `// ${snippetName}\n$value = '${'a'.repeat(4000)}';\n` + + await wpCli(['eval', ` + $snippet = new \\Code_Snippets\\Model\\Snippet([ + 'name' => ${JSON.stringify(snippetName)}, + 'code' => ${JSON.stringify(code)}, + 'scope' => 'global', + 'active' => false, + ]); + echo \\Code_Snippets\\save_snippet($snippet)->id; + `]) + + try { + await helper.openSnippet(snippetName) + + // Line wrapping is on by default, and the page widened regardless. + await expect(page.locator('.CodeMirror').first()).toHaveClass(/CodeMirror-wrap/) + + // One pixel of tolerance covers sub-pixel rounding in layout. + const pageOverflow = await page.evaluate(() => + document.documentElement.scrollWidth - document.documentElement.clientWidth) + expect(pageOverflow, 'the page should not scroll sideways').toBeLessThanOrEqual(1) + + const sidebar = await page.locator('.snippet-editor-sidebar').boundingBox() + + if (!sidebar) { + throw new Error('The editor sidebar is not laid out') + } + + const viewportWidth = page.viewportSize()?.width ?? 0 + expect(sidebar.x + sidebar.width, 'the sidebar should stay on screen') + .toBeLessThanOrEqual(viewportWidth + 1) + } finally { + await helper.cleanupSnippet(snippetName) + } + }) + test('Can add a new snippet', async () => { const snippetName = SnippetsTestHelper.makeUniqueSnippetName() await helper.createSnippet({ From b474a7e0661e3661cfb0949557996ed09d02bb7b Mon Sep 17 00:00:00 2001 From: TallblokeUK Date: Mon, 5 Oct 2026 11:05:32 +0100 Subject: [PATCH 3/4] test: keep the long line fixture valid PHP --- tests/e2e/code-snippets-edit.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/e2e/code-snippets-edit.spec.ts b/tests/e2e/code-snippets-edit.spec.ts index 22a21f32b..d1de1506f 100644 --- a/tests/e2e/code-snippets-edit.spec.ts +++ b/tests/e2e/code-snippets-edit.spec.ts @@ -18,7 +18,10 @@ test.describe('Code Snippets Admin', () => { test('A long unbroken line does not widen the editor page', async ({ page }) => { const snippetName = SnippetsTestHelper.makeUniqueSnippetName('E2E Long Line') - const code = `// ${snippetName}\n$value = '${'a'.repeat(4000)}';\n` + // A comment, so the long line is valid PHP whatever it contains. The code + // is handed to PHP inside a double-quoted string, where a variable would + // be interpolated away and leave a parse error behind. + const code = `// ${snippetName}\n// ${'a'.repeat(4000)}\n` await wpCli(['eval', ` $snippet = new \\Code_Snippets\\Model\\Snippet([ From c7be3ed30a43c7bd6feb87fb60bfe7a6e6c055bb Mon Sep 17 00:00:00 2001 From: TallblokeUK Date: Tue, 6 Oct 2026 10:17:57 +0100 Subject: [PATCH 4/4] test: save the long line fixture through the editor --- tests/e2e/code-snippets-edit.spec.ts | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/tests/e2e/code-snippets-edit.spec.ts b/tests/e2e/code-snippets-edit.spec.ts index d1de1506f..f018b055c 100644 --- a/tests/e2e/code-snippets-edit.spec.ts +++ b/tests/e2e/code-snippets-edit.spec.ts @@ -1,7 +1,6 @@ import { expect, test } from '@playwright/test' import { DEFAULT_E2E_SNIPPET_BASE_NAME, SnippetsTestHelper } from './helpers/SnippetsTestHelper' import { MESSAGES, SELECTORS, TIMEOUTS } from './helpers/constants' -import { wpCli } from './helpers/wpCli' test.describe('Code Snippets Admin', () => { let helper: SnippetsTestHelper @@ -17,21 +16,16 @@ test.describe('Code Snippets Admin', () => { }) test('A long unbroken line does not widen the editor page', async ({ page }) => { - const snippetName = SnippetsTestHelper.makeUniqueSnippetName('E2E Long Line') - // A comment, so the long line is valid PHP whatever it contains. The code - // is handed to PHP inside a double-quoted string, where a variable would - // be interpolated away and leave a parse error behind. - const code = `// ${snippetName}\n// ${'a'.repeat(4000)}\n` - - await wpCli(['eval', ` - $snippet = new \\Code_Snippets\\Model\\Snippet([ - 'name' => ${JSON.stringify(snippetName)}, - 'code' => ${JSON.stringify(code)}, - 'scope' => 'global', - 'active' => false, - ]); - echo \\Code_Snippets\\save_snippet($snippet)->id; - `]) + const snippetName = SnippetsTestHelper.makeUniqueSnippetName() + + // Saved through the editor rather than WP-CLI. CI runs WP-CLI as root, and + // with file-based execution on, the first snippet saved that way leaves + // root-owned directories that the web server cannot write later snippets + // into, so every snippet saved after it silently never runs. + await helper.createSnippet({ + name: snippetName, + code: `// ${'a'.repeat(4000)}` + }) try { await helper.openSnippet(snippetName)