From 4297d0273acddc17f86144773a95a22a93d1b0cf Mon Sep 17 00:00:00 2001 From: Developer Ravi Date: Wed, 11 Mar 2026 21:00:48 +0545 Subject: [PATCH 1/4] Fix user profile SCF field table layout on edit screens Scope layout CSS to profile/user-edit screens using wp_add_inline_style. Limit width rules to text-like inputs, textarea, and select2 to avoid side effects. Add PHPUnit coverage for generated user form layout CSS. Fixes #349 --- includes/forms/form-user.php | 67 ++++++++++++++++++++- tests/php/includes/forms/test-form-user.php | 25 ++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/includes/forms/form-user.php b/includes/forms/form-user.php index 14531b58..5de13514 100644 --- a/includes/forms/form-user.php +++ b/includes/forms/form-user.php @@ -63,6 +63,71 @@ function admin_enqueue_scripts() { // enqueue acf_enqueue_scripts(); + + // Fix SCF field layout on user profile/edit screens. + if ( ! acf_is_screen( array( 'profile', 'user-edit', 'profile-network', 'user-edit-network' ) ) ) { + return; + } + + wp_add_inline_style( 'acf-input', $this->get_user_form_layout_css() ); + } + + /** + * Returns custom CSS to improve SCF field layout on user profile/edit screens. + * + * @since ACF 6.8.2 + * + * @return string + */ + function get_user_form_layout_css() { + return <<<'CSS' +table.form-table tr.acf-field > td.acf-label { + width: 200px; + vertical-align: top; + padding-top: 14px; +} + +table.form-table tr.acf-field > td.acf-input { + vertical-align: top; +} + +table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="text"], +table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="password"], +table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="email"], +table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="number"], +table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="url"], +table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="search"], +table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="tel"], +table.form-table tr.acf-field > td.acf-input textarea { + width: 100%; + max-width: 25em; +} + +table.form-table tr.acf-field > td.acf-input .select2-container { + width: 100% !important; + max-width: 25em; +} + +@media screen and (max-width: 782px) { + table.form-table tr.acf-field > td.acf-label, + table.form-table tr.acf-field > td.acf-input { + display: block; + width: auto; + } + + table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="text"], + table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="password"], + table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="email"], + table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="number"], + table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="url"], + table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="search"], + table.form-table tr.acf-field > td.acf-input .acf-input-wrap input[type="tel"], + table.form-table tr.acf-field > td.acf-input textarea, + table.form-table tr.acf-field > td.acf-input .select2-container { + max-width: 100%; + } +} +CSS; } @@ -358,4 +423,4 @@ function filter_pre_load_value( $null, $post_id, $field ) { acf_new_instance( 'ACF_Form_User' ); endif; // class_exists check -?> \ No newline at end of file +?> diff --git a/tests/php/includes/forms/test-form-user.php b/tests/php/includes/forms/test-form-user.php index 6ce3d2a3..aec0b1dc 100644 --- a/tests/php/includes/forms/test-form-user.php +++ b/tests/php/includes/forms/test-form-user.php @@ -358,6 +358,31 @@ function () use ( &$enqueue_called ) { $this->assertFalse( $enqueue_called, 'acf_enqueue_scripts should not be called when not on user screen' ); } + /** + * Test user form layout CSS includes expected rules. + */ + public function test_get_user_form_layout_css_contains_expected_rules() { + $form_user = new ACF_Form_User(); + $css = $form_user->get_user_form_layout_css(); + + $this->assertIsString( $css, 'CSS should be a string' ); + $this->assertStringContainsString( + 'table.form-table tr.acf-field > td.acf-label', + $css, + 'CSS should target ACF labels in user form table rows' + ); + $this->assertStringContainsString( + 'input[type="text"]', + $css, + 'CSS should target text-like input fields' + ); + $this->assertStringContainsString( + '@media screen and (max-width: 782px)', + $css, + 'CSS should include responsive rules for mobile viewports' + ); + } + /** * Test render_edit renders nothing without field groups. */ From 88ba27b6ad0da41ab60d9e16c8c2a48ec8a9f7eb Mon Sep 17 00:00:00 2001 From: Developer Ravi Date: Wed, 11 Mar 2026 21:15:52 +0545 Subject: [PATCH 2/4] Declare visibility for user form layout CSS helper --- includes/forms/form-user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/forms/form-user.php b/includes/forms/form-user.php index 5de13514..f23e7d38 100644 --- a/includes/forms/form-user.php +++ b/includes/forms/form-user.php @@ -79,7 +79,7 @@ function admin_enqueue_scripts() { * * @return string */ - function get_user_form_layout_css() { + public function get_user_form_layout_css() { return <<<'CSS' table.form-table tr.acf-field > td.acf-label { width: 200px; From 03231b8f53ded976d8f4119ceea78122ab45c528 Mon Sep 17 00:00:00 2001 From: Developer Ravi Date: Wed, 11 Mar 2026 21:41:20 +0545 Subject: [PATCH 3/4] ci: rerun flaky e2e jobs From 25c300d01c9e917ca4aef229b7816d899b48423b Mon Sep 17 00:00:00 2001 From: Developer Ravi Date: Wed, 11 Mar 2026 22:29:49 +0545 Subject: [PATCH 4/4] Stabilize media-modal waits in image e2e tests --- tests/e2e/field-helpers.js | 27 +++++++++++---- tests/e2e/field-type-image.spec.ts | 53 ++---------------------------- 2 files changed, 24 insertions(+), 56 deletions(-) diff --git a/tests/e2e/field-helpers.js b/tests/e2e/field-helpers.js index 1aaa7d7d..abeaf26d 100644 --- a/tests/e2e/field-helpers.js +++ b/tests/e2e/field-helpers.js @@ -141,7 +141,10 @@ async function waitForMetaBoxes( page ) { */ async function uploadImageViaModal( page, imagePath ) { // Wait for media modal to open - await page.waitForSelector( '.media-modal', { state: 'visible' } ); + await page.waitForSelector( '.media-modal', { + state: 'visible', + timeout: 20000, + } ); // Click "Upload files" tab if not already there const uploadTab = page.locator( '.media-modal #menu-item-upload' ); @@ -153,11 +156,23 @@ async function uploadImageViaModal( page, imagePath ) { const fileInput = page.locator( '.media-modal input[type="file"]' ); await fileInput.setInputFiles( imagePath ); - // Wait for upload to complete - await page.waitForSelector( '.media-modal .attachment.selected', { - state: 'visible', - timeout: 15000, - } ); + // Wait for upload to complete and ensure an attachment is selected. + try { + await page.waitForSelector( '.media-modal .attachment.selected', { + state: 'visible', + timeout: 30000, + } ); + } catch { + await page.waitForSelector( '.media-modal .attachments .attachment', { + state: 'visible', + timeout: 30000, + } ); + await page.locator( '.media-modal .attachments .attachment' ).first().click(); + await page.waitForSelector( '.media-modal .attachment.selected', { + state: 'visible', + timeout: 10000, + } ); + } // Click "Select" button const selectButton = page.locator( diff --git a/tests/e2e/field-type-image.spec.ts b/tests/e2e/field-type-image.spec.ts index ba8f0184..d9a276ef 100644 --- a/tests/e2e/field-type-image.spec.ts +++ b/tests/e2e/field-type-image.spec.ts @@ -9,6 +9,7 @@ const { PLUGIN_SLUG, deleteFieldGroups, waitForMetaBoxes, + uploadImageViaModal, } = require( './field-helpers' ); const path = require( 'path' ); @@ -81,34 +82,7 @@ test.describe( 'Field Type > Image', () => { '.acf-field[data-name="test_image"] .acf-image-uploader[data-uploader="wp"] .acf-button-edit, .acf-field[data-name="test_image"] .acf-image-uploader a[data-name="add"]' ); await addImageButton.click(); - - // Wait for media modal - await page.waitForSelector( '.media-modal', { state: 'visible' } ); - - // Click "Upload files" tab - const uploadTab = page.locator( '.media-modal #menu-item-upload' ); - if ( await uploadTab.isVisible() ) { - await uploadTab.click(); - } - - // Upload the file - const fileInput = page.locator( '.media-modal input[type="file"]' ); - await fileInput.setInputFiles( TEST_IMAGE_PATH ); - - // Wait for upload to complete - await page.waitForSelector( '.media-modal .attachment.selected', { - state: 'visible', - timeout: 30000, - } ); - - // Click "Select" button - const selectButton = page.locator( - '.media-modal .media-toolbar-primary .media-button-select' - ); - await selectButton.click(); - - // Wait for modal to close - await page.waitForSelector( '.media-modal', { state: 'hidden' } ); + await uploadImageViaModal( page, TEST_IMAGE_PATH ); // Verify image is displayed in the field const imagePreview = page.locator( @@ -174,28 +148,7 @@ test.describe( 'Field Type > Image', () => { '.acf-field[data-name="removable_image"] .acf-image-uploader a[data-name="add"]' ); await addImageButton.click(); - - await page.waitForSelector( '.media-modal', { state: 'visible' } ); - - const uploadTab = page.locator( '.media-modal #menu-item-upload' ); - if ( await uploadTab.isVisible() ) { - await uploadTab.click(); - } - - const fileInput = page.locator( '.media-modal input[type="file"]' ); - await fileInput.setInputFiles( TEST_IMAGE_PATH ); - - await page.waitForSelector( '.media-modal .attachment.selected', { - state: 'visible', - timeout: 30000, - } ); - - const selectButton = page.locator( - '.media-modal .media-toolbar-primary .media-button-select' - ); - await selectButton.click(); - - await page.waitForSelector( '.media-modal', { state: 'hidden' } ); + await uploadImageViaModal( page, TEST_IMAGE_PATH ); // Verify image is there const imagePreview = page.locator(