Sitelet https://github.com/Strategy11/formidable-forms/pull/3511
Skip to content

Defer loading codelist icons - #3511

Merged
Crabcyborg merged 3 commits into
masterfrom
defer_loading_codelist_icons
Sep 29, 2026
Merged

Crabcyborg merged 3 commits into
masterfrom
defer_loading_codelist_icons

Conversation

@Crabcyborg

@Crabcyborg Crabcyborg commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Improvements
    • Field icons now appear when the shortcode panel opens, keeping hidden field lists free of rendered icons until they’re needed.

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Next included review available in 47 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: Strategy11/formidable-forms/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 7abff24c-a6f0-4e42-9abb-204bacf7c3bc

📥 Commits

Reviewing files that changed from the base of the PR and between 81d49d4 and f4c1d77.

📒 Files selected for processing (4)
  • classes/helpers/FrmFormsHelper.php
  • classes/views/shared/mb_adv_info.php
  • js/formidable_admin.js
  • js/src/admin/admin.js

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: Strategy11/formidable-forms/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: a7cb5693-50ef-4e29-b95b-6ff14129cfe4

📥 Commits

Reviewing files that changed from the base of the PR and between eef1775 and 81d49d4.

📒 Files selected for processing (7)
  • classes/controllers/FrmFormsController.php
  • classes/helpers/FrmFormsHelper.php
  • classes/views/frm-forms/mb_insert_fields.php
  • classes/views/shared/mb_adv_info.php
  • js/formidable_admin.js
  • js/src/admin/admin.js
  • tests/cypress/e2e/Forms/codeListIconsDeferredInit.cy.js

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Field-code rendering can defer icon markup in data-frm-icon. When the shortcode panel opens, admin JavaScript inserts the markup into field links. A Cypress test checks the deferred and hydrated states.

Changes

Deferred code-list icons

Layer / File(s) Summary
Generate deferred icon markup
classes/controllers/FrmFormsController.php, classes/helpers/FrmFormsHelper.php, classes/views/frm-forms/mb_insert_fields.php, classes/views/shared/mb_adv_info.php
The controller and views pass the defer_icon option during field-code generation. The helper stores icons in data-frm-icon and omits inline icons when deferral is enabled.
Hydrate icons when the panel opens
js/src/admin/admin.js, tests/cypress/e2e/Forms/codeListIconsDeferredInit.cy.js
The admin script inserts deferred icons into field links when the shortcode panel opens, then removes the data attribute. The Cypress test checks the markup before and after opening a popup.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant FrmFormsHelper
  participant showShortcodeBox
  participant hydrateDeferredCodeListIcons
  FrmFormsHelper->>showShortcodeBox: Provides code-list items with data-frm-icon
  showShortcodeBox->>hydrateDeferredCodeListIcons: Hydrates icons before displaying the panel
  hydrateDeferredCodeListIcons->>showShortcodeBox: Inserts icon HTML and removes data-frm-icon
Loading

Suggested reviewers: truongwp

Merge Risk: ⚪ Minimal · up to 81d49

The change defers field icon rendering until the shortcode panel opens. No merge-blocking risk is evident from the supplied review.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 81d49

The new icon-loading path is limited to the form editor in the inspected code, and its generated markup uses escaped values. No exploitable path was established, but the new browser insertion step and incomplete production-script coverage prevent a minimal-risk assessment.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected enabled path affects the form editor's shortcode list and its admin viewer; the default-off parameter does not itself enable deferral for every mb_tags_box caller.

Security Findings and Attack Paths

  • inferred — Plugin-influenced field icon values can reach the new HTML insertion path, but the inspected producer does not preserve those values as arbitrary HTML: it places escaped values in fixed icon templates. An attacker-controlled injection path was not established.

Trust Boundaries and Controls

  • observed — Attribute escaping protects the server-to-HTML transport, while fixed icon-tag construction and value escaping constrain the markup later parsed by the browser. The hydration selector itself does not verify that every matching attribute came from that producer.

Hardening Proposals

  • proposed — If add-ons can supply matching list items, constrain hydration to trusted generated icon data rather than treating any data-frm-icon value in the list as HTML. This is a precaution, not an established exploit.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: deferred loading of codelist icons.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 6 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Crabcyborg Crabcyborg added the full automated qa Run analysis, PHPUnit, and Cypress E2E workflows label Sep 28, 2026
@Crabcyborg Crabcyborg added this to the 6.36 milestone Sep 28, 2026

@franky-the-going-merry franky-the-going-merry Bot 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.

Request Changes. The popup itself works. There are two blocking items: PHPCS is red on a line this PR adds, and the default tags box now loses its icons in the Views editor.

Blocking

  1. Run PHPCS inspection fails on a line this PR adds, classes/views/shared/mb_adv_info.php:80 (multi-item array with explicit keys on one line). One-click fix is inline.
  2. defer_icon => true is hard-coded in the shared default template, but that template is also rendered visible, not in the popup. mb_tags_box() with the default template is what Views' frm_get_cd_tags_box AJAX returns after you pick a form. Nothing hydrates the icons there, so the Fields list loses them. Details, screenshot and a fix are inline on :76.

Non-blocking

  • The clickTab hydrate call (admin.js:1059) finds nothing to hydrate. Inline.
  • hydrateDeferredCodeListIcons() copies icon_by_class()'s marker parsing and deprecated-icon map into JS. Inline.
  • No test was added. A small Cypress check (open the popup, expect an svg in each .frm_customize_field_list li, no [data-frm-icon] left) would pin the behaviour. Blocking item 2 lives in Pro/Views, so this wouldn't have caught it.

What I checked, and what I didn't

  • Ran Lite at the PR head in Playground (WP 7.0, SQLite). Form settings > Customize HTML, opening the popup from a .frm-show-box icon: before opening, 5 li[data-frm-icon] and 0 svg. After opening, 0 deferred and 10 svg (5 items x the id and key links), each <svg class="frmsvg" aria-hidden="true"><use href="#frm_text2_icon">, the same shape icon_by_class() prints. Clicking an item still inserts its code ([3]).
  • Confirmed the committed js/formidable_admin.js contains the new code. The bundle is minified, so I checked for the code, not a byte diff.
  • Ran Pro and Views (local master copies of formidable-pro and formidable-views) for blocking item 2. Baseline: opening the Views editor with &form=1 renders the list with icons (0 deferred, 10 svg). After picking a form in the editor, the list has 5 deferred and 0 svg. Switching to Advanced and back to Fields doesn't change that.
  • Cypress shard 1 is red on FormTemplates.cy.js (cy.select() matched more than one Form Template Test option, and an svg visibility timeout). That's not this area. The same spec failed on other PRs today.
  • Not exercised: the form builder page's copy of the popup (same showShortcodeBox() path, read from source), the Keys view visually, Lite's legacy Views editor (js/admin/legacy-views.js:244 makes the same AJAX call, so it regresses the same way by reading), and any add-on's frm_field_code_tab handler.
  • I didn't run the code-review/security-review/simplify skills on this one. I read the full diff by hand, since the change is small and has no user-input or data-handling surface (the only new output is data-frm-icon, which goes through esc_attr()).

Comment thread classes/views/shared/mb_adv_info.php Outdated
);

do_action( 'frm_field_code_tab', array( 'field' => $f ) );
do_action( 'frm_field_code_tab', array( 'field' => $f, 'defer_icon' => true ) );

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.

Blocking: PHPCS fails here (Run PHPCS inspection: "When a multi-item array is declared with explicit keys, each item should start on a new line"). This line is new in this PR.

Suggested change
do_action( 'frm_field_code_tab', array( 'field' => $f, 'defer_icon' => true ) );
do_action(
'frm_field_code_tab',
array(
'field' => $f,
'defer_icon' => true,
)
);

Comment thread classes/views/shared/mb_adv_info.php Outdated
'name' => $f->name,
'type' => $f->type,
'class' => 'frm-customize-list dropdown-item',
'defer_icon' => true,

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.

Blocking: icons go missing in the Views editor.

'defer_icon' => true here (and on line 80) applies to every render of the default template, but only the form-settings and builder popup (mb_insert_fields.php) is hidden until showShortcodeBox() runs the hydration. Formidable Views renders its own template on first load (editor/mb_adv_info.php, no deferral), then refreshes the sidebar with frm_get_cd_tags_box, which calls FrmFormsController::mb_tags_box( $form_id, 'frm_doing_ajax' ) with the default template. That HTML is put straight into the visible customization sidebar (formidable-views/js/editor.js:4760; Lite's js/admin/legacy-views.js:244 does the same). Nothing there opens the popup, so nothing hydrates.

Reproduced with Lite at this PR's head plus Pro and Views on master. After picking the form, the Fields list is visible and has 5 li[data-frm-icon] and 0 svg. Reloading the editor with the form already chosen, where Views' own template renders, has 10 svg and no deferred items. Clicking Advanced and back to Fields doesn't change that.

Views editor sidebar after picking a form: Fields list with no icons

Fix: make deferral opt-in for the popup only, so any other caller of the default template keeps server-rendered icons.

// FrmFormsController::mb_tags_box()
public static function mb_tags_box( $form_id, $class = '', $template_path = 'default', $defer_icon = false ) {

// mb_adv_info.php, both places (this line and the do_action on :80)
'defer_icon' => $defer_icon,

// classes/views/frm-forms/mb_insert_fields.php:10, the hidden popup
FrmFormsController::mb_tags_box( $id, '', 'default', true );

The new parameter is optional, so existing callers (Pro, Views, third parties) are unchanged.

Comment thread js/src/admin/admin.js Outdated
const targetEl = document.getElementById( targetId );
if ( targetEl ) {
if ( targetId === 'frm-adv-info-tab' ) {
hydrateDeferredCodeListIcons( targetEl );

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.

Non-blocking. frm-adv-info-tab is the Advanced panel. The Fields list, the only .frm_customize_field_list in mb_adv_info.php, sits in #frm-insert-fields-box, so this call finds no [data-frm-icon] and does nothing. Opening the popup already hydrates the whole box at showShortcodeBox(). If it was meant to cover a visible copy of the list, it is aimed at the wrong panel, and it still wouldn't run for the Fields tab that starts active. Drop it, or fix the target once blocking item 2 is settled.

Comment thread js/src/admin/admin.js
* @param {HTMLElement} container The opened shortcode panel.
* @return {void}
*/
function hydrateDeferredCodeListIcons( container ) {

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.

Non-blocking. This repeats FrmAppHelper::icon_by_class()'s work: the frmfont/frm_icon_font marker split and the frm_clone_solid_icon/frm_keyalt_* rename map, plus a font-icon <i> branch for icons PHP already marks deprecated. The two copies will drift. It also means the PHP _deprecated_argument() notice no longer fires for an add-on's old icon when it is deferred. Having PHP resolve the icon name and extra classes once and put them in the data attribute (for example data-frm-icon="frm_text2_icon", plus data-frm-icon-class if needed) would leave JS to just build the <svg>.

@franky-the-going-merry franky-the-going-merry Bot 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.

Approve. Both blocking items from my last review are fixed, and I found nothing new that blocks.

Fixed since last review

  • Run PHPCS inspection is green. The do_action call is now multi-line.
  • Deferral is opt-in. mb_tags_box() takes $defer_icon = false and only mb_insert_fields.php passes true, so Views' frm_get_cd_tags_box refresh keeps server-rendered icons.
  • The stray clickTab hydrate call is gone. Hydration now copies one icon per field type from a <template> instead of re-implementing icon_by_class() in JS.
  • A Cypress spec was added (codeListIconsDeferredInit.cy.js).

Non-blocking, optional

  • The opt-in default is the thing that broke last time, and nothing in Lite pins it. A small PHPUnit test would: mb_tags_box( $form_id ) output has no data-frm-icon and does contain svg; with $defer_icon = true it has data-frm-icon and a template.frm-code-list-icons.

What I checked, and what I didn't

  • Ran Lite at f4c1d77 in Playground (WP 7.0, SQLite), with Pro and Views on their local master copies. Form settings > Actions > Confirmation > Redirect to URL, opening the popup from its .frm-show-box icon:
    • Before opening: 5 li[data-frm-icon], 0 svg. The template holds 3 icons (text, email, textarea) for 5 items.
    • After opening: 0 deferred, 10 svg (5 items x the id and key links). Each link got the icon for its own type (#frm_text2_icon, #frm_email2_icon, #frm_paragraph_icon).
    • Opening and closing the popup three times leaves the count at 10, so nothing is duplicated.
    • Clicking the Email item inserted [3 sanitize_url=1] into the redirect field. The Keys view also has an icon on each of its 5 links.
  • Views editor (the earlier regression): after picking a form in the editor, the Fields list has 5 items, 0 deferred and 10 svg, so the AJAX refresh keeps its icons. I read this from the DOM. The list sits in a hidden container in that editor, so I have no screenshot of it.
  • Rebuilt js/formidable_admin.js from the PR's js/src/admin/admin.js (npm run build, sibling checkout's node_modules). After normalizing webpack module ids, the rebuild and the committed bundle differ only in those ids, so the committed bundle matches the source at this head.
  • CI: everything is green except Cypress (shard 1). That is FormTemplates.cy.js again (an svg visibility timeout on the Form Templates page), the same spec that failed on other PRs today. The new spec ran in shard 3, which passed.
  • Popup at the PR head, Fields tab: Shortcode popup with icons after opening
  • Not exercised: the form builder page's copy of the popup (same showShortcodeBox() path, read from source only), Lite's legacy Views editor (js/admin/legacy-views.js, same AJAX call, read from source), and any add-on's frm_field_code_tab handler. Pro's field_sidebar handler receives the new defer_icon key but its items keep inline icons, which is harmless.
  • I read the full diff by hand and didn't run the code-review/security-review/simplify skills. The only new output is data-frm-icon/data-frm-icon-key, escaped with esc_attr(), and the injected icon markup comes from server-rendered icon_by_class() output, not user input.

@Crabcyborg
Crabcyborg merged commit f21ac3d into master Sep 29, 2026
23 of 24 checks passed
@Crabcyborg
Crabcyborg deleted the defer_loading_codelist_icons branch September 29, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full automated qa Run analysis, PHPUnit, and Cypress E2E workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant