Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions classes/helpers/FrmStylesPreviewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ public function get_html_for_form_preview() {
// Force is_admin to false so the "Entry Key" field doesn't render in the preview.
add_filter( 'frm_is_admin', '__return_false' );

// The styler edit page also renders its own settings form, so this preview's <form> landmark
// needs a distinct name to avoid tripping the aria_landmark_name_unique a11y rule.
add_filter( 'frm_form_attributes', array( $this, 'add_preview_landmark_label' ) );

$target_form_preview_html = FrmFormsController::show_form( $this->form_id, '', 'auto', 'auto' );

$this->form_includes_captcha = wp_script_is( 'captcha-api', 'enqueued' );
Expand All @@ -217,13 +221,26 @@ public function get_html_for_form_preview() {
wp_dequeue_script( 'captcha-api' );
}

remove_filter( 'frm_form_attributes', array( $this, 'add_preview_landmark_label' ) );

// Return the is_admin status.
// Otherwise success messages won't use the proper mark up and will appear without the green background and padding.
remove_filter( 'frm_is_admin', '__return_false' );

return $target_form_preview_html;
}

/**
* @since x.x
*
* @param string $attributes
*
* @return string
*/
public function add_preview_landmark_label( $attributes ) {
return $attributes . ' aria-label="' . esc_attr__( 'Form preview', 'formidable' ) . '"';
}

/**
* @since 6.0
*
Expand Down
2 changes: 1 addition & 1 deletion classes/views/styles/_styles-edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// It is accessed from /wp-admin/admin.php?page=formidable-styles&frm_action=edit&form=782
?>
<div id="frm_style_sidebar" class="frm-right-panel frm-fields frm_wrap frm-p-6">
<form id="frm_styling_form" method="post" action="<?php echo esc_url( FrmStylesHelper::get_edit_url( $style, $form->id, FrmAppHelper::get_param( 'section' ) ) ); ?>">
<form id="frm_styling_form" method="post" action="<?php echo esc_url( FrmStylesHelper::get_edit_url( $style, $form->id, FrmAppHelper::get_param( 'section' ) ) ); ?>" aria-label="<?php esc_attr_e( 'Style settings', 'formidable' ); ?>">
<input type="hidden" name="ID" value="<?php echo esc_attr( $style->ID ); ?>" />
<input type="hidden" name="frm_action" value="save" />

Expand Down
4 changes: 2 additions & 2 deletions classes/views/xml/import_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
?>
</p>
<br/>
<form enctype="multipart/form-data" method="post" class="frm-fields">
<form enctype="multipart/form-data" method="post" class="frm-fields" aria-label="<?php esc_attr_e( 'Import', 'formidable' ); ?>">
<input type="hidden" name="frm_action" value="import_xml" />
<?php wp_nonce_field( 'import-xml-nonce', 'import-xml' ); ?>
<p>
Expand Down Expand Up @@ -71,7 +71,7 @@
<p class="howto">
<?php esc_html_e( 'Export your forms, entries, views, and styles so you can easily import them on another site.', 'formidable' ); ?>
</p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" id="frm_export_xml" class="frm-fields frm_grid_container">
<form method="post" action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" id="frm_export_xml" class="frm-fields frm_grid_container" aria-label="<?php esc_attr_e( 'Export', 'formidable' ); ?>">
<input type="hidden" name="action" value="frm_export_xml" />
<?php wp_nonce_field( 'export-xml-nonce', 'export-xml' ); ?>

Expand Down
28 changes: 28 additions & 0 deletions tests/phpunit/base/FrmUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -843,4 +843,32 @@ protected function use_frm_role( $role ) {
break;
}
}

/**
* Assert that every <form> tag in some rendered HTML has a non-empty aria-label,
* and that no two forms share the same one (aria_landmark_name_unique).
*
* @since x.x
*
* @param string $html
* @param int $expected_count Required so an empty/short match list fails loudly instead of
* passing vacuously (assertNotContains/assertSame both pass on an
* empty array).
*
* @return void
*/
protected function assert_form_landmarks_have_unique_names( $html, $expected_count ) {
preg_match_all( '/<form\b[^>]*>/', $html, $matches );
$this->assertCount( $expected_count, $matches[0], 'Unexpected number of <form> elements' );

$labels = array();

foreach ( $matches[0] as $form_tag ) {
preg_match( '/aria-label="([^"]*)"/', $form_tag, $label_match );
$labels[] = $label_match[1] ?? '';
}

$this->assertNotContains( '', $labels, 'Every form landmark needs a non-empty accessible name' );
$this->assertSame( array_unique( $labels ), $labels, 'Form landmarks must have distinct accessible names' );
}
}
29 changes: 29 additions & 0 deletions tests/phpunit/styles/test_FrmStylesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,35 @@ private function get_custom_stylesheet() {
return $stylesheet_urls;
}

/**
* The styler edit view renders two <form> elements on the same page: the style
* settings sidebar form, and the live form preview. Both need distinct
* accessible names or they violate the aria_landmark_name_unique a11y rule.
*
* @covers FrmStylesController::render_style_page
*/
public function test_render_style_page_has_unique_landmark_names_for_both_forms() {
$this->set_current_user_to_1();

// render_style_page() reads $_GET to decide the view ('edit' vs 'list'); a leftover
// 'form'/'style_id' from another test would silently switch this to the list view.
$_GET = array();

$form_id = $this->factory->form->create();
$form = FrmForm::getOne( $form_id );
$frm_style = new FrmStyle( 'default' );
$active_style = $frm_style->get_one();

ob_start();
$this->run_private_method(
array( 'FrmStylesController', 'render_style_page' ),
array( $active_style, $form, $active_style )
);
$html = ob_get_clean();

$this->assert_form_landmarks_have_unique_names( $html, 2 );
}

/**
* @covers FrmStylesController::save_style
* @covers FrmStyle::update
Expand Down
15 changes: 15 additions & 0 deletions tests/phpunit/xml/test_FrmXMLController.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,19 @@ public function test_validate_xml_url() {
private function validate_xml_url( $url ) {
return $this->run_private_method( array( 'FrmXMLController', 'validate_xml_url' ), array( $url ) );
}

/**
* The Import/Export page renders two <form> elements (Import, Export). Both
* need distinct accessible names or they violate the aria_landmark_name_unique
* a11y rule.
*
* @covers FrmXMLController::form
*/
public function test_form_has_unique_landmark_names_for_import_and_export_forms() {
ob_start();
FrmXMLController::form();
$html = ob_get_clean();

$this->assert_form_landmarks_have_unique_names( $html, 2 );
}
}
Loading