Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix required form radio/checkbox fields #1643

Merged
merged 13 commits into from
Sep 3, 2020
Merged
1 change: 1 addition & 0 deletions .dev/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {

'js/coblocks-accordion-polyfill': path.resolve( process.cwd(), 'src/js/coblocks-accordion-polyfill.js' ),
'js/coblocks-accordion-carousel': path.resolve( process.cwd(), 'src/js/coblocks-accordion-carousel.js' ),
'js/coblocks-checkbox-required': path.resolve( process.cwd(), 'src/js/coblocks-checkbox-required.js' ),
'js/coblocks-datepicker': path.resolve( process.cwd(), 'src/js/coblocks-datepicker.js' ),
'js/coblocks-events': path.resolve( process.cwd(), 'src/js/coblocks-events.js' ),
'js/coblocks-fromEntries': path.resolve( process.cwd(), 'src/js/coblocks-fromEntries.js' ),
Expand Down
45 changes: 44 additions & 1 deletion .dev/tests/phpunit/includes/test-coblocks-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ public function test_render_field_radio_empty_options() {
*/
public function test_render_field_radio() {

$this->expectOutputRegex( '/<label class="coblocks-radio-label" for="radio-option-1">Option 1<\/label><input id="radio-option-2" type="radio" name="field-radio\[value\]" value="Option 2" class="radio">/' );
$this->expectOutputRegex( '/<label class="coblocks-radio-label" for="choose-one-option-1">Option 1<\/label><input id="choose-one-option-2" type="radio" name="field-choose-one\[value\]" value="Option 2" class="radio">/' );

echo $this->coblocks_form->render_field_radio(
[
Expand Down Expand Up @@ -508,6 +508,49 @@ public function test_render_field_checkbox_inline() {

}

/**
* Test the checkbox required class is added to the markup as expected
*/
public function test_render_field_checkbox_required_class() {

$this->expectOutputRegex( '/<div class="coblocks-field checkbox required">/' );

echo $this->coblocks_form->render_field_checkbox(
[
'options' => [
'option-1' => 'Option 1',
'option-2' => 'Option 2',
],
'required' => true,
],
''
);

}

/**
* Test that the required checkbox field script is loaded when checkboxes
* are set to required
*/
public function test_required_checkbox_script() {

$this->coblocks_form->render_field_checkbox(
[
'options' => [
'option-1' => 'Option 1',
'option-2' => 'Option 2',
],
'required' => true
],
''
);

global $wp_scripts;

$this->assertArrayHasKey( 'coblocks-checkbox-required', $wp_scripts->registered );

}

/**
* Test the website field markup is as expected
*/
Expand Down
2 changes: 2 additions & 0 deletions .dev/tests/phpunit/test-class-coblocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ public function test_final_build_assets_exist() {
'js' => [
'dist/coblocks.js',
'dist/js/coblocks-accordion-polyfill.js',
'dist/js/coblocks-checkbox-required.js',
'dist/js/coblocks-datepicker.js',
'dist/js/coblocks-fromEntries.js',
'dist/js/coblocks-google-maps.js',
Expand All @@ -195,6 +196,7 @@ public function test_final_build_assets_exist() {
'dist/js/vendors/flickity.js',
'dist/js/vendors/slick.js',
'src/js/coblocks-accordion-polyfill.js',
'src/js/coblocks-checkbox-required.js',
'src/js/coblocks-datepicker.js',
'src/js/coblocks-fromEntries.js',
'src/js/coblocks-google-maps.js',
Expand Down
19 changes: 19 additions & 0 deletions docs/hooks/forms-block.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,22 @@ function coblocks_form_sent_message() {
}
add_filter( 'coblocks_form_sent_notice', 'coblocks_form_sent_message' );
```

## Customize the Checkbox Required Text

The following `PHP` filter can be used to set a custom response when a required checkbox group has no options set before submitting the form.

```php
/**
* Set a custom error message to display when a required checkbox
* group has no options set before submitting the form.
*
* @return string Form submission success message
*/
function coblocks_checkbox_required_text() {

return __( 'Please check at least one checkbox.', 'textdomain' );

}
add_filter( 'coblocks_form_checkbox_required_text', 'coblocks_checkbox_required_text' );
```
43 changes: 36 additions & 7 deletions includes/class-coblocks-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,10 @@ public function render_field_radio( $atts ) {

$the_options = array_filter( $atts['options'] );

$label = isset( $atts['label'] ) ? $atts['label'] : __( 'Choose one', 'coblocks' );
$label_desc = sanitize_title( $label ) !== 'choose-one' ? sanitize_title( $label ) : 'radio';
$label_slug = $radio_count > 1 ? sanitize_title( $label_desc . '-' . $radio_count ) : sanitize_title( $label_desc );
$label = isset( $atts['label'] ) ? $atts['label'] : __( 'Choose one', 'coblocks' );
$label_desc = sanitize_title( $label ) !== 'choose-one' ? sanitize_title( $label ) : 'choose-one';
$label_slug = $radio_count > 1 ? sanitize_title( $label_desc . '-' . $radio_count ) : sanitize_title( $label_desc );
$required_attr = ( isset( $atts['required'] ) && $atts['required'] ) ? ' required' : '';

ob_start();

Expand All @@ -503,14 +504,15 @@ public function render_field_radio( $atts ) {

}

foreach ( $the_options as $value ) {
foreach ( $the_options as $key => $value ) {

printf(
'<input id="%1$s" type="radio" name="field-%2$s[value]" value="%3$s" class="radio">
<label class="coblocks-radio-label" for="%1$s">%4$s</label>',
'<input id="%1$s" type="radio" name="field-%2$s[value]" value="%3$s" class="radio"%4$s>
<label class="coblocks-radio-label" for="%1$s">%5$s</label>',
esc_attr( $label_slug . '-' . sanitize_title( $value ) ),
esc_attr( $label_slug ),
esc_attr( $value ),
$key === 0 ? esc_attr( $required_attr ) : '',
esc_html( $value )
);

Expand Down Expand Up @@ -600,10 +602,37 @@ public function render_field_checkbox( $atts ) {

$label = isset( $atts['label'] ) ? $atts['label'] : __( 'Select', 'coblocks' );
$label_slug = $checkbox_count > 1 ? sanitize_title( $label . '-' . $checkbox_count ) : sanitize_title( $label );
$required = ( isset( $atts['required'] ) && $atts['required'] );

if ( $required ) {

wp_enqueue_script(
'coblocks-checkbox-required',
CoBlocks()->asset_source( 'js' ) . 'coblocks-checkbox-required.js',
array(),
COBLOCKS_VERSION,
true
);

}

ob_start();

print( '<div class="coblocks-field">' );
printf(
'<div class="coblocks-field checkbox%1$s">
%2$s',
$required ? esc_attr( ' required' ) : '',
$required ? sprintf(
'<div class="required-error hidden">%s</div>',
/**
* Filter the checkbox required text that displays when no checkbox is
* selected when the form is submitted.
*
* @param string $error_text Error text displayed to the user.
*/
(string) apply_filters( 'coblocks_form_checkbox_required_text', esc_html__( 'Please select at least one checkbox.', 'coblocks' ) )
EvanHerman marked this conversation as resolved.
Show resolved Hide resolved
) : ''
);

$this->render_field_label( $atts, $label, $checkbox_count );

Expand Down
4 changes: 4 additions & 0 deletions src/blocks/form/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
margin: 1.25rem 0 3px 0;
}

.required-error.hidden {
display: none;
}

.coblocks-field,
select {
margin: 0 0 1.25rem 0;
Expand Down
30 changes: 30 additions & 0 deletions src/js/coblocks-checkbox-required.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
document.addEventListener( 'DOMContentLoaded', function() {
document.querySelectorAll( '.coblocks-form form' ).forEach( form => {

let requiredErrorDiv = form.getElementsByClassName( 'required-error' )[0];

// No required checkboxes
if ( ! form.querySelectorAll( '.coblocks-field.checkbox.required' ).length ) {
return;
}

// Form Submit Event Listener
form.addEventListener( 'submit', event => {
let selectedCheckboxes = form.querySelectorAll( '.coblocks-field.checkbox.required input[type="checkbox"]:checked' ).length;
if ( selectedCheckboxes === 0 ) {
requiredErrorDiv.style.display = 'block';
event.preventDefault();
return;
}
requiredErrorDiv.style.display = 'none';
} );

// Required Checkbox Event Listener
form.querySelectorAll( '.coblocks-field.checkbox.required input[type="checkbox"]' ).forEach( requiredCheckbox => {
requiredCheckbox.addEventListener( 'change', () => {
requiredErrorDiv.style.display = 'none';
} );
} );

} );
} );