Skip to content

fix: show every snippet on sites with more than a hundred - #560

Open
TallblokeUK wants to merge 7 commits into
core-betafrom
fix/snippets-list-truncation/core
Open

TallblokeUK wants to merge 7 commits into
core-betafrom
fix/snippets-list-truncation/core

Conversation

@TallblokeUK

Copy link
Copy Markdown
Contributor

Fixes the snippets list showing only the first 100 snippets on sites with larger libraries.

Reported at https://wordpress.org/support/topic/hiding-some-snipptes/

The problem

The manage screen embeds a list capped at min( $snippets_per_page, 100 ) snippets so the table paints immediately, then replaces it with the complete list from the REST API. That request carried every snippet's code, which is by far the largest thing the screen transfers and grows without bound with the library.

When the request does not land, the screen keeps the capped list, reports its length as the total, and gives no indication that anything is wrong. The type counts are derived from whichever list is loaded, so they agree with the truncation and look plausible. The admin bar counts come from a separate server-side query, which is how the reporter noticed the discrepancy.

This is why the report is hard to reproduce: it does not need more than 100 snippets, it needs the list request to fail. On a healthy site the screen corrects itself within a moment.

Changes

  • The list is requested without snippet code, via _fields. On a 135-snippet library the response drops from 294 KB to 45 KB. No server change is required.
  • The type counts use the values localised with the page until the complete list has actually arrived. The previous check treated the embedded list as though it were loaded, so the authoritative counts were never used.
  • A failed list request shows a notice rather than only reaching the console.
  • Searching snippet contents still needs the code, so it is fetched once, on the first search that could match it.
  • Cloning and previewing from the list resolve the snippet's body first, since the list no longer carries it. The preview editor now follows its code prop, having previously read the value only when it was created.

Verification

  • PHPUnit: 335 tests green. Includes a new test asserting the requested field list still matches the snippets schema minus code, so a property added to the schema cannot quietly stop reaching the browser. Confirmed it fails both when a field is dropped from the list and when code is added back.
  • Playwright: 68 tests green across the default and file-based projects, including five new scenarios. Confirmed the counts scenario fails against the previous logic.
  • Measured on a 144-snippet site: 45 KB list request; counts correct while the request is in flight; counts correct and accompanied by a notice when it fails.
  • ESLint, PHPCS and Stylelint clean.

Note

Why the reporter's request fails has not been established; their site is on shared LiteSpeed. A much smaller response makes it considerably more likely to succeed, but if the cause is the response being cached or filtered rather than its size, this will not resolve their site on its own. It will at least show them an error instead of a silently partial list.

The manage screen embeds a list capped at a hundred snippets so the table
paints immediately, then replaces it with the complete list from the REST
API. That request carried every snippet's code, which grows without bound
with the library and is by far the largest thing the screen transfers, so
it can fail on a constrained host. When it did, the screen kept the capped
list, reported its length as the total, and said nothing.

The list is now requested without code, taking a 135-snippet library from
294 KB to 45 KB. The type counts keep using the authoritative values sent
with the page until the complete list has arrived, rather than counting
whichever list happens to be present. A failed request now shows a notice
instead of only reaching the console.

Searching snippet contents still needs the code, so it is fetched once, on
the first search that could match it.
The list no longer carries snippet code, so cloning from it produced an
empty snippet and the preview opened on an empty editor. Both now resolve
the snippet's body first.

The preview editor also follows its code prop. It read the value only when
it was created, so a body arriving afterwards was ignored.
Checks that the whole library is shown rather than the embedded subset,
that the list request never asks for snippet code, that a snippet
previewed from the list still shows its code, and that a failed list
request leaves the counts telling the truth and says so on screen.

Also checks the requested field list still matches the snippets schema, so
a property added to the schema cannot quietly stop reaching the browser.
@TallblokeUK TallblokeUK added bug Something isn't working run-tests Trigger automated tests labels Sep 16, 2026
Resolved against the incoming pro-beta sync:

- SnippetPreviewModal keeps both the new extraActions prop and the code
  resolution this branch adds, since a snippet opened from the list no
  longer arrives with its body.
- SnippetsTable keeps this branch's comment explaining why the counts wait
  for the list to load before switching away from the localized values.
- readme.txt follows core-beta in dropping the 4.0.0 section; the entry for
  this fix lives in CHANGELOG.md alongside the rest.

The sync also added created_by and updated_by to the snippets schema, so the
list now requests them. The schema test added on this branch is what caught
their absence.
@lightbulbman

Copy link
Copy Markdown
Contributor

Reviewed for 4.1. Holding this back for now: with the list fetched without code, a few paths still write or export the list's copy of a snippet, and that loses the code.

Blocking

Changing the priority from the table erases the snippet's code. Reproduced on wp-env with this branch merged into current core-beta: a snippet with code // ZZ Probe priority, priority changed to 7 in the table, and the stored code afterwards is "". SnippetPriorityInput calls snippetsAPI.update({ ...snippet, priority }) with the list snippet, mapToSchema posts code: '', and prepare_item_for_database saves it because the parameter is present.

The same pattern, found by reading rather than reproduced:

  • Bulk clone: useApplyBulkAction still calls api.create(cloneSnippetObject(snippet)) on list snippets, so each clone is created with empty code. Only the row, card and modal clone actions go through ensureCode.
  • Bulk export: downloadBulkSnippetExportFile builds the JSON from the list snippets, so every exported snippet has "code": "". As a backup, that file would restore empty snippets.

Suggested fix: have mapToSchema leave code out unless it was loaded, so every caller of update or create is covered, not just the ones that remember ensureCode. Then resolve the code before bulk clone and bulk export. A test that edits the priority from the table and checks the code afterwards would guard the first case.

Worth fixing while in here

  • useSnippetsList: turning on withCode while the no-code request is in flight starts a second request, and whichever lands last wins. If the no-code response arrives second, it replaces the list with code and code search finds nothing until the next refresh.
  • SnippetPreviewModal: Copy Code, Export and Clone are enabled before useSnippetWithCode resolves, so an early Copy Code copies an empty string.
  • The "Could not load the complete list of snippets" notice also shows when a later refresh fails after the full list has loaded, where the list is only stale.
  • ensureCode repeats the request and mapping in fetch; snippet.code ? Promise.resolve(snippet) : fetch(snippet.id, snippet.network) would do.

Tests

The Playwright failures on the last CI run came from the core-beta of the time. Merged into current core-beta, code-snippets-evaluation, code-snippets-list and snippets-list-size pass locally in both the default and file-based projects (70 passed), and REST_API_Snippets_List_Fields_Test passes. The branch has a CHANGELOG.md conflict with core-beta.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working run-tests Trigger automated tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants