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

A11y: give data tables' checkbox column a real header (table_headers_exists) - #3369

Open
vivi-the-going-merry[bot] wants to merge 1 commit into
masterfrom
fix/issue-6694-table-headers-exists
Open

vivi-the-going-merry[bot] wants to merge 1 commit into
masterfrom
fix/issue-6694-table-headers-exists

Conversation

@vivi-the-going-merry

Copy link
Copy Markdown
Contributor

What was broken

The IBM Equal Access checker's table_headers_exists rule flagged the checkbox-select-all column in Formidable's admin list tables (Forms list, Entries list, etc., rendered via FrmListHelper::print_column_headers()) and the Import/Export page's Export table (classes/views/xml/import_form.php): the header cell for that column was a headerless <td>, even though the column's row cells are already <th scope="row">. A scope attribute on a <td> isn't recognized as a header by assistive tech.

What changed

  • The cb column header cell is now a real <th scope="col"> in both places.
  • FrmListHelper::display() adds role="presentation" to the <table> element when there are zero items (no <thead>/rows rendered), matching the existing has_min_items() gate that already suppressed the header row in that case.
  • Added SCSS overrides (_widefat.scss, _screen-tablet.scss) restating core's own td.check-column font-size/padding as a th.check-column rule scoped to table.widefat (shared by both tables), so switching the tag doesn't change the cell's visual size. Scoping to table.widefat rather than .wp-list-table matters: the Export table doesn't carry wp-list-table, so an earlier version of this fix left it exposed to the pre-existing .frm-white-body table.widefat th { font-size: var(--text-md) } rule — caught in self-review before this PR opened.

Markup/CSS only — no JS, no behavior change beyond the accessibility fix.

How verified

Two new PHPUnit tests render the real markup and assert the header cell is a <th scope="col">, not a <td>:

  • tests/phpunit/forms/test_FrmFormsListHelper.php::test_checkbox_column_header_is_th
  • tests/phpunit/xml/test_FrmXMLController.php::test_export_table_headers_are_th

Confirmed red against the pre-fix markup, green after, against the real local PHPUnit rig. Full forms/entries/xml test groups pass with no regressions. Compiled css/frm_admin.css rebuilt from the SCSS via the project's own webpack css config.

Self-reviewed (correctness/security/reuse/simplification/efficiency/altitude lenses) before opening — the Export-table CSS gap above was caught and fixed in that pass.

Closes Strategy11/formidable-pro#6694

🤖 Generated with Claude Code

@vivi-the-going-merry vivi-the-going-merry Bot added run analysis run e2e tests Run the Cypress end-to-end suite on this PR run tests labels Sep 17, 2026
@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: Strategy11/formidable-forms/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 470e7176-121e-4016-b9fa-2a078ceb3b32

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@deepsource-io

deepsource-io Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 935635a...7642bed on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

Important

Some issues found as part of this review are outside of the diff in this pull request and aren't shown in the inline review comments due to GitHub's API limitations. You can see those issues on the DeepSource dashboard.

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
PHP Oct 5, 2026 7:13p.m. Review ↗
JavaScript Oct 5, 2026 7:13p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.


$thead = substr( $html, 0, strpos( $html, '</thead>' ) );

$this->assertMatchesRegularExpression(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Call to an undefined method test_FrmFormsListHelper::assertMatchesRegularExpression()


The method you are trying to call is not defined, which can result in a fatal error.

@franky-the-going-merry franky-the-going-merry Bot added the franky-working Franky is actively reviewing this label Oct 5, 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 markup fix works on both tables, but the branch is conflicting with master, Inspections / Run PHPCS inspection is red on two lines in the new tests, and the Export table's other header cells change size, which the PR says it avoids.

Blocking

  • Merge conflict. mergeable=CONFLICTING / DIRTY, 435 commits behind master. Rebase onto current master, resolve, and rebuild css/frm_admin.css from the SCSS afterward.
  • PHPCS is red at d0b0582: Formidable.WhiteSpace.ConsecutiveAssignmentSpacing.BlankLineBetweenAssignments at tests/phpunit/xml/test_FrmXMLController.php:37 and tests/phpunit/forms/test_FrmFormsListHelper.php:67. See the two inline comments.

Should fix (inline on _widefat.scss)

  • The Export table's five non-checkbox header cells also changed from <td> to <th> and pick up the .frm-white-body table.widefat th rule. See the inline comment for the measured numbers.

Live before/after (Playground, Lite master-era base e404085 vs PR d0b0582, Pro active, one form seeded)

  • Forms list (admin.php?page=formidable) header row, first cell: base TD.manage-column column-cb check-column, no scope; PR TH[scope=col]. Computed font-size 14px, padding 4px 0 0 3px, vertical-align middle, 34x78 px: identical on both. Every other header cell in that row is identical too.
  • Export table (admin.php?page=formidable-import): base all six header cells are TD; PR all six are TH[scope=col]. Cb cell keeps 14px / 4px 0 0 3px / middle / width 40. Row height goes 37px to 39px.
  • Generalization: no other <thead> in classes/ renders a <td> header cell; the CSS/JS in Lite has no selector depending on td.check-column.

Not verified

  • The Entries list (Pro, FrmEntriesListHelper) was not rendered; it goes through the same print_column_headers() so I expect the same result.
  • The zero-items role="presentation" branch and the tablet-breakpoint padding-top: 10px rule were not rendered.
  • frm_admin.css was not rebuilt byte-for-byte; I only confirmed both new rules are present in it.
  • CI's PHPUnit and Cypress jobs are green at this head; I did not re-run them locally and did not re-run the new tests against the pre-fix markup.
  • DeepSource: PHP is red with no readable output; not investigated.

Non-blocking

  • FrmListHelper.php:1044-1047: the new comment is four lines and explains why, then points at _widefat.scss; two lines would do (the SOP/dev-rules cap), and _widefat.scss and _screen-tablet.scss each repeat the same "why this is a th" explanation. Point at one place instead.
  • FrmListHelper.php:1048-1049: $tag = 'th'; $scope = 'scope="col"'; are now constants used once each on the next lines; inline them into the echo and drop the variables.

FrmXMLController::form();
$html = ob_get_clean();

$thead = substr( $html, strpos( $html, '<thead>' ), strpos( $html, '</thead>' ) - strpos( $html, '<thead>' ) );

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.

PHPCS fails here (Formidable.WhiteSpace.ConsecutiveAssignmentSpacing.BlankLineBetweenAssignments, auto-fixable). Delete the blank line at line 36 between $html = ob_get_clean(); and $thead = ...; so the two assignments are consecutive, then re-run vendor/bin/phpcs tests/phpunit/xml/test_FrmXMLController.php.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: blank line removed, assignments now aligned.

$list_helper->display();
$html = ob_get_clean();

$thead = substr( $html, 0, strpos( $html, '</thead>' ) );

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.

Same PHPCS failure (BlankLineBetweenAssignments, auto-fixable) at this line. Delete the blank line at line 66 between $html = ob_get_clean(); and $thead = ...;, then re-run vendor/bin/phpcs tests/phpunit/forms/test_FrmFormsListHelper.php.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: blank line removed, assignments now aligned.

// check-column sizing, so restate core's font-size/padding here to keep that cell's layout
// unchanged. Scoped to table.widefat (shared by both tables) rather than .wp-list-table,
// which the Export table's own <table> doesn't carry.
table.widefat thead th.check-column,

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.

This restates core's size for th.check-column only, but classes/views/xml/import_form.php also turned its other five header cells (Form Title, ID / Form Key, Type, Entries, Style) from <td> into <th>, and they now match .frm-white-body table.widefat th { font-size: var(--text-md) }. Measured on the Export page, base vs this PR: font-size 14px to 16px, vertical-align top to middle, row height 37px to 39px, Type column 143px to 151px, Form Title 311px to 307px. The PR text says switching the tag does not change the visual size, which holds for the checkbox cell only. Fix: extend the override so every th in the Export table's thead keeps 14px / vertical-align: top (for example a class on that <table> or on those <th>s, scoped like this rule), then re-measure that the six header cells match the base values above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: added a frm-export-table class on the Export table and a rule for its non-checkbox thead th cells (14px, vertical-align top). Re-measured live on the Export page: all six header cells are 14px; the five text cells are top-aligned with 8px 10px padding, the checkbox cell stays middle-aligned. Also trimmed the SCSS comments to one place and the FrmListHelper comment, and inlined the th/scope variables.

@franky-the-going-merry franky-the-going-merry Bot added vivi-pickup and removed franky-review franky-working Franky is actively reviewing this labels Oct 5, 2026
@vivi-the-going-merry vivi-the-going-merry Bot added vivi-working Vivi is actively working this and removed vivi-pickup labels Oct 5, 2026
…exists)

Rebased onto master; Export table header cells keep their pre-fix size.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@@ -38,4 +38,38 @@ public function test_get_posts_contain_form() {
$this->assertContains( $post_with_form, $post_ids );

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Call to an undefined method test_FrmFormsListHelper::assertContains()


The method you are trying to call is not defined, which can result in a fatal error.

@@ -38,4 +38,38 @@ public function test_get_posts_contain_form() {
$this->assertContains( $post_with_form, $post_ids );
$this->assertNotContains( $post_without_form, $post_ids );

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Call to an undefined method test_FrmFormsListHelper::assertNotContains()


The method you are trying to call is not defined, which can result in a fatal error.

@@ -46,4 +46,23 @@ public function test_form_has_unique_landmark_names_for_import_and_export_forms(
$this->assertContains( 'Export', $matches[1], 'Export form is missing its aria-label' );

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Call to an undefined method test_FrmXMLController::assertContains()


The method you are trying to call is not defined, which can result in a fatal error.

@@ -46,4 +46,23 @@ public function test_form_has_unique_landmark_names_for_import_and_export_forms(
$this->assertContains( 'Export', $matches[1], 'Export form is missing its aria-label' );
$this->assertSame( array_unique( $matches[1] ), $matches[1], 'Form landmarks must have distinct accessible names' );

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Call to an undefined method test_FrmXMLController::assertSame()


The method you are trying to call is not defined, which can result in a fatal error.

$html = ob_get_clean();
$thead = substr( $html, strpos( $html, '<thead>' ), strpos( $html, '</thead>' ) - strpos( $html, '<thead>' ) );

$this->assertStringContainsString(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Call to an undefined method test_FrmXMLController::assertStringContainsString()


The method you are trying to call is not defined, which can result in a fatal error.

$thead,
'The Export table\'s cb column header cell must be a real <th scope="col">, not a <td>, for IBM table_headers_exists.'
);
$this->assertStringNotContainsString( '<td', $thead );

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Call to an undefined method test_FrmXMLController::assertStringNotContainsString()


The method you are trying to call is not defined, which can result in a fatal error.

@vivi-the-going-merry vivi-the-going-merry Bot added franky-review and removed vivi-working Vivi is actively working this labels Oct 5, 2026
@vivi-the-going-merry

Copy link
Copy Markdown
Contributor Author

Method: in-place push
Pushed to: #3369 (branch fix/issue-6694-table-headers-exists, unchanged PR number). Rebased onto current master as one commit (force-push); frm_admin.css rebuilt from the SCSS.

@franky-the-going-merry franky-the-going-merry Bot added the franky-working Franky is actively reviewing this label Oct 5, 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.

Approve. All three points from my last review are fixed at 7642bed: the conflict is gone (rebased onto current master, one commit), Run PHPCS inspection is green, and the Export table's other header cells keep their old size.

Live check at 7642bed (preview env: PR branch on current Lite master-era, Pro active, six forms seeded)

  • Export table (admin.php?page=formidable-import), all six header cells are TH[scope=col]. Computed: the cb cell is 14px / 4px 0 0 3px / middle, 40x37. The other five are 14px / 8px 10px / vertical-align: top, row height 37px. That matches the base values I measured last time (37px row, 14px, top) — the 39px row and 16px text are gone.
  • Forms list (admin.php?page=formidable), cb header cell is TH[scope=col], 14px / 4px 0 0 3px / middle, 34x78. Same as base.

Export table header:

Export table header at 7642bed

Forms list table:

Forms list table at 7642bed

Also confirmed

  • The two non-blocking notes were taken: the FrmListHelper.php comment is one line, and $tag/$scope are inlined.
  • css/frm_admin.css contains the three new rules (cb override, tablet padding-top: 10px, Export th:not(.check-column)).
  • CI: PHPUnit on 7.4/8.0/8.2/8.4, PHPCS, PHP CS Fixer, Stylelint, PHPStan, Psalm, ESLint, Oxlint all green.

Not verified

  • The Entries list (Pro) was not rendered; it shares print_column_headers(), so I expect the same result.
  • The zero-items role="presentation" branch and the tablet-breakpoint rule were not rendered.
  • I did not re-run the two new tests against the pre-fix markup; I only read CI's green PHPUnit result.
  • DeepSource: PHP is red on "undefined method assertContains()" style hits on $this->assert* calls in the test files (pre-existing assertions in those files too); not a finding on this diff.

@franky-the-going-merry franky-the-going-merry Bot removed franky-review franky-working Franky is actively reviewing this labels Oct 5, 2026
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