Skip to content

Cherry-pick hot-patch-6.14.3 fixes into development#1657

Open
jakejackson1 wants to merge 20 commits into
developmentfrom
cherry-pick/hot-patch-6.14.3
Open

Cherry-pick hot-patch-6.14.3 fixes into development#1657
jakejackson1 wants to merge 20 commits into
developmentfrom
cherry-pick/hot-patch-6.14.3

Conversation

@jakejackson1
Copy link
Copy Markdown
Member

Summary

Brings the 20 hot-patch-6.14.3 commits onto development, with conflict resolutions for the regions dev has already refactored. One hot-patch commit (b19ce882 settings/view/SCSS WP 7.0 tweaks) was skipped — its content already landed on development via 03be1d41.

Highlights — conflict resolutions adapted to current dev API

  • templates.js saga: hot-patch routes HTTP failures to templateUploadProcessingFailed; rewired to dev's { message } payload contract so TemplateUploader.ajaxFailed still reads error?.message ?? genericUploadErrorText.
  • coreFonts.js saga: added response.ok guards on both branches (mirrors fontManager.js / templates.js pattern).
  • setupDynamicTemplateFields.js: integrated snapshotFormValues/restoreFormValues while preserving dev's actionToolbar variable + import path. Exported TEMPLATE_SECTION_SELECTOR / TEMPLATE_DROPDOWN_SELECTOR from formValuesSnapshot.js to collapse the 5x-duplicated selector literals.
  • loadTinyMCEEditor.js: merged hot-patch's restoreLastEditorTab helper with dev's earlier switchEditors.go migration.
  • FontManager getTabLocation() extraction: applied to AddUpdateFontFooter.js, FontListItems.js, FontManager.js.
  • \GFForms::$version migration: standardised on unprefixed GFForms::$version everywhere use GFForms; is in scope.

Skipped

  • b19ce882 fix: settings, view, and SCSS tweaks for WP 7.0 / PHP 8.5 — content already on development (commit 03be1d41 covers it; the cherry-pick produced an empty diff).

Test plan

  • yarn lint:js clean
  • yarn test:js -- tests/js-unit/react/sagas/coreFonts.test.js — new "non-2xx" test runs inside the getFilesFromGitHub() describe
  • yarn test:js -- tests/js-unit/react/sagas/templates.test.js — three saga branches (non-ok, missing templates array, fetch throws) all dispatch { message } payloads
  • yarn test:js -- tests/js-unit/admin/settings/common/dynamicTemplateFields/formValuesSnapshot.test.js — full round-trip + every standard form-field type
  • yarn test:js -- tests/js-unit/admin/settings/common/dynamicTemplateFields/loadTinyMCEEditor.test.js — new restoreLastEditorTab deferral path
  • composer lint clean
  • yarn test:phpHelper_Misc, Pre_Checks, Gravity_Forms, Templates suites green
  • Manual: change a PDF template on a form-level settings page with unsaved edits → values reappear in the new template's matching fields
  • Manual: upload an invalid .zip → modal stays open, inline error appears (no TypeError)
  • Manual: trigger a 4xx from /wp-admin/admin-ajax.php core-fonts route → .gfpdf-core-font-status-error appears
  • Manual: open Font Manager from the Tools tab → no "select default font" tick appears in the footer

🤖 Generated with Claude Code

jakejackson1 and others added 20 commits May 19, 2026 14:57
Hide the select-default-font tick in the AddUpdateFontFooter when the
Font Manager is opened from the global Tools tab, matching the existing
handleDisableSelectFields() pattern in FontListItems.js — the tick is
meaningless without a parent font dropdown to write the selection back
into. Also pin font-size: 20px / line-height: 1 on the dashicons trash
and tick buttons inside .select-delete-icons-container so they render
at ~35px and match the adjacent .gfpdf-button on WordPress 7.0, where
the inherited <button> line-height (~33px) was ballooning them to 48px.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Revert the search result list selector from ol back to ul (the React
component renders <ul class="search-menu">, so the ol rule was an
orphan and stripped row backgrounds, shadows and the flex layout) and
re-add the .hit-action rules that were removed in e35add7. Use
visibility: hidden / visible instead of display: none / flex on
.hit-action so the select-icon slot is always reserved — hovering a
result no longer pushes the title and path onto a new line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add .button to the #gfpdf-template-container and #gfpdf-font-manager-container
override so the activate <a class="button"> picks up the same
min-height: 32px / line-height: 2.30769231 reset as the <button>
elements (without it the broad #tab_PDF .button rule was applying
line-height: 2.95, which rendered as 42.48px in a 36px button).

Set box-sizing: border-box on .theme .more-details so the WP-core
20% / 60% / 20% positioning includes the 24px horizontal padding —
otherwise the hover label sat ~12px right of centre.
…load

apiPostTemplateUploadProcessing never throws on HTTP failures, so the
saga always dispatched the success action — even when the server
returned 400 or a JSON error body. ajaxSuccess then ran
response.body.templates.forEach() on a non-array (e.g. the integer 400
parsed from the wp_die response), throwing a TypeError that unmounted
the modal. ajaxFailed never ran, and even if it had it was reading
error.response.body.error — a shape that no caller produced.

Route the saga to templateUploadProcessingFailed when response.ok is
false or response.body.templates is missing, and rewrite ajaxFailed to
read the new response shape (falling back to genericUploadErrorText
when the body is not a JSON error payload). Tests updated to cover the
three failure paths: non-ok status, missing templates array, and a
thrown fetch.
glob() intermittently returned an empty result immediately after
unzip_file() wrote the new entries through WP_Filesystem, even when
scandir() on the same directory showed the .php files were present.
This surfaced for users as "No valid PDF template found in Zip archive."
on otherwise-valid template uploads, and as a ~6% flake rate on
Test_Templates::test_unzip_and_verify_templates in the suite.

Switch to scandir() + suffix filter for the post-unzip listing so the
directory is always re-read from disk, and harden the test itself with
a unique archive name per run, a flush of the template transient cache
before/after the test, and a try/finally to guarantee cleanup runs even
when an assertion mid-test fails.

Verified 40/40 consecutive full-suite passes after the change.
Replace the scandir + array_filter dance with a CallbackFilterIterator
over a FilesystemIterator, extracted into a small find_template_files()
helper, and reuse it from both the validation path and the post-copy
header lookup so they share one source of truth.

Kept non-recursive to mirror Helper_Templates::get_all_templates_in_folder():
the rest of the template system only discovers top-level .php files in
the install folder, so picking up templates from subdirectories at
validation time would let zips pass that the system can't actually
load post-install.
…im comments

Replace the new Model_Templates::find_template_files() with a direct
call to the existing Helper_Templates::get_all_templates_in_folder(),
which already wraps the same FilesystemIterator + CallbackFilterIterator
pipeline behind a logged try/catch — removes ~30 lines and the
CallbackFilterIterator / FilesystemIterator imports.

Extract the brittle window.location.search.substr(lastIndexOf('=')+1)
pattern used in four Font Manager components into a single
getTabLocation() utility. Preserve the trailing-`=`-value semantics:
the call sites depend on the value of the *final* query parameter
(sometimes 'tab', sometimes 'subview', sometimes the form id), so
URLSearchParams.get('tab') is not a drop-in replacement — it returns
'' on the global PDF settings page (?page=gf_settings&subview=PDF) and
breaks FontListItems.handleSetSelectedFontNameValue.

Drop the redundant pre-test cleanup in test_unzip_and_verify_templates
(the try/finally block already guarantees teardown), and prune
explanatory comments that just restated what the code below them did.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Changing the Template dropdown on a form-level PDF settings page wipes
the user's in-progress edits because the entire template-fields section
is replaced via AJAX with server-rendered markup based on saved values.

Snapshot every input inside the template fieldset before the request
fires, then re-apply matching values to the new HTML before TinyMCE and
wpColorPicker are re-initialised on top of it. Snapshot entries that no
longer match a field in the new template are silently dropped — the new
template doesn't surface them. Restored checkbox/radio toggles dispatch
`change` so dependent conditional panels driven by setupToggledFields
settle into the right state.

The snapshot/restore pair lives in its own formValuesSnapshot module so
it can be unit-tested against a real jQuery + JSDOM environment. Two
non-obvious cases worth flagging:

1. `.gfpdf-input-toggle` checkboxes (the First Page Header / First Page
   Footer toggles) have no `name` attribute, so a named-input scan
   silently drops them. They're snapshotted keyed by the name of the
   textarea inside the container they show/hide — a stable identity
   across HTML swaps — and restored via the same lookup, firing
   `change` so setupToggledFields slides the container correctly.

2. When a TinyMCE editor is in Code/Text mode the editor body has stale
   content and the textarea holds the live edits. The snapshot checks
   `editor.hidden` and reads the textarea in Code mode.

Also fixes a real bug surfaced by the test suite: jQuery's `:button`
pseudo doesn't match input[type=submit] or input[type=reset], so the
guard spells out `:button, :submit, :reset, [type=file]`.

20 Jest tests cover text/number/select/textarea/radio/checkbox, TinyMCE
Visual + Code modes, nameless toggle round-trip, template dropdown
exclusion, button/submit/reset/file skipping, and a full snapshot →
swap → restore integration case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The superagent → fetch migration in 890c053 swapped a client that
auto-rejected on 4xx/5xx for native fetch, which only sets
response.ok = false. The coreFonts saga still only guarded against a
falsy parsed body — and getJsonString('{}') returns a truthy empty
object, so a 400 from /wp-admin/admin-ajax.php was happily treated as
"download succeeded".

The E2E "Tools tab - Install core fonts field test failure" expected
the .gfpdf-core-font-status-error element to render after the mock
returned 400; it never appeared because the saga walked the success
branch.

Add response.ok to the guards on both saga branches (mirrors the
existing pattern in sagas/fontManager.js and sagas/templates.js) and
extend the Jest suite to lock in the non-2xx-is-a-failure behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… round-trip

Add a success-path case for the templateUploadProcessing saga (only the
two failure branches and the thrown-error case were exercised before),
and a round-trip test that snapshots and restores every standard HTML
form input type — including multi-select, multi-checkbox groups, and
hidden/range/date/time/colour inputs — to lock in formValuesSnapshot's
coverage of the field surface it has to handle on a template swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rules

The form-settings UI exposes two related fields — the `conditional` toggle
(user-visible on/off switch) and the `conditionalLogic` rules array — and
they can drift out of sync. When that happens (toggle off, rules array
still populated from a previous enable), the runtime checks evaluated the
stale rules and rejected entries against rules the UI said were disabled,
manifesting as a "[gravitypdf] shortcode says conditional logic enabled,
entry didn't pass" error on form confirmations.

Route all four PDF-level runtime sites — get_pdf_config, middle_conditional,
get_active_pdfs, and the [gravitypdf] merge-tag check — through a new
Helper_Misc::conditional_logic_passes() that treats the toggle as the
source of truth and short-circuits when it's explicitly off. Legacy
settings without the toggle key fall back to evaluating the rules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…plate swap

Switching the PDF template dropdown with the user's last-selected editor
tab set to 'html' was buggy in two ways:

1. switchEditors.go('html') was called immediately after tinyMCE.init()
   while the iframe was still being mounted, throwing
   "Cannot read properties of undefined (reading 'getSelection')" inside
   editor.hide(). The exception unwound the surrounding ajaxCall()
   success callback, skipping initialiseCommonElements, merge tags, and
   tooltip re-init for the swapped-in fields.

2. switchEditors.go reads the iframe selection and, if a range is set,
   schedules textArea.focus() 100ms later (base.js
   selectTextInTextArea), which scrolls the page to the editor. TinyMCE
   seeds a default cursor on init, so this fires for every editor on
   the form — the viewport ends up on the last one, yanking the user
   away from the template dropdown they just clicked.

Defer the switchEditors.go call via editor.on('init', ...) when the
editor isn't initialised yet, then clear the iframe selection inside
the deferred apply() so findBookmarkedPosition short-circuits and the
focus/scroll path is skipped. Wrap the call in a try/catch as a final
safety net: if WP can't switch, the editor falls back to Visual mode
silently and the user can flip the tab manually.

Drive-bys from the same area:
- hoist getUserSetting('editor') out of the editors.forEach loop and
  drop narrative comments inside loadTinyMCEEditor
- replace stringly-typed class_exists() with
  GF_Background_Process::class in bootstrap.php
- reorder/dedupe the 6.14.3 changelog entries

13 Jest tests cover the apply() behaviour — selection-clear, deferred
init via editor.on('init'), missing-getWin/getSelection guards,
switchEditors throwing, switchEditors absent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

❌ Patch coverage is 95.45455% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.99%. Comparing base (46f8500) to head (21abb8d).
⚠️ Report is 1 commits behind head on development.

Files with missing lines Patch % Lines
src/assets/js/react/sagas/templates.js 90.90% 1 Missing ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##           development    #1657      +/-   ##
===============================================
+ Coverage        92.85%   92.99%   +0.14%     
===============================================
  Files               58       59       +1     
  Lines             1511     1528      +17     
  Branches           419      430      +11     
===============================================
+ Hits              1403     1421      +18     
+ Misses             103      102       -1     
  Partials             5        5              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant