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

enhanceSelectElement reset content if user input does not match selection #592

Open
vahidhedayati opened this issue Apr 26, 2023 · 0 comments

Comments

@vahidhedayati
Copy link

vahidhedayati commented Apr 26, 2023

I have spent some time trying to get enhanceSelectElement combo box to reset content to nothing when user puts in invalid data.

I came across another issue raised which someone had got content to reset for auto complete and populate div content, think underlying code is here:
https://github.com/OfficeForProductSafetyAndStandards/product-safety-database/blob/37d4d681d26640ffcb06483748a8fd89f57f75c6/mspsds-web/app/assets/application/javascripts/autocomplete.js

Trying to actual use existing calls:

accessibleAutocomplete.enhanceSelectElement({
      confirmOnBlur: true,
      onConfirm:removeValue,
       selectElement: document.querySelector('#' + fieldName)
    });

 function removeValue(val) {
       if (val===undefined) {
         let e =  document.querySelector('#' + fieldName);
    
    
          setTimeout(function() {
            const $enhancedElement = $(e).parent().find('input');
            //$enhancedElement.text="";
            $enhancedElement.val("");
           },190);
        }
    }

You need the confirmOnBlur: true to be enabled for onConfirm to work in all cases including invalid, otherwise if set to false invalid input does not make it into onConfirm function.
The removeValue function only works if there is a setTimeout to delay change otherwise the autoComplete workings overwrites value with what ever user had originally put in, ignoring local change.

Beyond this when adding setTimeout as in when data is reset, autocomplete behaves unusual as in it won't submit selection suggesting nothing was selected and when clicking outside of box, the auto complete content resets back to blank even with a valid selection.

A fix that works for what we need but perhaps needs to be worked better into the autocomplete.js and to have a switch that user can enable to allow the js to reset data :

Is this added to below segment:

if (r.length==0) {
	setTimeout(() => {
			this.handleInputChange({target: {value: ''}});
		}, 300);
}


t.handleComponentBlur = function (e) {
          var t, n = this.state,
              r = n.options,
              o = n.query,
              i = n.selected;
              
            //Added in 
          if (r.length==0) {
			setTimeout(() => {
					this.handleInputChange({target: {value: ''}});
				}, 300);
		}

          this.props.confirmOnBlur ? (t = e.query || o, this.props.onConfirm(r[i])) : t = o, this.setState({
              focused: null,
              menuOpen: e.menuOpen || !1,
              query: t,
              selected: null,
              validChoiceMade: this.isQueryAnOption(t, r)
          })
      },

To give a further update and enhancement after further discussion with other teams.
When a user types in incorrect data as per above it will clear input and also if user deletes input in auto complete in both cases it will end up calling this added eventListener of focusout

 var fieldName = "[[${id}]]";
    const e =  document.querySelector('#' + fieldName);

    var hint = "[[${hints != null && hints != '' ? hints : label}]]";
    accessibleAutocomplete.enhanceSelectElement({
      defaultValue: '',
      autoselect: true,
      confirmOnBlur: true,
      selectElement: e
    });
    let parentDiv = document.querySelector('#' + fieldName + '-select').parentNode;
    let newLabel = document.createElement("label");
    newLabel.innerHTML = hint;
    newLabel.style.display = "none";
    newLabel.setAttribute("for", document.querySelector('#' + fieldName + '-select').id);
    parentDiv.appendChild(newLabel);

    const select = e.parentNode.querySelector("select");
    const autocompleteInput =  e.parentNode.querySelector("input");

    function handleFocusOut() {
      let value = autocompleteInput.value;
      if (value === '' || value === null || value.length===0) {
        for (var i=0; i<select.length; i++) {
          select[i].removeAttribute('selected');
        }
        //This is also needed if value selected then cleared or changed to invalid pre-submit
        select.value='';
      }
    }
    handleFocusOut();
    autocompleteInput.addEventListener("focusout", handleFocusOut);

Further thought led me to try revert the added content to the autocomplete.js and get the new eventListener to handle it all including the newly added code in autocomplete.js

Unfortunately the unusual behaviour returned as per explanation of above where clicking outside of selection reset the content
and in this instance setTimeout delay was not required.

 <script >
    var fieldName = "[[${id}]]";
    const e =  document.querySelector('#' + fieldName);

    var hint = "[[${hints != null && hints != '' ? hints : label}]]";
    accessibleAutocomplete.enhanceSelectElement({
      defaultValue: '',
      autoselect: true,
      confirmOnBlur: false,
      selectElement: e
    });
    let parentDiv = document.querySelector('#' + fieldName + '-select').parentNode;
    let newLabel = document.createElement("label");
    newLabel.innerHTML = hint;
    newLabel.style.display = "none";
    newLabel.setAttribute("for", document.querySelector('#' + fieldName + '-select').id);
    parentDiv.appendChild(newLabel);

    const select = e.parentNode.querySelector("select");
    const autocompleteInput =  e.parentNode.querySelector("input");

    function handleFocusOut() {
      let value = autocompleteInput.value;
      if (value === '' || value === null || value.length===0) {
        for (var i=0; i<select.length; i++) {
          console.log(' '+ select[i]+' '+ select[i].innerText);
          select[i].removeAttribute('selected');
        }
      } else {
      const option = Array.from(select).find(
         (o) => o.value === value
       );
       if (!option ) {
      //  setTimeout(()=> {
        autocompleteInput.value="";
        for (var i=0; i<select.length; i++) {
          select[i].removeAttribute('selected');
        }
     //   },300);

       }
      }
    }
    handleFocusOut();
    autocompleteInput.addEventListener("focusout", handleFocusOut);
  </script>

It would be nice to be able to do this without having to add new additional code and change autocomplete.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant