Parcourir la source

Fixes #15917: slim-select-pagination-bug-fix : fixed several bugs related to slim select (#15918)

* slim-select-pagination-bug-fix : fixed several bugs related to slim
select search box gui element
1. If user enters a search text in the filter text box, the user will
   not be able to scroll to the next page. That is the user will only be
   able to see the first page of returned item with a none empty search
   string.
2. User will not be able to select an item returned from search query
   if user clicks reload after a dynami search. When the user is able
   to load a second page, the user will be able to select an item from
   the third+ page if previous bug is fixed.

* Recompile static assets

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Nancy Yang il y a 1 an
Parent
commit
a544b55e9e

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
netbox/project-static/dist/netbox.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
netbox/project-static/dist/netbox.js.map


+ 49 - 22
netbox/project-static/src/select/api/apiSelect.ts

@@ -140,6 +140,10 @@ export class APISelect {
    */
   private queryUrl: string = '';
 
+  /**
+   * Interal state variable used to remember search key entered by user for "Filter" search box
+  */
+  private searchKey: Nullable<string> = null;
   /**
    * Scroll position of options is at the bottom of the list, or not. Used to determine if
    * additional options should be fetched from the API.
@@ -359,30 +363,41 @@ export class APISelect {
     this.slim.enable();
   }
 
+  private setSearchKey(event: Event) {
+    const { value: q } = event.target as HTMLInputElement;
+    this.searchKey = q
+  }
+
   /**
    * Add event listeners to this element and its dependencies so that when dependencies change
    * this element's options are updated.
    */
   private addEventListeners(): void {
     // Create a debounced function to fetch options based on the search input value.
-    const fetcher = debounce((event: Event) => this.handleSearch(event), 300, false);
+    const fetcher = debounce((action:ApplyMethod, url: Nullable<string>) => this.handleSearch(action, url), 300, false);
 
     // Query the API when the input value changes or a value is pasted.
     this.slim.slim.search.input.addEventListener('keyup', event => {
       // Only search when necessary keys are pressed.
       if (!event.key.match(/^(Arrow|Enter|Tab).*/)) {
-        return fetcher(event);
+        this.setSearchKey(event);
+        return fetcher('replace', null);
       }
     });
-    this.slim.slim.search.input.addEventListener('paste', event => fetcher(event));
+    this.slim.slim.search.input.addEventListener('paste', event => {
+      this.setSearchKey(event);
+      return fetcher('replace', null);;
+});
 
     // Watch every scroll event to determine if the scroll position is at bottom.
     this.slim.slim.list.addEventListener('scroll', () => this.handleScroll());
 
     // When the scroll position is at bottom, fetch additional options.
-    this.base.addEventListener(`netbox.select.atbottom.${this.name}`, () =>
-      this.fetchOptions(this.more, 'merge'),
-    );
+    this.base.addEventListener(`netbox.select.atbottom.${this.name}`, () => {
+      if (this.more!=null) {
+        return fetcher('merge', this.more, )
+      }      
+    });
 
     // When the base select element is disabled or enabled, properly disable/enable this instance.
     this.base.addEventListener(`netbox.select.disabled.${this.name}`, event =>
@@ -551,6 +566,14 @@ export class APISelect {
     }
   }
 
+  private getUrl() {
+    var url = this.queryUrl
+    if (this.searchKey!=null) {
+      url = queryString.stringifyUrl({ url: this.queryUrl, query: { q : this.searchKey } })
+    }      
+    return url
+  }
+
   /**
    * Query the NetBox API for this element's options.
    */
@@ -559,21 +582,25 @@ export class APISelect {
       this.resetOptions();
       return;
     }
-    await this.fetchOptions(this.queryUrl, action);
+    const url = this.getUrl()
+    await this.fetchOptions(url, action);
   }
 
   /**
    * Query the API for a specific search pattern and add the results to the available options.
    */
-  private async handleSearch(event: Event) {
-    const { value: q } = event.target as HTMLInputElement;
-    const url = queryString.stringifyUrl({ url: this.queryUrl, query: { q } });
-    if (!url.includes(`{{`)) {
-      await this.fetchOptions(url, 'merge');
-      this.slim.data.search(q);
-      this.slim.render();
+  private async handleSearch(action: ApplyMethod = 'merge', url: Nullable<string> ) {
+    if (url==null) {
+      url = this.getUrl()
     }
-    return;
+    if (url.includes(`{{`)) {
+      return
+    }  
+    await this.fetchOptions(url, action);
+    if (this.searchKey!=null) {
+      this.slim.data.search(this.searchKey);
+    }    
+    this.slim.render();
   }
 
   /**
@@ -586,13 +613,11 @@ export class APISelect {
       Math.floor(this.slim.slim.list.scrollTop) + this.slim.slim.list.offsetHeight ===
       this.slim.slim.list.scrollHeight;
 
-    if (this.atBottom && !atBottom) {
-      this.atBottom = false;
-      this.base.dispatchEvent(this.bottomEvent);
-    } else if (!this.atBottom && atBottom) {
-      this.atBottom = true;
+    this.atBottom = atBottom
+
+    if (this.atBottom) {
       this.base.dispatchEvent(this.bottomEvent);
-    }
+    }    
   }
 
   /**
@@ -994,7 +1019,9 @@ export class APISelect {
         ['btn', 'btn-sm', 'btn-ghost-dark'],
         [createElement('i', null, ['mdi', 'mdi-reload'])],
       );
-      refreshButton.addEventListener('click', () => this.loadData());
+      // calling this.loadData() will prevent first page of returned items
+      // with non-null search key inplace not selectable
+      refreshButton.addEventListener('click', () => this.handleSearch('replace', null));
       refreshButton.type = 'button';
       this.slim.slim.search.container.appendChild(refreshButton);
     }

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff