Skip to content

Commit

Permalink
Merge pull request #98 from localgovdrupal/fix/address-lookup-error
Browse files Browse the repository at this point in the history
Fix: Address lookup validation
  • Loading branch information
Adnan-cds authored Nov 20, 2024
2 parents 52d5d4c + f9a7e8d commit 3dd9341
Show file tree
Hide file tree
Showing 10 changed files with 1,075 additions and 26 deletions.
5 changes: 3 additions & 2 deletions js/address_change.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* Populate Central Hub Selected Address
*
* Adds the selected address to drupalSettings.centralhub.selectedAddress.
*
* @param {jQuery} selectList
* Address selectlist element.
*/
Expand Down Expand Up @@ -87,7 +88,7 @@
var searchButton = addressLookupElement.find('.js-address-searchbutton');
var selectListContainer = addressLookupElement.find('.js-address-select-container');
var selectList = selectListContainer.find('.js-address-select');
var error = selectListContainer.find('.js-address-error');
var error = selectListContainer.find('.js-address-error, .error');

// Change the search button to normal button.
searchButton.attr('type', 'button');
Expand Down Expand Up @@ -154,7 +155,7 @@
var searchButton = addressLookupElement.find('.js-address-searchbutton');
var selectListContainer = addressLookupElement.find('.js-address-select-container');
var selectList = selectListContainer.find('.js-address-select');
var error = selectListContainer.find('.js-address-error');
var error = selectListContainer.find('.js-address-error, .error');
var resetButton = addressLookupElement.find('.js-reset-address');
var manualButton = addressLookupElement.find('.js-manual-address');
var manualAddressContainer = addressLookupElement.find('+ .js-address-entry-container');
Expand Down
13 changes: 1 addition & 12 deletions js/address_select.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
function hideManualAddress(centralHubElement, type, settings) {
var manualAddressContainer = centralHubElement.find('.js-address-entry-container');
var manualButton = centralHubElement.find('.js-manual-address');
var addressSelectContainer = centralHubElement.find('.js-address-select-container');
manualAddressContainer.addClass('hidden');

if (type == 'hard') {
Expand All @@ -63,24 +62,18 @@

/**
* Show the manual address form elements.
*
* @param {jQuery} centralHubElement
* Centralhub address element.
*/
function showManualAddress(centralHubElement) {
var manualAddressContainer = centralHubElement.find('.js-address-entry-container');
var manualButton = centralHubElement.find('.js-manual-address');
var addressSelectContainer = centralHubElement.find('.js-address-select-container');
var addressSelect = addressSelectContainer.find('select');
var searchElement = centralHubElement.find('.js-address-searchstring');
var addressError = addressSelectContainer.find('.js-address-error');
manualAddressContainer.removeClass('hidden');
// manualAddressContainer.find('input').val('');
manualButton.hide();
// addressSelectContainer.addClass('hidden');
// addressSelect.val('0');
// Clear the search element when entering a manual address.
// This is to pass validation.
// searchElement.val('');
// Remove the error element when making a manual address.
addressError.remove();
}
Expand Down Expand Up @@ -174,7 +167,6 @@
central_hub_webform_address_container.find('input.js-localgov-forms-webform-uk-address--' + value).val(addressSelected[value]);
});

// hideManualAddress(centralHubElement, 'soft');
showManualAddress(centralHubElement);
} else if ($(this).val() == 0) {
// If choosing the empty option, clear out the address fields.
Expand Down Expand Up @@ -211,9 +203,6 @@
hideManualAddress(centralHubElement, 'soft', settings);
}

// centralHubElement.find('.js-address-searchstring').change(function() {
// hideManualAddress(centralHubElement, 'hard');
// });
centralHubElement.find('.js-reset-address').click(function () {
hideManualAddress(centralHubElement, 'hard', settings);
});
Expand Down
6 changes: 4 additions & 2 deletions src/Element/AddressLookupElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ public static function processAddressLookupElement(&$element, FormStateInterface

$element['address_search']['address_searchstring'] = [
'#type' => 'textfield',
'#title' => t('Postcode or Street'),
'#title' => t('Postcode or street'),
'#description' => $element['#address_search_description'] ?? t('Enter the postcode…'),
'#required_error' => t('You must enter a postcode or street.'),
'#maxlength' => 64,
'#size' => 64,
'#weight' => '0',
Expand Down Expand Up @@ -164,6 +165,7 @@ public static function processAddressLookupElement(&$element, FormStateInterface
$element['address_select']['address_select_list'] = [
'#type' => 'select',
'#title' => $element['#address_select_title'] ?? t('Select the address'),
'#required_error' => t('You must select an address.'),
'#options' => [],
'#empty_option' => '-' . t('Please choose an address') . '-',
'#empty_value' => 0,
Expand All @@ -189,7 +191,7 @@ public static function processAddressLookupElement(&$element, FormStateInterface
$parent_container = $parent_container[$keyval];
}

// Extract the parent values form container.
// Extract the parent values from container.
$parent_container_values = $form_values;
foreach ($parents as $keyval) {
$parent_container_values = $parent_container_values[$keyval];
Expand Down
55 changes: 45 additions & 10 deletions src/Element/UKAddressLookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public static function getCompositeElements(array $element) {
$element_list['address_1']['#prefix'] = '<div class="js-address-entry-container">';
$element_list['postcode']['#suffix'] = '</div>';

// Custom error message for address line 1.
$element_list['address_1']['#required_error'] = t('You must enter an address.');

// Extras to store information for webform builders to access in
// computed twig.
// @See DRUP-1287.
Expand Down Expand Up @@ -98,14 +101,22 @@ public static function validateWebformComposite(&$element, FormStateInterface $f
return;
}

// Temporarily reset the #limit_validation_errors property. Otherwise we
// can't safely set and manipulate errors below.
//
// @see Drupal\Core\Form\FormState::setErrorByName()
// @see AddressLookupElement::processAddressLookupElement()
$orig_limit_validation_errors = $form_state->getLimitValidationErrors();
$form_state->setLimitValidationErrors(NULL);

// If the element or any of its parent containers are hidden by conditions,
// Bypass validation and clear any required element errors generated
// for this element.
if (!WebformHelper::isElementVisibleThroughParent($element, $form_state, $complete_form)) {
$form_errors = $form_state->getErrors();
$form_state->clearErrors();
foreach ($form_errors as $error_key => $error_value) {
if (strpos($error_key, $element_key . ']') !== 0) {
if (strpos($error_key, $element_key . ']') === FALSE) {
$form_state->setErrorByName($error_key, $error_value);
}
}
Expand All @@ -115,6 +126,7 @@ public static function validateWebformComposite(&$element, FormStateInterface $f
// Get the search string and selected value.
$search_string = $value['address_lookup']['address_search']['address_searchstring'];
$selected = $value['address_lookup']['address_select']['address_select_list'] ?? [];
$is_address_lookup_op = $form_state->getTriggeringElement()['#name'] === $element_key . '[address_lookup][address_search][address_actions][address_searchbutton]';

// Check to see if there are values in the address element form.
$has_address_values = FALSE;
Expand All @@ -126,9 +138,8 @@ public static function validateWebformComposite(&$element, FormStateInterface $f
}
}

// If the select is empty, and the manual address elements are filled in,
// validate the parent element.
if (empty($selected) && $has_address_values) {
// If the manual address elements are filled in, validate the parent.
if (!$is_address_lookup_op && $has_address_values) {
// Clear the address search string.
// This is to avoid the select box maintaing a value
// (it's cleared if search string is empty).
Expand All @@ -151,26 +162,46 @@ public static function validateWebformComposite(&$element, FormStateInterface $f
// Then show an error to search for a local address or select can't find
// the address.
if (!empty($search_string) && $element['address_lookup']['address_select']['address_select_list']['#type'] == 'markup') {
$form_state->setError($element, t('Search for a local address, or select "Can\'t find the address" to enter an address.'));
$form_state->setError($element['address_lookup']['address_search']['address_searchstring'], t('Enter a local address, or select "Can\'t find the address".'));

// Inline form errors don't work well for this element in Ajax calls.
// This is because the Ajax callback attached to the `Find address`
// button updates only *part* of the address lookup element. As a
// result, any error set on any other part of the address lookup element
// is lost. To avoid this, we disable inline errors here. There is an
// assumption here that the `Find address` button is using Ajax. This
// assumption is good enough for most cases but degrades without Ajax.
if ($is_address_lookup_op) {
$complete_form['#disable_inline_form_errors'] = TRUE;
}
}
// Else if there is a search but no address selected,
// set the select box as required.
elseif (!empty($search_string) && empty($selected)) {
elseif (!empty($search_string) && empty($selected) && !$is_address_lookup_op) {
WebformElementHelper::setRequiredError($element['address_lookup']['address_select']['address_select_list'], $form_state);

// UI needs a hint that we are in the middle of a search.
$element['address_lookup']['address_search']['address_actions']['address_searchbutton']['#attributes']['class'][] = 'js-searching';
}
// Else mark the entire element as required.
elseif (empty($search_string) && empty($selected)) {
WebformElementHelper::setRequiredError($element, $form_state);
WebformElementHelper::setRequiredError($element['address_lookup']['address_search']['address_searchstring'], $form_state);

// As explained above, inline form errors don't work well for this
// element in Ajax calls.
if ($is_address_lookup_op) {
$complete_form['#disable_inline_form_errors'] = TRUE;
}
}

// Fetch errors, to allow any generated errors for the child elements
// to be removed.
$form_errors = $form_state->getErrors();

// Loop through errors and remove child elements, except the select
// element.
// and search query elements.
foreach ($form_errors as $error_key => $error_value) {
if (strpos($error_key, $element_key . ']') === 0 && $error_key != $element_key . '][address_lookup][address_select][address_select_list') {
if (strpos($error_key, $element_key . ']') === 0 && ($error_key !== $element_key . '][address_lookup][address_select][address_select_list' && $error_key !== $element_key . '][address_lookup][address_search][address_searchstring')) {
unset($form_errors[$error_key]);
}
}
Expand All @@ -181,13 +212,17 @@ public static function validateWebformComposite(&$element, FormStateInterface $f
unset($form_errors[$element_key . '][address_lookup][address_select][address_select_list']);
}

// Reset form errors and reset them with the cleaned ones.
// Reset form errors and replace them with cleaned ones.
$form_state->clearErrors();
foreach ($form_errors as $error_key => $error_value) {
$form_state->setErrorByName($error_key, $error_value);
}
}

// Restore original value of the `limit_validation_errors` property now that
// we are done with manipulating errors.
$form_state->setLimitValidationErrors($orig_limit_validation_errors);

// Clear empty composites value.
if (empty(array_filter($value))) {
$element['#value'] = NULL;
Expand Down
2 changes: 2 additions & 0 deletions src/Element/WebformUKAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public static function getCompositeElements(array $element) {
$elements['town_city'] = [
'#type' => 'textfield',
'#title' => t('Town/City'),
'#required_error' => t('You must enter the town/city.'),
'#attributes' => [
'data-webform-composite-id' => $html_id . '--town_city',
// Add a namespaced class for setting the address fields
Expand All @@ -78,6 +79,7 @@ public static function getCompositeElements(array $element) {
$elements['postcode'] = [
'#type' => 'textfield',
'#title' => t('Postcode'),
'#required_error' => t('You must enter the postcode.'),
'#attributes' => [
'data-webform-composite-id' => $html_id . '--postcode',
// Add a namespaced class for setting the address fields
Expand Down
Loading

0 comments on commit 3dd9341

Please sign in to comment.