You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2025/06/20 07:42:29 UTC
(myfaces-tobago) branch main updated (c38b9380ba -> 783fc77894)
This is an automated email from the ASF dual-hosted git repository.
hnoeth pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
from c38b9380ba fix(sheet): not a function
new dcc106d2c7 fix(lazy-sheet): empty entries
new 783fc77894 build(theme): rebuild
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../tobago/internal/component/AbstractUISheet.java | 57 ++++---
.../internal/renderkit/renderer/SheetRenderer.java | 6 +-
.../taglib/component/SheetTagDeclaration.java | 8 +-
.../3000-sheet/80-lazy-sort/Lazy_Sort.test.js | 137 +++++++---------
.../900-test/3000-sheet/82-lazy-2/Lazy_2.test.js | 48 +++---
.../83-lazy-columnPanel/Lazy_ColumnPanel.test.js | 71 +++------
.../lazy-selectable-use-case.test.js | 27 +++-
.../lazy-selectable-use-case.xhtml | 2 +-
.../85-lazy-row-attribute/82-lazy-2/Lazy_2.test.js | 52 +++---
.../85-lazy-row-attribute/82-lazy-2/Lazy_2.xhtml | 3 +-
.../lazy-row-attribute.test.js | 69 +++-----
.../85-lazy-row-attribute/lazy-row-attribute.xhtml | 2 +
.../tobago-theme-standard/src/main/js/tobago.js | 8 +-
.../src/main/js/tobago.js.map | 2 +-
.../src/main/ts/tobago-sheet.ts | 174 +++++++++++++++------
15 files changed, 338 insertions(+), 328 deletions(-)
(myfaces-tobago) 02/02: build(theme): rebuild
Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
hnoeth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
commit 783fc77894b8d331a2b8ab395608dc096808ce67
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Fri Jun 20 09:35:34 2025 +0200
build(theme): rebuild
---
tobago-theme/tobago-theme-standard/src/main/js/tobago.js | 8 ++++----
tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
index 7d03194651..7bcbb7ee5b 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
@@ -1,20 +1,20 @@
-!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e,t,s,i;!function(e){e.ACTIVE="active",e.AUTOCOMPLETE="autocomplete",e.AUTOCOMPLETE_INPUT="autocomplete-input",e.BOTTOM_0="bottom-0",e.COLLAPSE="collapse",e.COLLAPSING="collapsing",e.D_NONE="d-none",e.DISABLED="disabled",e.DROPDOWN="dropdown",e.DROPDOWN_ITEM="dropdown-item",e.DROPDOWN_MENU="dropdown-menu",e.DROPDOWN_MENU_END="dropdown-menu-end",e.END_0="end-0",e.FADE="fade",e.FIXED_BOTTOM="fixe [...]
+!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e,t,s,i;!function(e){e.ACTIVE="active",e.AUTOCOMPLETE="autocomplete",e.AUTOCOMPLETE_INPUT="autocomplete-input",e.BOTTOM_0="bottom-0",e.COLLAPSE="collapse",e.COLLAPSING="collapsing",e.D_NONE="d-none",e.DISABLED="disabled",e.DROPDOWN="dropdown",e.DROPDOWN_ITEM="dropdown-item",e.DROPDOWN_MENU="dropdown-menu",e.DROPDOWN_MENU_END="dropdown-menu-end",e.END_0="end-0",e.FADE="fade",e.FIXED_BOTTOM="fixe [...]
/*!
* Bootstrap v5.3.6 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
-const st=new Map,it={set(e,t,s){st.has(e)||st.set(e,new Map);const i=st.get(e);i.has(t)||0===i.size?i.set(t,s):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(i.keys())[0]}.`)},get:(e,t)=>st.has(e)&&st.get(e).get(t)||null,remove(e,t){if(!st.has(e))return;const s=st.get(e);s.delete(t),0===s.size&&st.delete(e)}},nt="transitionend",ot=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,((e,t)=>`#${CSS.escape(t)}`))),e),r [...]
+const st=new Map,it={set(e,t,s){st.has(e)||st.set(e,new Map);const i=st.get(e);i.has(t)||0===i.size?i.set(t,s):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(i.keys())[0]}.`)},get:(e,t)=>st.has(e)&&st.get(e).get(t)||null,remove(e,t){if(!st.has(e))return;const s=st.get(e);s.delete(t),0===s.size&&st.delete(e)}},nt="transitionend",ot=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,((e,t)=>`#${CSS.escape(t)}`))),e),r [...]
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
-const ar=globalThis,lr=ar.trustedTypes,dr=lr?lr.createPolicy("lit-html",{createHTML:e=>e}):void 0,cr="$lit$",hr=`lit$${Math.random().toFixed(9).slice(2)}$`,ur="?"+hr,pr=`<${ur}>`,gr=document,mr=()=>gr.createComment(""),fr=e=>null===e||"object"!=typeof e&&"function"!=typeof e,br=Array.isArray,vr="[ \t\n\f\r]",Er=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_r=/-->/g,yr=/>/g,wr=RegExp(`>|${vr}(?:([^\\s"'>=/]+)(${vr}*=${vr}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Sr=/'/g,Ar=/"/g,O [...]
+const ar=globalThis,lr=ar.trustedTypes,dr=lr?lr.createPolicy("lit-html",{createHTML:e=>e}):void 0,cr="$lit$",hr=`lit$${Math.random().toFixed(9).slice(2)}$`,ur="?"+hr,pr=`<${ur}>`,gr=document,mr=()=>gr.createComment(""),fr=e=>null===e||"object"!=typeof e&&"function"!=typeof e,br=Array.isArray,vr="[ \t\n\f\r]",Er=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_r=/-->/g,yr=/>/g,wr=RegExp(`>|${vr}(?:([^\\s"'>=/]+)(${vr}*=${vr}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Sr=/'/g,Ar=/"/g,O [...]
<button type='button'
class='tobago-button btn btn-secondary badge' aria-label='deselect ${e}'
${s>0?" tabindex='"+String(s)+"'":""}
@click="${this.removeBadge.bind(this)}"
@focus="${this.focusEvent.bind(this)}"
- @blur="${this.blurEvent.bind(this)}"><i class='bi-x-lg'></i></button>`}removeBadge(e){var t;const s=e.target.closest(".btn-group").dataset.tobagoValue,i=this.hiddenSelect.querySelector(`[value="${s}"]`);i.selected=!1;const n=this.badges.querySelector(`[data-tobago-value="${s}"]`),o=n.previousElementSibling,r="SPAN"===(null===(t=n.nextElementSibling)||void 0===t?void 0:t.tagName)?n.nextElementSibling:null;o?o.querySelector("button.btn.badge").focus():r?r.querySelector("button.btn.ba [...]
+ @blur="${this.blurEvent.bind(this)}"><i class='bi-x-lg'></i></button>`}removeBadge(e){var t;const s=e.target.closest(".btn-group").dataset.tobagoValue,i=this.hiddenSelect.querySelector(`[value="${s}"]`);i.selected=!1;const n=this.badges.querySelector(`[data-tobago-value="${s}"]`),o=n.previousElementSibling,r="SPAN"===(null===(t=n.nextElementSibling)||void 0===t?void 0:t.tagName)?n.nextElementSibling:null;o?o.querySelector("button.btn.badge").focus():r?r.querySelector("button.btn.ba [...]
//# sourceMappingURL=tobago.js.map
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
index 501cf70bbb..d0da1347e3 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
@@ -1 +1 @@
-{"version":3,"file":"tobago.js","sources":["../ts/tobago-css.ts","../ts/tobago-key.ts","../ts/tobago-client-behaviors.ts","../ts/tobago-dropdown-menu.ts","../ts/tobago-bar.ts","../ts/tobago-date.ts","../ts/tobago-menu-store.ts","../ts/tobago-dropdown-item-factory.ts","../ts/tobago-dropdown-item.ts","../ts/tobago-dropdown.ts","../ts/tobago-overlay-type.ts","../ts/tobago-suggest-filter.ts","../ts/tobago-page-static.ts","../ts/tobago-page.ts","../ts/tobago-overlay.ts","../ts/tobago-file.ts" [...]
\ No newline at end of file
+{"version":3,"file":"tobago.js","sources":["../ts/tobago-css.ts","../ts/tobago-key.ts","../ts/tobago-client-behaviors.ts","../ts/tobago-dropdown-menu.ts","../ts/tobago-bar.ts","../ts/tobago-date.ts","../ts/tobago-menu-store.ts","../ts/tobago-dropdown-item-factory.ts","../ts/tobago-dropdown-item.ts","../ts/tobago-dropdown.ts","../ts/tobago-overlay-type.ts","../ts/tobago-suggest-filter.ts","../ts/tobago-page-static.ts","../ts/tobago-page.ts","../ts/tobago-overlay.ts","../ts/tobago-file.ts" [...]
\ No newline at end of file
(myfaces-tobago) 01/02: fix(lazy-sheet): empty entries
Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
hnoeth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
commit dcc106d2c7def6e49f2164a0b6de9f039db2fbdc
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon May 5 13:08:09 2025 +0200
fix(lazy-sheet): empty entries
The Ajax request for lazy loading was executed every 100ms. If the user scrolled in this time to an empty row which was not covered by the current Ajax request, it stayed empty.
Now there is an interval, which checks every 100ms if the rows are loaded. If so, the interval stops.
Also the lazy loading is not limited to fixed dummy rows. This prevents loading of already loaded rows and reduce the amount of Ajax requests.
* better description of lazyRows attribute
* adjust tests
Issue: TOBAGO-2416
---
.../tobago/internal/component/AbstractUISheet.java | 57 ++++---
.../internal/renderkit/renderer/SheetRenderer.java | 6 +-
.../taglib/component/SheetTagDeclaration.java | 8 +-
.../3000-sheet/80-lazy-sort/Lazy_Sort.test.js | 137 +++++++---------
.../900-test/3000-sheet/82-lazy-2/Lazy_2.test.js | 48 +++---
.../83-lazy-columnPanel/Lazy_ColumnPanel.test.js | 71 +++------
.../lazy-selectable-use-case.test.js | 27 +++-
.../lazy-selectable-use-case.xhtml | 2 +-
.../85-lazy-row-attribute/82-lazy-2/Lazy_2.test.js | 52 +++---
.../85-lazy-row-attribute/82-lazy-2/Lazy_2.xhtml | 3 +-
.../lazy-row-attribute.test.js | 69 +++-----
.../85-lazy-row-attribute/lazy-row-attribute.xhtml | 2 +
.../src/main/ts/tobago-sheet.ts | 174 +++++++++++++++------
13 files changed, 333 insertions(+), 323 deletions(-)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
index ea7a036c81..7d237f32fe 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
@@ -19,6 +19,28 @@
package org.apache.myfaces.tobago.internal.component;
+import jakarta.el.ELContext;
+import jakarta.el.MethodExpression;
+import jakarta.el.ValueExpression;
+import jakarta.faces.component.UIColumn;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UINamingContainer;
+import jakarta.faces.component.behavior.AjaxBehavior;
+import jakarta.faces.component.behavior.ClientBehavior;
+import jakarta.faces.component.behavior.ClientBehaviorContext;
+import jakarta.faces.component.behavior.ClientBehaviorHolder;
+import jakarta.faces.component.visit.VisitCallback;
+import jakarta.faces.component.visit.VisitContext;
+import jakarta.faces.component.visit.VisitHint;
+import jakarta.faces.component.visit.VisitResult;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.AbortProcessingException;
+import jakarta.faces.event.ComponentSystemEvent;
+import jakarta.faces.event.ComponentSystemEventListener;
+import jakarta.faces.event.FacesEvent;
+import jakarta.faces.event.ListenerFor;
+import jakarta.faces.event.PhaseId;
+import jakarta.faces.event.PreRenderComponentEvent;
import org.apache.myfaces.tobago.component.Attributes;
import org.apache.myfaces.tobago.component.Visual;
import org.apache.myfaces.tobago.event.PageActionEvent;
@@ -43,28 +65,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import jakarta.el.ELContext;
-import jakarta.el.MethodExpression;
-import jakarta.el.ValueExpression;
-import jakarta.faces.component.UIColumn;
-import jakarta.faces.component.UIComponent;
-import jakarta.faces.component.UINamingContainer;
-import jakarta.faces.component.behavior.AjaxBehavior;
-import jakarta.faces.component.behavior.ClientBehavior;
-import jakarta.faces.component.behavior.ClientBehaviorContext;
-import jakarta.faces.component.behavior.ClientBehaviorHolder;
-import jakarta.faces.component.visit.VisitCallback;
-import jakarta.faces.component.visit.VisitContext;
-import jakarta.faces.component.visit.VisitHint;
-import jakarta.faces.component.visit.VisitResult;
-import jakarta.faces.context.FacesContext;
-import jakarta.faces.event.AbortProcessingException;
-import jakarta.faces.event.ComponentSystemEvent;
-import jakarta.faces.event.ComponentSystemEventListener;
-import jakarta.faces.event.FacesEvent;
-import jakarta.faces.event.ListenerFor;
-import jakarta.faces.event.PhaseId;
-import jakarta.faces.event.PreRenderComponentEvent;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
@@ -95,6 +95,7 @@ public abstract class AbstractUISheet extends AbstractUIData
private transient boolean autoLayout;
private transient boolean lazyUpdate;
private transient int lazyFirstRow;
+ private transient int lazyLastRow;
private transient Grid headerGrid;
@@ -133,7 +134,9 @@ public void encodeBegin(final FacesContext facesContext) throws IOException {
}
if (isLazy() && !getLazyUpdate()) {
- setLazyFirstRow(theState.getLazyScrollPosition().getFirstVisibleRow());
+ Integer firstVisibleRow = theState.getLazyScrollPosition().getFirstVisibleRow();
+ setLazyFirstRow(Math.max(0, firstVisibleRow - Math.max(0, getLazyRows() / 2)));
+ setLazyLastRow(firstVisibleRow + Math.max(1, getLazyRows()));
}
super.encodeBegin(facesContext);
@@ -865,6 +868,14 @@ public void setLazyFirstRow(int lazyFirstRow) {
this.lazyFirstRow = lazyFirstRow;
}
+ public int getLazyLastRow() {
+ return lazyLastRow;
+ }
+
+ public void setLazyLastRow(int lazyLastRow) {
+ this.lazyLastRow = lazyLastRow;
+ }
+
public AjaxBehavior createReloadBehavior(final AbstractUISheet sheet) {
final AjaxBehavior reloadBehavior = findReloadBehavior(sheet);
final ArrayList<String> renderIds = new ArrayList<>();
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
index 097cd3331d..b3314eb920 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
@@ -154,6 +154,10 @@ public void decodeInternal(final FacesContext facesContext, final T component) {
if (requestParameterMap.containsKey(key)) {
component.setLazyFirstRow(Integer.parseInt(requestParameterMap.get(key)));
}
+ key = "tobago.sheet.lazyLastRow";
+ if (requestParameterMap.containsKey(key)) {
+ component.setLazyLastRow(Integer.parseInt(requestParameterMap.get(key)));
+ }
facesContext.renderResponse();
}
}
@@ -530,7 +534,7 @@ private void encodeTableBody(
boolean emptySheet = true;
// rows = 0 means: show all
final int first = sheet.isLazy() ? sheet.getLazyFirstRow() : sheet.getFirst();
- final int last = sheet.isLazy() ? sheet.getLazyFirstRow() + sheet.getLazyRows()
+ final int last = sheet.isLazy() ? sheet.getLazyLastRow()
: sheet.isRowsUnlimited() ? Integer.MAX_VALUE : sheet.getFirst() + sheet.getRows();
AbstractUIRow row = null;
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java
index b0fd5eee03..8d90021bff 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java
@@ -19,6 +19,7 @@
package org.apache.myfaces.tobago.internal.taglib.component;
+import jakarta.faces.component.UIData;
import org.apache.myfaces.tobago.apt.annotation.Behavior;
import org.apache.myfaces.tobago.apt.annotation.BodyContentDescription;
import org.apache.myfaces.tobago.apt.annotation.DynamicExpression;
@@ -40,8 +41,6 @@
import org.apache.myfaces.tobago.layout.ShowPosition;
import org.apache.myfaces.tobago.model.Selectable;
-import jakarta.faces.component.UIData;
-
/**
* Render a sheet element.
*/
@@ -295,7 +294,8 @@ public interface SheetTagDeclaration
void setLazy(String lazy);
/**
- * Indicate how many rows are loaded at once when lazy loading is enabled. Default is 50.
+ * Indicate how many rows must be loaded when lazy loading is enabled and how many rows are loaded at once.
+ * Default is 50.
*/
@TagAttribute
@UIComponentTagAttribute(type = "java.lang.Integer", defaultValue = "50")
@@ -391,10 +391,8 @@ public interface SheetTagDeclaration
* doesn't require updates potentially saving processing time.
* This optimization should only be applied when there are no non-readonly
* {@link jakarta.faces.component.EditableValueHolder} components in the sheet rows.
- *
*/
@TagAttribute
@UIComponentTagAttribute(type = "boolean", defaultValue = "false")
void setReadonlyRows(String readonly);
-
}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/80-lazy-sort/Lazy_Sort.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/80-lazy-sort/Lazy_Sort.test.js
index bb88b7f15c..e1067815c5 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/80-lazy-sort/Lazy_Sort.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/80-lazy-sort/Lazy_Sort.test.js
@@ -18,89 +18,70 @@
import {elementByIdFn, querySelectorAllFn, querySelectorFn} from "/script/tobago-test.js";
import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
-it("focus row index 5 (min row index is 0) and scroll up", function (done) {
+it("focus row index 25 and scroll up", function (done) {
const submit = elementByIdFn("page:mainForm:submit");
- let timestamp;
const test = new JasmineTestTool(done);
test.do(() => focusRowIndex(100));
test.wait(() => isLoaded(100));
- test.setup(() => isLoaded(100) && !isLoaded(5), () => focusRowIndex(100), "click", submit);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 1000);
- test.do(() => focusRowIndex(5));
- test.wait(() => isLoaded(5));
+ test.setup(() => isLoaded(100) && !isLoaded(0) && !isLoaded(15), () => focusRowIndex(100), "click", submit);
+ test.do(() => focusRowIndex(15)); //visible rows: 15 to 28 (new area)
+ test.wait(() => isLoaded(15));
test.do(() => expect(isLoaded(4)).toBeFalse());
- test.do(() => expect(isLoaded(5, 24)).toBeTrue());
- test.do(() => expect(isLoaded(25)).toBeFalse());
+ test.do(() => expect(isLoaded(5, 38)).toBeTrue());
+ test.do(() => expect(isLoaded(39)).toBeFalse());
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.do(() => focusRowIndex(4));
+ test.do(() => focusRowIndex(8)); //visible rows: 8 to 17 (known area)
test.wait(() => isLoaded(4));
- test.do(() => expect(isLoaded(0, 24)).toBeTrue());
- test.do(() => expect(isLoaded(25)).toBeFalse());
+ test.do(() => expect(isLoaded(0, 38)).toBeTrue());
+ test.do(() => expect(isLoaded(39)).toBeFalse());
test.start();
});
it("focus row index 9970 (max row index is 9999) and scroll down", function (done) {
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.do(() => focusRowIndex(9970));
+ test.do(() => focusRowIndex(9970)); //visible rows: 9970 to 9983 (new area)
test.wait(() => isLoaded(9970));
- test.do(() => expect(isLoaded(9969)).toBeFalse());
- test.do(() => expect(isLoaded(9970, 9989)).toBeTrue());
- test.do(() => expect(isLoaded(9990)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.do(() => focusRowIndex(9971));
- test.wait(() => isLoaded(9990));
- test.do(() => expect(isLoaded(9969)).toBeFalse());
- test.do(() => expect(isLoaded(9970, 9999)).toBeTrue());
+ test.do(() => expect(isLoaded(9959)).toBeFalse());
+ test.do(() => expect(isLoaded(9960, 9993)).toBeTrue());
+ test.do(() => expect(isLoaded(9994)).toBeFalse());
+
+ test.do(() => focusRowIndex(9980)); //visible rows: 9980 to 9989 (known area)
+ test.wait(() => isLoaded(9999));
+ test.do(() => expect(isLoaded(9959)).toBeFalse());
+ test.do(() => expect(isLoaded(9960, 9999)).toBeTrue());
test.start();
});
it("focus row index 500 and go up", function (done) {
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.do(() => focusRowIndex(500));
+ test.do(() => focusRowIndex(500)); //visible rows: 500 to 513 (new area)
test.wait(() => isLoaded(500));
- test.do(() => expect(isLoaded(499)).toBeFalse());
- test.do(() => expect(isLoaded(500, 519)).toBeTrue());
- test.do(() => expect(isLoaded(520)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.do(() => focusRowIndex(499));
- test.wait(() => isLoaded(499));
- test.do(() => expect(isLoaded(479)).toBeFalse());
- test.do(() => expect(isLoaded(480, 519)).toBeTrue());
- test.do(() => expect(isLoaded(520)).toBeFalse());
+ test.do(() => expect(isLoaded(489)).toBeFalse());
+ test.do(() => expect(isLoaded(490, 523)).toBeTrue());
+ test.do(() => expect(isLoaded(524)).toBeFalse());
+
+ test.do(() => focusRowIndex(494)); //visible rows: 494 to 503 (known area)
+ test.wait(() => isLoaded(489));
+ test.do(() => expect(isLoaded(469)).toBeFalse());
+ test.do(() => expect(isLoaded(470, 523)).toBeTrue());
+ test.do(() => expect(isLoaded(524)).toBeFalse());
test.start();
});
it("focus row index 1000 and go down", function (done) {
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => focusRowIndex(1000));
+ test.do(() => focusRowIndex(1000)); //visible rows: 1000 to 1013 (new area)
test.wait(() => isLoaded(1000));
- test.do(() => expect(isLoaded(999)).toBeFalse());
- test.do(() => expect(isLoaded(1000, 1019)).toBeTrue());
- test.do(() => expect(isLoaded(1020)).toBeFalse());
-
- test.do(() => focusRowIndex(1001));
- test.wait(() => isLoaded(1020));
- test.do(() => expect(isLoaded(999)).toBeFalse());
- test.do(() => expect(isLoaded(1000, 1039)).toBeTrue());
- test.do(() => expect(isLoaded(1040)).toBeFalse());
+ test.do(() => expect(isLoaded(989)).toBeFalse());
+ test.do(() => expect(isLoaded(990, 1023)).toBeTrue());
+ test.do(() => expect(isLoaded(1024)).toBeFalse());
+
+ test.do(() => focusRowIndex(1010)); //visible rows: 1010 to 1019 (known area)
+ test.wait(() => isLoaded(1024));
+ test.do(() => expect(isLoaded(989)).toBeFalse());
+ test.do(() => expect(isLoaded(990, 1043)).toBeTrue());
+ test.do(() => expect(isLoaded(1044)).toBeFalse());
test.start();
});
@@ -108,38 +89,34 @@ it("focus row index 1500 and sort name asc, focus row index 2000 and sort desc",
const nameColumn = elementByIdFn("page:mainForm:sheet:nameColumn_sorter");
const nameOutputs = querySelectorAllFn("tr tobago-out[id$=':name']");
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
test.do(() => focusRowIndex(1500));
test.wait(() => isLoaded(1500));
- test.do(() => expect(isLoaded(1499)).toBeFalse());
- test.do(() => expect(isLoaded(1500, 1519)).toBeTrue());
- test.do(() => expect(isLoaded(1520)).toBeFalse());
+ test.do(() => expect(isLoaded(1489)).toBeFalse());
+ test.do(() => expect(isLoaded(1490, 1523)).toBeTrue());
+ test.do(() => expect(isLoaded(1524)).toBeFalse());
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.event("click", nameColumn, () => nameOutputs().length === 40);
- test.do(() => expect(nameOutputs().length).toEqual(40));
+ test.event("click", nameColumn, () => nameColumn().classList.contains("tobago-ascending"));
+ test.wait(() => isLoaded(1500));
+ test.do(() => expect(isLoaded(1489)).toBeFalse());
+ test.do(() => expect(isLoaded(1490, 1519)).toBeTrue()); //preloaded from server
+ test.do(() => expect(isLoaded(1520)).toBeFalse());
+ test.do(() => expect(nameOutputs().length).toEqual(30));
test.do(() => expect(nameOutputs()[0].id).not.toEqual("page:mainForm:sheet:0:name"));
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.do(() => focusRowIndex(2000));
+ test.do(() => focusRowIndex(2000)); //visible rows: 2000 to 2013 (new area)
test.wait(() => isLoaded(2000));
- test.do(() => expect(isLoaded(1999)).toBeFalse());
- test.do(() => expect(isLoaded(2000, 2019)).toBeTrue());
- test.do(() => expect(isLoaded(2020)).toBeFalse());
+ test.do(() => expect(isLoaded(1989)).toBeFalse());
+ test.do(() => expect(isLoaded(1990, 2023)).toBeTrue());
+ test.do(() => expect(isLoaded(2024)).toBeFalse());
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 100);
- test.event("click", nameColumn, () => nameOutputs().length === 40);
- test.do(() => expect(nameOutputs().length).toEqual(40));
- test.do(() => expect(isLoaded(1979)).toBeFalse());
- test.do(() => expect(isLoaded(1980, 2019)).toBeTrue());
+ test.event("click", nameColumn, () => nameColumn().classList.contains("tobago-descending"));
+ test.wait(() => isLoaded(2000));
+ test.do(() => expect(isLoaded(1989)).toBeFalse());
+ test.do(() => expect(isLoaded(1990, 2019)).toBeTrue()); //preloaded from server
test.do(() => expect(isLoaded(2020)).toBeFalse());
+ test.do(() => expect(nameOutputs().length).toEqual(30));
+ test.do(() => expect(nameOutputs()[0].id).not.toEqual("page:mainForm:sheet:0:name"));
test.start();
});
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/82-lazy-2/Lazy_2.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/82-lazy-2/Lazy_2.test.js
index 8ebbae682b..b147058cd0 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/82-lazy-2/Lazy_2.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/82-lazy-2/Lazy_2.test.js
@@ -31,30 +31,25 @@ it("initial load", function (done) {
it("focus row index 500 and scroll up", function (done) {
const reset = elementByIdFn("page:mainForm:reset");
- let timestamp;
const test = new JasmineTestTool(done);
test.setup(() => !isLoaded(500), null, "click", reset);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(500));
+ test.do(() => focusRowIndex(500)); //new area; rows 500 to 513 visible; range 485 to 528
test.wait(() => isLoaded(500));
- test.do(() => expect(isLoaded(499)).toBeFalse());
- test.do(() => expect(isLoaded(500, 529)).toBeTrue());
- test.do(() => expect(isLoaded(530)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(499));
- test.wait(() => isLoaded(470));
- test.do(() => expect(isLoaded(469)).toBeFalse());
- test.do(() => expect(isLoaded(470, 529)).toBeTrue());
- test.do(() => expect(isLoaded(530)).toBeFalse());
+ test.do(() => expect(isLoaded(484)).toBeFalse());
+ test.do(() => expect(isLoaded(485, 528)).toBeTrue());
+ test.do(() => expect(isLoaded(529)).toBeFalse());
+
+ test.do(() => focusRowIndex(494)); //discovered area: 494 to 504 visible; range 484 to 514
+ test.wait(() => isLoaded(484));
+ test.do(() => expect(isLoaded(454)).toBeFalse());
+ test.do(() => expect(isLoaded(455, 528)).toBeTrue());
+ test.do(() => expect(isLoaded(529)).toBeFalse());
test.start();
});
-it("focus row index 2, select row 1 and 40, then press 'Period'", function (done) {
+it("scroll down, select row 1 and 40, then press 'Period'", function (done) {
const row1 = querySelectorFn("tbody tr[row-index='1']");
const row40 = querySelectorFn("tbody tr[row-index='40']");
const selectedInput = elementByIdFn("page:mainForm:sheet::selected");
@@ -63,20 +58,15 @@ it("focus row index 2, select row 1 and 40, then press 'Period'", function (done
const selectedRows = querySelectorFn("#page\\:mainForm\\:selectedRows .form-control-plaintext");
const actionCountOut = querySelectorFn("#page\\:mainForm\\:actionCount .form-control-plaintext");
const actionListenerCountOut = querySelectorFn("#page\\:mainForm\\:actionListenerCount .form-control-plaintext");
- let timestamp;
let actionCount = Number(actionCountOut().textContent);
let actionListenerCount = Number(actionListenerCountOut().textContent);
const test = new JasmineTestTool(done);
- test.setup(() => isLoaded(0) && selectedInput().value === "[]", null, "click", resetButton);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(2));
+ test.setup(() => isLoaded(0) && !isLoaded(30) && selectedInput().value === "[]", null, "click", resetButton);
+ test.do(() => focusRowIndex(12)); //discovered area: rows 12 to 21 visible; range 2 to 31
test.wait(() => isLoaded(30));
test.do(() => expect(isLoaded(0, 59)).toBeTrue());
test.do(() => expect(isLoaded(60)).toBeFalse());
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
test.event("click", row1, () => selectedInput().value === "[1]");
test.event("click", row40, () => selectedInput().value === "[1,40]");
test.event("click", periodButton, () => Number(actionCountOut().textContent) > actionCount);
@@ -94,18 +84,16 @@ it("focus row index 800, press 'Ajax'", function (done) {
const test = new JasmineTestTool(done);
test.setup(() => !isLoaded(800), null, "click", reset);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(800));
+ test.do(() => focusRowIndex(800)); //new area: rows 800 to 813 visible; range 785 to 828
test.wait(() => isLoaded(800));
- test.do(() => expect(isLoaded(799)).toBeFalse());
- test.do(() => expect(isLoaded(800, 829)).toBeTrue());
- test.do(() => expect(isLoaded(830)).toBeFalse());
+ test.do(() => expect(isLoaded(784)).toBeFalse());
+ test.do(() => expect(isLoaded(785, 828)).toBeTrue());
+ test.do(() => expect(isLoaded(829)).toBeFalse());
test.do(() => expect(getFirstVisibleRow()).toBe(800));
test.do(() => timestamp = Number(timestampOut().textContent));
test.event("click", ajax, () => Number(timestampOut().textContent) > timestamp);
- test.do(() => expect(isLoaded(800, 829)).toBeTrue());
+ test.do(() => expect(isLoaded(785, 829)).toBeTrue()); //preloaded
test.do(() => expect(isLoaded(830)).toBeFalse());
test.do(() => expect(getFirstVisibleRow()).toBe(800));
test.start();
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/83-lazy-columnPanel/Lazy_ColumnPanel.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/83-lazy-columnPanel/Lazy_ColumnPanel.test.js
index 089dbeeb02..16b7858096 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/83-lazy-columnPanel/Lazy_ColumnPanel.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/83-lazy-columnPanel/Lazy_ColumnPanel.test.js
@@ -15,14 +15,14 @@
* limitations under the License.
*/
-import {elementByIdFn, isFirefox, querySelectorFn} from "/script/tobago-test.js";
+import {elementByIdFn, querySelectorFn} from "/script/tobago-test.js";
import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
it("initial load", function (done) {
const submit = elementByIdFn("page:mainForm:submit");
const test = new JasmineTestTool(done);
- test.setup(() => isLoaded(0) && isLoaded(9), null, "click", submit);
+ test.setup(() => isLoaded(0) && isLoaded(9), () => focusRowIndex(0), "click", submit);
test.do(() => expect(isLoaded(0)).toBeTrue());
test.do(() => expect(isLoaded(9)).toBeTrue());
test.do(() => expect(isLoaded(10)).toBeFalse());
@@ -30,65 +30,34 @@ it("initial load", function (done) {
});
it("focus row index 200 and scroll up", function (done) {
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(200));
+ test.do(() => focusRowIndex(200)); //new area; 200 to 213 visible; range 195 to 218
test.wait(() => isLoaded(200));
+ test.do(() => expect(isLoaded(194)).toBeFalse());
+ test.do(() => expect(isLoaded(195, 218)).toBeTrue());
+ test.do(() => expect(isLoaded(219)).toBeFalse());
- if (isFirefox()) {
- test.wait(() => isLoaded(199));
- test.do(() => expect(isLoaded(189)).toBeFalse());
- test.do(() => expect(isLoaded(190, 199)).toBeTrue());
- } else {
- test.do(() => expect(isLoaded(199)).toBeFalse());
- }
- test.do(() => expect(isLoaded(200, 209)).toBeTrue());
- test.do(() => expect(isLoaded(210)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(199));
- test.wait(() => isLoaded(199));
- test.do(() => expect(isLoaded(189)).toBeFalse());
- test.do(() => expect(isLoaded(190, 209)).toBeTrue());
- test.do(() => expect(isLoaded(210)).toBeFalse());
+ test.do(() => focusRowIndex(197)); //discovered area; 197 to 200 visible; range 194 to 203
+ test.wait(() => isLoaded(194));
+ test.do(() => expect(isLoaded(184)).toBeFalse());
+ test.do(() => expect(isLoaded(185, 218)).toBeTrue());
+ test.do(() => expect(isLoaded(219)).toBeFalse());
test.start();
});
it("focus row index 300 and scroll down", function (done) {
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(300));
+ test.do(() => focusRowIndex(300)); //new area; 300 to 313 visible; range 295 to 318
test.wait(() => isLoaded(300));
+ test.do(() => expect(isLoaded(294)).toBeFalse());
+ test.do(() => expect(isLoaded(295, 318)).toBeTrue());
+ test.do(() => expect(isLoaded(319)).toBeFalse());
- if (isFirefox()) {
- test.wait(() => isLoaded(299));
- test.do(() => expect(isLoaded(289)).toBeFalse());
- test.do(() => expect(isLoaded(290, 299)).toBeTrue());
- } else {
- test.do(() => expect(isLoaded(299)).toBeFalse());
- }
- test.do(() => expect(isLoaded(300, 309)).toBeTrue());
- test.do(() => expect(isLoaded(310)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(301));
- test.wait(() => isLoaded(310));
- if (isFirefox()) {
- test.do(() => expect(isLoaded(289)).toBeFalse());
- test.do(() => expect(isLoaded(290, 299)).toBeTrue());
- } else {
- test.do(() => expect(isLoaded(299)).toBeFalse());
- }
- test.do(() => expect(isLoaded(300, 319)).toBeTrue());
- test.do(() => expect(isLoaded(320)).toBeFalse());
+ test.do(() => focusRowIndex(313)); //discovered area; 313 to 316 visible; range 310 to 319
+ test.wait(() => isLoaded(319));
+ test.do(() => expect(isLoaded(294)).toBeFalse());
+ test.do(() => expect(isLoaded(295, 328)).toBeTrue());
+ test.do(() => expect(isLoaded(329)).toBeFalse());
test.start();
});
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.test.js
index 335cea3cf1..840e58ee5d 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.test.js
@@ -17,7 +17,7 @@
* under the License.
*/
-import {elementByIdFn, querySelectorFn} from "/script/tobago-test.js";
+import {elementByIdFn, querySelectorAllFn, querySelectorFn} from "/script/tobago-test.js";
import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
it("TOBAGO-2349: Lazy sheet: tobago-behavior not initialized", function (done) {
@@ -35,16 +35,17 @@ it("TOBAGO-2349: Lazy sheet: tobago-behavior not initialized", function (done) {
let timestamp;
const test = new JasmineTestTool(done);
+ test.setup(() => getFirstVisibleRow() === 0, null, "click", reset);
test.setup(() => checkAjax().checked, () => selectAjax().checked = true, "change", selectAjax);
test.setup(() => checkSelectable().value === "multi", () => selectMulti().checked = true, "change", selectMulti);
test.setup(() => name().value === "Sun", null, "click", reset);
- test.do(() => focusRowIndex(18));
- test.wait(() => isLoaded(18, 67));
- test.do(() => expect(isLoaded(18, 67)).toBeTrue());
+ test.do(() => focusRowIndex(30)); //discovered area; visible rows 30 to 34; range 5 to 59
+ test.wait(() => isLoaded(50));
+ test.do(() => expect(isLoaded(0, 87)).toBeTrue()); //loaded to row 99, but max row is 87
test.do(() => timestamp = Number(timestampElement().textContent));
test.event("click", ajaxButton, () => Number(timestampElement().textContent) > timestamp);
- test.wait(() => isLoaded(18, 67));
+ test.wait(() => isLoaded(5, 69));
test.event("click", europaRow, () => name().value === "Europa");
test.do(() => expect(name().value).toBe("Europa"));
test.do(() => timestamp = Number(timestampElement().textContent));
@@ -73,3 +74,19 @@ function row(rowIndex) {
function focusRowIndex(rowIndex) {
querySelectorFn("#page\\:mainForm\\:solar .tobago-body")().scrollTop = row(rowIndex).offsetTop;
}
+
+function getFirstVisibleRow() {
+ const tobagoBodyScrollTop = querySelectorFn("tobago-sheet .tobago-body")().scrollTop;
+ const rows = querySelectorAllFn("tobago-sheet tr[row-index]")();
+
+ let firstVisibleRow = 0;
+ for (const row of rows) {
+ if (row.offsetTop <= tobagoBodyScrollTop) {
+ firstVisibleRow = Number(row.getAttribute("row-index"));
+ } else {
+ return firstVisibleRow;
+ }
+ }
+
+ return -1;
+}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.xhtml
index 49d2b5ac6c..ee2a8bdd4a 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/84-lazy-selectable-use-case/lazy-selectable-use-case.xhtml
@@ -64,7 +64,7 @@
<tc:out value="#{object.name}" id="t_name" labelLayout="skip"/>
</tc:column>
<tc:row id="rowAjax" rendered="#{selectableUseCaseController.columnEvent == 'ajax'}">
- <f:ajax render=":::timestamp solar :::detail" listener="#{selectableUseCaseController.selectSolarObject}"
+ <f:ajax execute="solar" render=":::timestamp solar :::detail" listener="#{selectableUseCaseController.selectSolarObject}"
id="columnEventAjax"/>
</tc:row>
<tc:columnPanel>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.test.js
index ebc625c007..674c1c928c 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.test.js
@@ -31,30 +31,24 @@ it("initial load", function (done) {
it("focus row index 500 and scroll up", function (done) {
const reset = elementByIdFn("page:mainForm:reset");
- let timestamp;
const test = new JasmineTestTool(done);
- test.setup(() => !isLoaded(500), null, "click", reset);
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(500));
+ test.setup(() => !isLoaded(425) && !isLoaded(500) && !isLoaded(538), null, "click", reset);
+ test.do(() => focusRowIndex(500)); //new area; visible 500 to 513; range 475 to 538
test.wait(() => isLoaded(500));
- test.do(() => expect(isLoaded(499)).toBeFalse());
- test.do(() => expect(isLoaded(500, 549)).toBeTrue());
- test.do(() => expect(isLoaded(550)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(499));
- test.wait(() => isLoaded(450));
- test.do(() => expect(isLoaded(449)).toBeFalse());
- test.do(() => expect(isLoaded(450, 549)).toBeTrue());
- test.do(() => expect(isLoaded(550)).toBeFalse());
+ test.do(() => expect(isLoaded(474)).toBeFalse());
+ test.do(() => expect(isLoaded(475, 538)).toBeTrue());
+ test.do(() => expect(isLoaded(539)).toBeFalse());
+
+ test.do(() => focusRowIndex(493)); //discovered area; visible 493 to 503; range: 474 to 523
+ test.wait(() => isLoaded(474));
+ test.do(() => expect(isLoaded(424)).toBeFalse());
+ test.do(() => expect(isLoaded(425, 538)).toBeTrue());
+ test.do(() => expect(isLoaded(539)).toBeFalse());
test.start();
});
-it("focus row index 2, select row 1 and 60, then press 'Period'", function (done) {
+it("focus row index 20, select row 1 and 60, then press 'Period'", function (done) {
const row1 = querySelectorFn("tbody tr[row-index='1']");
const row40 = querySelectorFn("tbody tr[row-index='60']");
const selectedInput = elementByIdFn("page:mainForm:sheet::selected");
@@ -63,20 +57,15 @@ it("focus row index 2, select row 1 and 60, then press 'Period'", function (done
const selectedRows = querySelectorFn("#page\\:mainForm\\:selectedRows .form-control-plaintext");
const actionCountOut = querySelectorFn("#page\\:mainForm\\:actionCount .form-control-plaintext");
const actionListenerCountOut = querySelectorFn("#page\\:mainForm\\:actionListenerCount .form-control-plaintext");
- let timestamp;
let actionCount = Number(actionCountOut().textContent);
let actionListenerCount = Number(actionListenerCountOut().textContent);
const test = new JasmineTestTool(done);
- test.setup(() => isLoaded(0) && selectedInput().value === "[]", null, "click", resetButton);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(2));
+ test.setup(() => isLoaded(0) && !isLoaded(50) && selectedInput().value === "[]", null, "click", resetButton);
+ test.do(() => focusRowIndex(23)); //visible: 23 to 33; range: 4 to 53;
test.wait(() => isLoaded(50));
test.do(() => expect(isLoaded(0, 99)).toBeTrue());
test.do(() => expect(isLoaded(100)).toBeFalse());
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
test.event("click", row1, () => selectedInput().value === "[1]");
test.event("click", row40, () => selectedInput().value === "[1,60]");
test.event("click", periodButton, () => Number(actionCountOut().textContent) > actionCount);
@@ -94,18 +83,17 @@ it("focus row index 800, press 'Ajax'", function (done) {
const test = new JasmineTestTool(done);
test.setup(() => !isLoaded(800), null, "click", reset);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(800));
+ test.do(() => focusRowIndex(800)); //new area; visible 800 to 813; range 775 to 838
test.wait(() => isLoaded(800));
- test.do(() => expect(isLoaded(799)).toBeFalse());
- test.do(() => expect(isLoaded(800, 849)).toBeTrue());
- test.do(() => expect(isLoaded(850)).toBeFalse());
+ test.do(() => expect(isLoaded(774)).toBeFalse());
+ test.do(() => expect(isLoaded(775, 838)).toBeTrue());
+ test.do(() => expect(isLoaded(839)).toBeFalse());
test.do(() => expect(getFirstVisibleRow()).toBe(800));
test.do(() => timestamp = Number(timestampOut().textContent));
test.event("click", ajax, () => Number(timestampOut().textContent) > timestamp);
- test.do(() => expect(isLoaded(800, 849)).toBeTrue());
+ test.do(() => expect(isLoaded(774)).toBeFalse());
+ test.do(() => expect(isLoaded(775, 849)).toBeTrue());
test.do(() => expect(isLoaded(850)).toBeFalse());
test.do(() => expect(getFirstVisibleRow()).toBe(800));
test.start();
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.xhtml
index 18fd41c168..b9a4c75827 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/82-lazy-2/Lazy_2.xhtml
@@ -22,7 +22,8 @@
xmlns:f="jakarta.faces.core"
xmlns:tc="http://myfaces.apache.org/tobago/component"
xmlns:ui="jakarta.faces.facelets">
- <p>This is the "Lazy 2" test just with "rows" attribute.</p>
+ <p>This is the "Lazy 2" test just with "rows" attribute. The rows attribute is ignored.
+ Use default value of "lazyRows" attribute.</p>
<tc:button id="submit" label="submit"/>
<tc:button id="ajax" label="Ajax">
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.test.js
index e12fb0b8ad..c7382232c8 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.test.js
@@ -15,69 +15,38 @@
* limitations under the License.
*/
-import {isFirefox, querySelectorFn} from "/script/tobago-test.js";
+import {querySelectorFn} from "/script/tobago-test.js";
import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
it("focus row index 200 and scroll up", function (done) {
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(200));
+ test.do(() => focusRowIndex(200)); //new area; visible: 200 to 213; range: 175 to 238
test.wait(() => isLoaded(200));
+ test.do(() => expect(isLoaded(174)).toBeFalse());
+ test.do(() => expect(isLoaded(175, 238)).toBeTrue());
+ test.do(() => expect(isLoaded(239)).toBeFalse());
- if (isFirefox()) {
- test.wait(() => isLoaded(199));
- test.do(() => expect(isLoaded(149)).toBeFalse());
- test.do(() => expect(isLoaded(150, 199)).toBeTrue());
- } else {
- test.do(() => expect(isLoaded(199)).toBeFalse());
- }
- test.do(() => expect(isLoaded(200, 249)).toBeTrue());
- test.do(() => expect(isLoaded(250)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(199));
- test.wait(() => isLoaded(199));
- test.do(() => expect(isLoaded(149)).toBeFalse());
- test.do(() => expect(isLoaded(150, 249)).toBeTrue());
- test.do(() => expect(isLoaded(250)).toBeFalse());
+ test.do(() => focusRowIndex(189)); //discovered area; visible 189 to 202; range: 174 to 217
+ test.wait(() => isLoaded(174));
+ test.do(() => expect(isLoaded(124)).toBeFalse());
+ test.do(() => expect(isLoaded(125, 238)).toBeTrue());
+ test.do(() => expect(isLoaded(239)).toBeFalse());
test.start();
});
it("focus row index 400 and scroll down", function (done) {
- let timestamp;
-
const test = new JasmineTestTool(done);
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(400));
+ test.do(() => focusRowIndex(400)); //new area; visible: 400 to 413; range: 375 to 438
test.wait(() => isLoaded(400));
+ test.do(() => expect(isLoaded(374)).toBeFalse());
+ test.do(() => expect(isLoaded(375, 438)).toBeTrue());
+ test.do(() => expect(isLoaded(439)).toBeFalse());
- if (isFirefox()) {
- test.wait(() => isLoaded(399));
- test.do(() => expect(isLoaded(349)).toBeFalse());
- test.do(() => expect(isLoaded(350, 399)).toBeTrue());
- } else {
- test.do(() => expect(isLoaded(399)).toBeFalse());
- }
- test.do(() => expect(isLoaded(400, 449)).toBeTrue());
- test.do(() => expect(isLoaded(450)).toBeFalse());
-
- test.do(() => timestamp = Date.now());
- test.wait(() => Date.now() - timestamp > 200);
- test.do(() => focusRowIndex(401));
- test.wait(() => isLoaded(450));
- if (isFirefox()) {
- test.do(() => expect(isLoaded(349)).toBeFalse());
- test.do(() => expect(isLoaded(350, 399)).toBeTrue());
- } else {
- test.do(() => expect(isLoaded(399)).toBeFalse());
- }
- test.do(() => expect(isLoaded(400, 499)).toBeTrue());
- test.do(() => expect(isLoaded(500)).toBeFalse());
+ test.do(() => focusRowIndex(411)); //discovered area; visible 411 to 424; range: 396 to 439
+ test.wait(() => isLoaded(439));
+ test.do(() => expect(isLoaded(374)).toBeFalse());
+ test.do(() => expect(isLoaded(375, 488)).toBeTrue());
+ test.do(() => expect(isLoaded(489)).toBeFalse());
test.start();
});
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.xhtml
index 2c10249596..a121bcc737 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/85-lazy-row-attribute/lazy-row-attribute.xhtml
@@ -20,6 +20,8 @@
xmlns:tc="http://myfaces.apache.org/tobago/component"
xmlns:ui="jakarta.faces.facelets">
+ <p>"rows" attribute is used instead of "lazyRows". Ignore "rows" and use default value of "lazyRows" attribute.</p>
+
<tc:out id="timestamp" label="Timestamp" value="#{eventController.currentTimestamp}"/>
<tc:sheet id="sheet" value="#{sheetController.hugeSolarList}"
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
index 8e08e56a0b..f2518f9526 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
@@ -54,6 +54,7 @@ export class Sheet extends HTMLElement {
lastCheckMillis: number;
+ private lazyCheckInterval;
private lazyActive: boolean;
private sheetLoader: HTMLElement;
@@ -144,15 +145,16 @@ export class Sheet extends HTMLElement {
tableBody.insertAdjacentHTML("beforeend", Sheet.getDummyRowTemplate(columns, i));
}
- this.body.addEventListener("scroll", this.lazyCheck.bind(this));
+ this.body.addEventListener("scroll", this.activateLazyCheckInterval.bind(this));
const lazyScrollPosition = this.lazyScrollPosition;
- const firstVisibleRow
- = this.tableBody.querySelector<HTMLTableRowElement>(`tr[row-index='${lazyScrollPosition[0]}']`);
+ const firstVisibleRow = this.getRowElement(lazyScrollPosition[0]);
if (firstVisibleRow) {
this.body.scrollTop = firstVisibleRow.offsetTop + lazyScrollPosition[1];
//in Firefox setting "scrollTop" triggers scroll event -> lazyCheck()
}
+
+ this.lazyCheck();
}
// init paging by pages ---------------------------------------------------------------------------------------- //
@@ -182,42 +184,44 @@ export class Sheet extends HTMLElement {
// -------------------------------------------------------------------------------------- //
/*
- when an event occurs (initial load OR scroll event OR AJAX response)
-
- then -> Tobago.Sheet.lazyCheck()
- 1. check, if the lazy reload is currently active
- a) yes -> do nothing and exit
- b) no -> step 2.
- 2. check, if there are data need to load (depends on scroll position and already loaded data)
- a) yes -> set lazy reload to active and make an AJAX request with Tobago.Sheet.reloadLazy()
- b) no -> do nothing and exit
+ When a scroll event occurs, activateLazyCheckInterval, which triggers lazyCheck() every 100ms till all rows are
+ loaded.
+
+ On initial load OR on lazyCheckInterval:
+ -> Tobago.Sheet.lazyCheck()
+ 1. check, if the the lazy reload is currently active
+ a) yes -> do nothing
+ b) no -> step 2.
+ 2. check, if there are data need to load (depends on scroll position and already loaded data)
+ a) yes -> set lazy reload to active and make an AJAX request with Tobago.Sheet.reloadLazy()
+ b) no -> do nothing and stop lazyCheckInterval
AJAX response -> 1. update the rows in the sheet from the response
2. go to the first part of this description
*/
+ private activateLazyCheckInterval(event: Event): void {
+ if (!this.lazyCheckInterval) {
+ this.lazyCheckInterval = setInterval(() => this.lazyCheck(), 100);
+ }
+ }
+
/**
* Checks if a lazy update is required, because there are unloaded rows in the visible area.
*/
lazyCheck(event?: Event): void {
if (this.lazyActive) {
- // nothing to do, because there is an active AJAX running
- return;
- }
-
- if (this.lastCheckMillis && Date.now() - this.lastCheckMillis < 100) {
- // do nothing, because the last call was just a moment ago
+ // nothing to do, because there is an active AJAX running; check again later
return;
}
- this.lastCheckMillis = Date.now();
- const next = this.nextLazyLoad();
- // console.info("next %o", next); // @DEV_ONLY
+ const next: { from: number; to: number } = this.nextLazyLoadInterval();
+ // console.info("next from=%o to=%o", next?.from, next?.to); // @DEV_ONLY
if (next !== null) {
this.lazyActive = true;
const rootNode = this.getRootNode() as ShadowRoot | Document;
const input = rootNode.getElementById(this.id + ":pageActionlazy") as HTMLInputElement;
- input.value = String(next + 1); //input.value rowIndex starts at 1 (not 0)
+ input.value = String(next.from + 1); //input.value rowIndex starts at 1 (not 0)
console.debug(`reload sheet with action '${input.id}'`); // @DEV_ONLY
faces.ajax.request(
@@ -226,20 +230,88 @@ export class Sheet extends HTMLElement {
{
params: {
"jakarta.faces.behavior.event": "lazy",
- "tobago.sheet.lazyFirstRow": next
+ "tobago.sheet.lazyFirstRow": next.from,
+ "tobago.sheet.lazyLastRow": next.to + 1 //to+1, because the to-row should also be loaded
},
execute: this.id,
render: this.id,
onevent: this.lazyResponse.bind(this),
onerror: this.lazyError.bind(this)
});
+ } else {
+ clearInterval(this.lazyCheckInterval);
+ delete this.lazyCheckInterval;
+ }
+ }
+
+ private nextLazyLoadInterval(): { from: number; to: number } {
+ const firstVisibleRowIndex = this.firstVisibleRowIndex;
+ const lastVisibleRowIndex = this.lastVisibleRowIndex;
+ const firstVisibleRow = this.getRowElement(firstVisibleRowIndex);
+ let from = this.getFirstDummyRowIndex(firstVisibleRow, lastVisibleRowIndex);
+ let to = this.getLastDummyRowIndex(firstVisibleRow, lastVisibleRowIndex);
+
+ const isNewArea = from === firstVisibleRowIndex && to === lastVisibleRowIndex;
+ const visibleRowCount = lastVisibleRowIndex - firstVisibleRowIndex + 1;
+ const remainingLazyRows = isNewArea ? this.lazyRows : Math.max(this.lazyRows - visibleRowCount, 0);
+ const rangeStart = firstVisibleRowIndex - Math.floor(remainingLazyRows / 2);
+ const rangeEnd = lastVisibleRowIndex + Math.ceil(remainingLazyRows / 2);
+
+ const startElement = this.getRowElement(Math.max(rangeStart, 0));
+ from = this.getFirstDummyRowIndex(startElement, rangeEnd);
+
+ if (from >= 0) {
+ const fromElement = this.getRowElement(from);
+ to = this.getLastDummyRowIndex(fromElement, Math.max(from + (this.lazyRows - 1), rangeEnd));
+
+ if (to - from < this.lazyRows - 1) {
+ from = this.getLastDummyRowIndex(fromElement, Math.min(to - (this.lazyRows - 1), rangeStart));
+ }
+ return {from, to};
+ } else {
+ return null;
+ }
+ }
+
+ private getFirstDummyRowIndex(row: HTMLTableRowElement, toRowIndex: number): number {
+ if (row) {
+ const rowIndex: number = this.getRowIndex(row);
+ if (row.hasAttribute("dummy")) {
+ return rowIndex;
+ } else if (rowIndex < toRowIndex) {
+ return this.getFirstDummyRowIndex(row.nextElementSibling as HTMLTableRowElement, toRowIndex);
+ } else if (rowIndex > toRowIndex) {
+ return this.getFirstDummyRowIndex(row.previousElementSibling as HTMLTableRowElement, toRowIndex);
+ }
}
+ return -1;
+ }
+
+ private getLastDummyRowIndex(row: HTMLTableRowElement, toRowIndex: number): number {
+ if (row) {
+ const rowIndex: number = this.getRowIndex(row);
+ if (row.hasAttribute("dummy")) {
+ if (rowIndex < toRowIndex) {
+ const next = this.getLastDummyRowIndex(row.nextElementSibling as HTMLTableRowElement, toRowIndex);
+ if (next > -1) {
+ return next;
+ }
+ } else if (rowIndex > toRowIndex) {
+ const previous = this.getLastDummyRowIndex(row.previousElementSibling as HTMLTableRowElement, toRowIndex);
+ if (previous > -1) {
+ return previous;
+ }
+ }
+ return rowIndex;
+ }
+ }
+ return -1;
}
nextLazyLoad(): number {
const rows = this.lazyRows;
const firstVisibleRowIndex = this.firstVisibleRowIndex;
- const firstVisibleRow = this.tableBody.querySelector(`tr[row-index='${firstVisibleRowIndex}']`);
+ const firstVisibleRow = this.getRowElement(firstVisibleRowIndex);
const maxNextLazyLoad = firstVisibleRowIndex + rows - 1;
const nextDummyRowIndex = this.getNextDummyRowIndex(firstVisibleRow.nextElementSibling, rows - 1);
@@ -313,16 +385,16 @@ export class Sheet extends HTMLElement {
for (const newRow of newRows) {
if (newRow.hasAttribute("row-index")) {
const rowIndex = Number(newRow.getAttribute("row-index"));
- const row = this.tableBody.querySelector(`tr[row-index='${rowIndex}']`);
- const previousElement = row.previousElementSibling;
+ const rowElement = this.getRowElement(rowIndex);
+ const previousElement = rowElement.previousElementSibling;
// first remove the old element and then add the new element
// otherwise eventlisteners cannot be registered
if (previousElement == null) {
- const parentElement = row.parentElement;
- row.remove();
+ const parentElement = rowElement.parentElement;
+ rowElement.remove();
parentElement.insertAdjacentElement("afterbegin", newRow);
} else {
- row.remove();
+ rowElement.remove();
previousElement.insertAdjacentElement("afterend", newRow);
}
} else if (newRow.classList.contains(Css.TOBAGO_COLUMN_PANEL)) {
@@ -333,8 +405,8 @@ export class Sheet extends HTMLElement {
columnPanel.remove();
previousElement.insertAdjacentElement("afterend", newRow);
} else {
- const row = this.tableBody.querySelector(`tr[row-index='${rowIndex}']`);
- row.insertAdjacentElement("afterend", newRow);
+ const rowElement = this.getRowElement(rowIndex);
+ rowElement.insertAdjacentElement("afterend", newRow);
}
}
@@ -346,8 +418,7 @@ export class Sheet extends HTMLElement {
}
const lazyScrollPosition = this.lazyScrollPosition;
- const firstRow
- = this.tableBody.querySelector<HTMLTableRowElement>(`tr[row-index='${lazyScrollPosition[0]}']`);
+ const firstRow = this.getRowElement(lazyScrollPosition[0]);
if (firstRow) {
this.body.scrollTop = firstRow.offsetTop + lazyScrollPosition[1];
//in Firefox setting "scrollTop" triggers scroll event -> lazyCheck()
@@ -357,15 +428,6 @@ export class Sheet extends HTMLElement {
}
}
- private getRowIndex(row: HTMLTableRowElement): number {
- if (row.hasAttribute("row-index")) {
- return Number(row.getAttribute("row-index"));
- } else if (row.classList.contains(Css.TOBAGO_COLUMN_PANEL)) {
- return Number(row.getAttribute("name"));
- }
- return null;
- }
-
lazyError(data: ErrorData): void {
console.error(`Sheet lazy loading error:
Error Name: ${data.errorName}
@@ -526,8 +588,7 @@ Type: ${data.type}`);
if (this.lazy) {
const firstVisibleRowIndex = this.firstVisibleRowIndex;
- const firstVisibleRow
- = this.tableBody.querySelector<HTMLTableRowElement>(`tr[row-index='${firstVisibleRowIndex}']`);
+ const firstVisibleRow = this.getRowElement(firstVisibleRowIndex);
const firstVisibleRowScrollTop = sheetBody.scrollTop - firstVisibleRow.offsetTop;
this.lazyScrollPosition = [firstVisibleRowIndex, Math.round(firstVisibleRowScrollTop), scrollLeft];
@@ -771,6 +832,19 @@ Type: ${data.type}`);
}
}
+ private getRowElement(index: number): HTMLTableRowElement {
+ return this.tableBody.querySelector<HTMLTableRowElement>(`tr[row-index='${index}']`);
+ }
+
+ private getRowIndex(row: HTMLTableRowElement): number {
+ if (row.hasAttribute("row-index")) {
+ return Number(row.getAttribute("row-index"));
+ } else if (row.classList.contains(Css.TOBAGO_COLUMN_PANEL)) {
+ return Number(row.getAttribute("name"));
+ }
+ return null;
+ }
+
public get selectable(): Selectable {
return Selectable[this.dataset.tobagoSelectionMode];
}
@@ -983,6 +1057,18 @@ Type: ${data.type}`);
return this.getRowIndex(rowElements[index]);
}
+
+ get lastVisibleRowIndex(): number {
+ const rowElements = this.tableBody.rows;
+ for (let i = this.firstVisibleRowIndex; i < rowElements.length; i++) {
+ if (rowElements[i].offsetTop > (this.body.scrollTop + this.body.offsetHeight)) {
+ const index = Math.max(0, i - 1);
+ return this.getRowIndex(rowElements[index]);
+ }
+ }
+
+ return this.getRowIndex(rowElements[rowElements.length - 1]);
+ }
}
document.addEventListener("tobago.init", function (event: Event): void {