Media editor: Compute the open panel instead of setting it in an effect by ramonjd · Pull Request #85881 · WordPress/gutenberg · GitHub
Skip to content

Media editor: Compute the open panel instead of setting it in an effect - #85881

Merged
ramonjd merged 2 commits into
trunkfrom
update/media-editor-derive-default-panel
Oct 29, 2026
Merged

ramonjd merged 2 commits into
trunkfrom
update/media-editor-derive-default-panel

Conversation

@ramonjd

@ramonjd ramonjd commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

What?

Follow up to #85880. Removes the effect in the media editor that picks the open settings panel, and computes the panel during render instead.

Why?

The effect ran after each render where the width or the media type changed, and set the panel from there. So a render with the wrong panel was committed first, then corrected: with the attachment already loaded, the editor committed Details and then switched to Crop. See Adjusting some state when a prop changes.

How?

  • What the user chose is state, in place of three refs: chosenPanel (undefined until the user opens or closes the panel, then null or the open panel's id) and chosenTab (null until the user picks a tab).
  • The tab is chosenTab, or Crop for an image on a wide screen, or Details otherwise. The open panel is chosenPanel, or until the user has toggled it, that tab on a wide screen and nothing on a narrow one.
  • Behaviour is unchanged, including one detail: after the user opens the panel themselves, the tab it shows does not change with the width.

Testing Instructions

No UI changes. There should be no regressions in the way the panels work.

  1. Insert an Image block with an image and open the media editor from its toolbar, in a window wider than 600px. The panel is open on Crop.
  2. Narrow the window below 600px. The panel closes. Widen it again: it reopens on Crop.
  3. Click the Details tab, narrow the window, then widen it. The panel reopens on Details.
  4. Close the panel with the "Media settings" button, narrow the window, then widen it. The panel stays closed. Click "Media settings": it opens on Crop.
  5. Reopen the editor in a window narrower than 600px. The panel is closed. Click "Media settings": it opens on Details. Widen the window: it stays on Details.
  6. Open the media editor for a file that is not an image. The panel opens on Details.
2026-10-29.13.53.29.mov

User of AI

Claude hunted down the useEffect and culled it

…ffect

The effect picked the default panel after each render where the width or the media type changed, using refs to remember what the user had chosen. Those choices are now state, and the open panel and its tab are computed from them during render, so the first commit already shows the right panel.
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

Preview in WordPress Playground

🎉 Props

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: andrewserong <andrewserong@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

📦 Bundle size

Size Change: -32 B (0%)

Total Size: 8.56 MB

📦 View Changed
Filename Size Change
build/scripts/editor/index.min.js 556 kB -32 B (-0.01%)

dbda153 Run

⚡ Performance

Show the results

Client side metrics exclude the server response time.

front-end-block-theme

Metric 81f5a5f trunk % Change
timeToFirstByte 55.6 ms +6.38% -3.24% 56.4 ms +6.74% -4.96% -1.42%
largestContentfulPaint 92 ms +8.7% -4.35% 90 ms +11.11% -2.22% 2.22%
lcpMinusTtfb 34.9 ms +8.17% -3.15% 34.65 ms +6.2% -8.8% 0.72%
wpBeforeTemplate 29.46 ms +12.02% -2.51% 29.14 ms +9.23% -2.02% 1.1%
wpTemplate 21.67 ms +2.86% -3.14% 22.44 ms +3.74% -4.59% -3.43%
wpTotal 51.71 ms +6.73% -2.96% 52.45 ms +7.19% -5.11% -1.41%
wpMemoryUsage 7.70 MB +0% -0% 7.67 MB +0% -0% 0.43%
wpDbQueries 16 +0% -0% 16 +0% -0% 0%

front-end-classic-theme

Metric 81f5a5f trunk % Change
timeToFirstByte 46.6 ms +5.58% -2.47% 45.5 ms +3.52% -1.87% 2.42%
largestContentfulPaint 100 ms +4% -0% 100 ms +2% -4% 0%
lcpMinusTtfb 53.75 ms +1.86% -2.42% 52.6 ms +3.52% -2.19% 2.19%
wpBeforeTemplate 26.41 ms +12.53% -1.93% 26.15 ms +3.71% -1.61% 0.99%
wpTemplate 16.42 ms +4.69% -2.56% 16.13 ms +3.53% -1.92% 1.8%
wpTotal 43.57 ms +5.51% -2.64% 42.6 ms +3.15% -2.32% 2.28%
wpMemoryUsage 6.39 MB +0% -0% 6.36 MB +0% -0% 0.53%
wpDbQueries 13 +0% -0% 13 +0% -0% 0%

media-processing

Metric 81f5a5f trunk % Change
mediaProcessingJpeg 400.15 ms +1.14% -1.03% 402.01 ms +1.23% -1.59% -0.46%
mediaProcessingAvif 6173.23 ms +0.55% -0.27% 6170.19 ms +0.11% -0.22% 0.05%
mediaProcessingJpegToAvif 4254.53 ms +0.12% -0.5% 4253.59 ms +0.32% -0.47% 0.02%

media-upload

Metric 81f5a5f trunk % Change
jpegUploadProcessing 1738.55 ms +13.9% -15.29% 1457.93 ms +1.24% -1.04% 19.25%
pngUploadProcessing 183.95 ms +49.31% -17.37% 172.63 ms +44.48% -3.56% 6.56%
largeJpegUploadProcessing 1466.19 ms +32.03% -0.87% 1689.9 ms +14.94% -13.45% -13.24%
multipleImageUploadProcessing 2036.41 ms +0.69% -1.25% 2031.01 ms +0.36% -0.55% 0.27%

post-editor

Metric 81f5a5f trunk % Change
serverResponse 534.75 ms +3% -2.82% 543.76 ms +5.11% -5.4% -1.66%
firstPaint 434.81 ms +15.55% -27.6% 384.91 ms +35.32% -27.62% 12.96%
domContentLoaded 888.02 ms +0.54% -2.68% 921.52 ms +0.83% -1.96% -3.64%
loaded 889.23 ms +0.54% -2.69% 922.78 ms +0.83% -1.98% -3.64%
firstContentfulPaint 512.09 ms +0.18% -2.21% 537.16 ms +1.87% -8.11% -4.67%
firstBlock 2949.12 ms +2.61% -1.63% 3081.71 ms +1.42% -0.19% -4.3%
type 19.23 ms +4.37% -8.01% 21.12 ms +7.24% -3.79% -8.95%
typeWithoutInspector 19.14 ms +2.98% -4.81% 19.79 ms +11.37% -7.18% -3.28%
typeWithTopToolbar 24.34 ms +3.04% -1.27% 25.96 ms +9.94% -3.2% -6.24%
typeContainer 8.96 ms +11.05% -12.83% 9.63 ms +1.97% -9.03% -6.96%
focus 120.28 ms +4.87% -2.63% 125.36 ms +8.44% -5.15% -4.05%
firstFocus 237.82 ms +0% -0% 278.29 ms +0% -0% -14.54%
selectAll 464.02 ms +0.88% -1.12% 482.7 ms +2.81% -3.41% -3.87%
undo 29.8 ms +14.06% -6.41% 32.85 ms +4.08% -3.14% -9.28%
listViewOpen 77.26 ms +13.88% -6.43% 83.26 ms +7.66% -7.93% -7.21%
inserterOpen 39.41 ms +3.02% -4.82% 47.48 ms +2.67% -15.21% -17%
inserterHover 12.19 ms +5.41% -10.01% 12.89 ms +1.78% -12.26% -5.43%
inserterSearch 8.06 ms +17.99% -2.85% 8.23 ms +6.93% -2.67% -2.07%
loadPatterns 688 ms +6.32% -1.89% 663.52 ms +2.25% -2.12% 3.69%
wpTotal 524.89 ms +2.97% -2.96% 533.48 ms +5.12% -5.6% -1.61%
wpMemoryUsage 14.75 MB +0% -0% 14.72 MB +0% -0% 0.24%
wpDbQueries 55 +0% -1.82% 55 +0% -1.82% 0%

site-editor

Metric 81f5a5f trunk % Change
serverResponse 541.23 ms +0.93% -7.72% 530 ms +3.52% -9.28% 2.12%
firstPaint 251.09 ms +9.24% -3.58% 291.83 ms +7.19% -4.43% -13.96%
domContentLoaded 916.51 ms +1.05% -0.98% 929.58 ms +1.44% -1.51% -1.41%
loaded 917.71 ms +1.04% -1% 930.58 ms +1.44% -1.51% -1.38%
firstContentfulPaint 501.66 ms +6.55% -0.68% 535.72 ms +3.57% -3.57% -6.36%
firstBlock 4015.54 ms +2.58% -0.54% 3975.68 ms +0.31% -0.95% 1%
type 20.32 ms +6.05% -7.19% 18.7 ms +4.81% -8.88% 8.66%
navigate 123.84 ms +1.61% -5.61% 119.92 ms +4.19% -2.99% 3.27%
loadPatterns 1247.27 ms +8.09% -4.19% 1199.02 ms +4.37% -4.76% 4.02%
loadPages 992.13 ms +6.85% -1.6% 990.73 ms +1.34% -0.99% 0.14%
wpTotal 531.16 ms +0.93% -7.9% 520.09 ms +3.58% -9.52% 2.13%
wpMemoryUsage 12.77 MB +0% -0% 12.74 MB +0% -0% 0.24%
wpDbQueries 44 +2.27% -0% 44 +2.27% -0% 0%

dbda153 Run

🏁 Flaky tests

Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

Undo restores the post selection when another synced entity is loaded (RTC WebSocket) in /test/e2e/specs/editor/collaboration/collaboration-undo-redo.spec.ts, passed after 1 failed attempt.
Error: expect(received).toMatchObject(expected)

- Expected  - 2
+ Received  + 2

  Object {
    "attributeKey": "content",
    "blockIndex": 0,
    "content": "abcdef",
-   "endOffset": 3,
-   "offset": 3,
+   "endOffset": 6,
+   "offset": 6,
  }

Call Log:
- Timeout 5000ms exceeded while waiting on the predicate
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/collaboration/collaboration-undo-redo.spec.ts:363:5
inserts a block should show a blue line indicator in /test/e2e/specs/editor/various/inserting-blocks.spec.js, passed after 1 failed attempt.
TypeError: Cannot read properties of null (reading 'y')
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/inserting-blocks.spec.js:681:32
passes the search value in the main inserter when clicking `Browse all` in /test/e2e/specs/editor/various/inserting-blocks.spec.js, passed after 1 failed attempt.
Error: apiRequestContext.fetch: socket hang up
Call log:
  - → GET http://localhost:8891/wp-json/wp/v2/blocks?per_page=100&status=publish%2Cfuture%2Cdraft%2Cpending%2Cprivate%2Ctrash
    - user-agent: Playwright/1.63.0 (x64; ubuntu 24.04) node/24.20 CI/1
    - accept: */*
    - accept-encoding: gzip,deflate,br
    - X-WP-Nonce: e1b024e775
    - cookie: wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_da433b7faafd8180ebeb1624ceeeab7e=admin%7C1793420606%7CFpaDZSZ6TzcixroNryU5fmbt6wZiOgiyFuTMrlGxOZS%7C3691d13b9bc78fe44606b9147b0cee1beac0fe93f009a9e48093814ca1c86ea0; wp-settings-time-1=1793248269

    at RequestUtils.rest (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/rest.ts:111:39)
    at RequestUtils.deleteAllBlocks (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/blocks.ts:23:28)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/inserting-blocks.spec.js:11:22
should cut and paste individual blocks with collapsed selection in /test/e2e/specs/editor/various/copy-cut-paste.spec.js, passed after 1 failed attempt.
Error: expect(string).toMatchSnapshot(expected) failed

  @@ -1,3 +1,3 @@
   <!-- wp:paragraph -->
  -<p>2Cut - collapsed selection</p>
  +<p>2</p>
   <!-- /wp:paragraph -->


    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/copy-cut-paste.spec.js:45:49
should indent and outdent level 1 in /test/e2e/specs/editor/blocks/list.spec.js, passed after 1 failed attempt.
TimeoutError: locator.click: Timeout 10000ms exceeded.
Call log:
  - waiting for locator('role=toolbar[name="Block tools"i]').locator('role=button[name="Indent"]')
    - locator resolved to <button id="_r_1l_" type="button" tabindex="-1" aria-label="Indent" aria-disabled="true" data-toolbar-item="true" data-truly-disabled="false" aria-describedby="components-button__description-26" class="components-button components-toolbar-button is-compact has-icon">…</button>
  - attempting click action
    2 × waiting for element to be visible, enabled and stable
      - element is not enabled
    - retrying click action
    - waiting 20ms
    2 × waiting for element to be visible, enabled and stable
      - element is not enabled
    - retrying click action
      - waiting 100ms
    18 × waiting for element to be visible, enabled and stable
       - element is not enabled
     - retrying click action
       - waiting 500ms

    at Editor.clickBlockToolbarButton (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/editor/click-block-toolbar-button.ts:17:15)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/blocks/list.spec.js:775:3
should indent and outdent level 2 in /test/e2e/specs/editor/blocks/list.spec.js, passed after 1 failed attempt.
TimeoutError: locator.click: Timeout 10000ms exceeded.
Call log:
  - waiting for locator('role=toolbar[name="Block tools"i]').locator('role=button[name="Indent"]')

    at Editor.clickBlockToolbarButton (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/editor/click-block-toolbar-button.ts:17:15)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/blocks/list.spec.js:933:3
should keep the list type when indenting an ordered list item in /test/e2e/specs/editor/blocks/list.spec.js, passed after 1 failed attempt.
Error: expect(received).toBe(expected) // Object.is equality

- Expected  - 1
+ Received  + 1

@@ -1,8 +1,8 @@
  <!-- wp:list {"ordered":true} -->
  <ol class="wp-block-list"><!-- wp:list-item -->
- <li>one<!-- wp:list {"ordered":true} -->
+ <li>ne<!-- wp:list {"ordered":true} -->
  <ol class="wp-block-list"><!-- wp:list-item -->
  <li>two</li>
  <!-- /wp:list-item --></ol>
  <!-- /wp:list --></li>
  <!-- /wp:list-item --></ol>

Call Log:
- Timeout 5000ms exceeded while waiting on the predicate
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/blocks/list.spec.js:916:52
should merge a following sibling list into the outermost list with Delete from a nested item (#77245) in /test/e2e/specs/editor/blocks/list.spec.js, passed after 1 failed attempt.
Error: expect(received).toMatchObject(expected)

- Expected  - 2
+ Received  + 2

@@ -8,11 +8,11 @@
          "innerBlocks": Array [
            Object {
              "innerBlocks": Array [
                Object {
                  "attributes": Object {
-                   "content": "b‸",
+                   "content": "b",
                  },
                  "name": "core/list-item",
                },
              ],
              "name": "core/list",
@@ -25,11 +25,11 @@
    },
    Object {
      "innerBlocks": Array [
        Object {
          "attributes": Object {
-           "content": "1",
+           "content": "‸1",
          },
          "innerBlocks": Array [
            Object {
              "innerBlocks": Array [
                Object {

Call Log:
- Timeout 5000ms exceeded while waiting on the predicate
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/blocks/list.spec.js:2854:41

dbda153 Run

@ramonjd
ramonjd requested a review from andrewserong October 29, 2026 02:57
@ramonjd
ramonjd marked this pull request as ready for review October 29, 2026 02:57
@andrewserong andrewserong added [Feature] Media Anything that impacts the experience of managing media [Type] Code Quality Issues or PRs that relate to code quality labels Oct 29, 2026

@andrewserong andrewserong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Preserves the expected behaviour:

✅ Opening the cropper on mobile, when you toggle the sidebar open it defaults to Details
✅ On desktop, the sidebar defaults to open, with Crop selected
✅ Resizing the browser window remembers the user's choices as expected
✅ If I've never toggled the sidebar, then dragging from big to small to big will re-open the sidebar
✅ If I've intentionally closed the sidebar, resizing doesn't re-open it unexpectedly

Anyway, you described all this in the PR description. Looking good!

// dragging the window narrow should still hand the canvas the full width.
const selectPanel = useCallback( ( panel: string ) => {
setChosenTab( panel );
setChosenPanel( ( open ) => ( open === undefined ? open : panel ) );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost not worth saying anything about, but it took me a second to realise that open in this case is the state of whether a panel has been chosen/selected by a user. I'm used to seeing open as the name of a function... if you wanted to rename, we could call this chosen instead? Anyway, no need to update, I know what it is now 😄

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! Whatever makes it clearer. Let's do that. I appreciate the review.

`open` read like a function name. The value is the panel the user chose, so call it `chosen`.
@ramonjd
ramonjd enabled auto-merge (squash) October 29, 2026 04:21
@ramonjd
ramonjd merged commit 3af70a6 into trunk Oct 29, 2026
78 checks passed
@ramonjd
ramonjd deleted the update/media-editor-derive-default-panel branch October 29, 2026 04:37
@github-actions github-actions Bot added this to the Gutenberg 24.4 milestone Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Media Anything that impacts the experience of managing media [Type] Code Quality Issues or PRs that relate to code quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants