From 060c8a0c6c13e4e0389e61bb10403927df508d46 Mon Sep 17 00:00:00 2001 From: "Ghislain B." Date: Fri, 26 Jul 2024 22:35:09 -0400 Subject: [PATCH] feat: add new Infinite Scroll example (#1040) * feat: add new Infinite Scroll example --- cypress/e2e/example-infinite-scroll-esm.cy.ts | 87 +++++++ examples/example-infinite-scroll-esm.html | 230 ++++++++++++++++++ examples/index.html | 1 + src/slick.grid.ts | 22 +- 4 files changed, 334 insertions(+), 6 deletions(-) create mode 100644 cypress/e2e/example-infinite-scroll-esm.cy.ts create mode 100644 examples/example-infinite-scroll-esm.html diff --git a/cypress/e2e/example-infinite-scroll-esm.cy.ts b/cypress/e2e/example-infinite-scroll-esm.cy.ts new file mode 100644 index 00000000..954a2e97 --- /dev/null +++ b/cypress/e2e/example-infinite-scroll-esm.cy.ts @@ -0,0 +1,87 @@ +describe('Example - Infinite Scroll', () => { + const GRID_ROW_HEIGHT = 25; + const titles = ['#', 'Title', 'Duration', '% Complete', 'Start', 'Finish', 'Effort Driven']; + + it('should display Example title', () => { + cy.visit(`${Cypress.config('baseUrl')}/examples/example-infinite-scroll-esm.html`); + cy.get('h2').contains('Demonstrates'); + cy.get('h2 + p').contains('This page demonstrates Infinite Scroll using DataView.'); + + cy.get('.grid-header > label') + .should('contain', 'SlickGrid - Infinite Scroll'); + }); + + it('should have exact Column Titles in the grid', () => { + cy.get('#myGrid') + .find('.slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(titles[index])); + }); + + it('should expect first row to include "Task 0" and other specific properties', () => { + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 0'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(3) .percent-complete-bar`).should('exist'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(4)`).should('contain', '01/01/2009'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(5)`).should('contain', '01/05/2009'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(6)`).find('.sgi.sgi-check').should('have.length', 1); + }); + + it('should scroll to bottom of the grid and expect next batch of 50 items appended to current dataset for a total of 100 items', () => { + cy.get('[data-test="itemCount"]') + .should('have.text', '50'); + + cy.get('.slick-viewport.slick-viewport-top.slick-viewport-left') + .scrollTo('bottom'); + + cy.get('[data-test="itemCount"]') + .should('have.text', '100'); + }); + + it('should scroll to bottom of the grid again and expect 50 more items for a total of now 150 items', () => { + cy.get('[data-test="itemCount"]') + .should('have.text', '100'); + + cy.get('.slick-viewport.slick-viewport-top.slick-viewport-left') + .scrollTo('bottom'); + + cy.get('[data-test="itemCount"]') + .should('have.text', '150'); + }); + + it('should disable onSort for data reset and expect same dataset length of 150 items after sorting by Title', () => { + cy.get('[data-test="onsort-off"]').click(); + + cy.get('[data-id="title"]') + .click(); + + cy.get('[data-test="itemCount"]') + .should('have.text', '150'); + + cy.get('.slick-viewport.slick-viewport-top.slick-viewport-left') + .scrollTo('top'); + + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 0'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 1'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 10'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 3}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 100'); + }); + + it('should enable onSort for data reset and expect dataset to be reset to 50 items after sorting by Title', () => { + cy.get('[data-test="onsort-on"]').click(); + + cy.get('[data-id="title"]') + .click(); + + cy.get('[data-test="itemCount"]') + .should('have.text', '50'); + + cy.get('.slick-viewport.slick-viewport-top.slick-viewport-left') + .scrollTo('top'); + + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 9'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 8'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 7'); + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 3}px;"] > .slick-cell:nth(1)`).should('contain', 'Task 6'); + }); +}); diff --git a/examples/example-infinite-scroll-esm.html b/examples/example-infinite-scroll-esm.html new file mode 100644 index 00000000..0abbb56d --- /dev/null +++ b/examples/example-infinite-scroll-esm.html @@ -0,0 +1,230 @@ + + + + + + SlickGrid example: Infinite Scroll + + + + + + +
+
+
+ +
+
+
+ +
+

+ + Demonstrates: +

+ +

+ This page demonstrates Infinite Scroll using DataView. +

    +
  • Infinite scrolling allows the grid to lazy-load rows from the server (or locally) when reaching the scroll bottom (end) position.
  • +
  • In its simplest form, the more the user scrolls down, the more rows will get loaded and appended to the in-memory dataset.
  • +
  • This demo will keep loading and adding data indifinitely, however in most cases you will eventually reach the end of your dataset and have everything loaded in memory.
  • +
  • You can choose to reset (or not) the dataset after Sorting by any column.
  • +
  • Note that instead of the DataView, we could also use just plain SlickGrid (without DataView) to add items
  • +
+

+

View Source:

+ +

Reset Dataset onSort

+ + + +

Data Count

+

+ + 0 +

+
+
+ + + + + + + diff --git a/examples/index.html b/examples/index.html index 19a3d571..6aa3e398 100644 --- a/examples/index.html +++ b/examples/index.html @@ -46,6 +46,7 @@

ES6 / ESM

  • Interactive grouping and aggregates
  • (must see) Realtime Trading - High Frequency Update
  • Web Component with PubSub Service instead of SlickEvent
  • +
  • Infinite Scroll with DataView
  • diff --git a/src/slick.grid.ts b/src/slick.grid.ts index 2b25dbe7..fd10c506 100644 --- a/src/slick.grid.ts +++ b/src/slick.grid.ts @@ -425,6 +425,7 @@ export class SlickGrid = Column, O e protected renderedRows = 0; protected numVisibleRows = 0; protected prevScrollTop = 0; + protected scrollHeight = 0; protected scrollTop = 0; protected lastRenderedScrollTop = 0; protected lastRenderedScrollLeft = 0; @@ -4547,6 +4548,7 @@ export class SlickGrid = Column, O e } this.scrollTop = this._viewportScrollContainerY.scrollTop; + this.scrollHeight = this._viewportScrollContainerY.scrollHeight; this.enforceFrozenRowHeightRecalc = false; // reset enforce flag } @@ -4993,13 +4995,14 @@ export class SlickGrid = Column, O e } } - protected handleScroll() { + protected handleScroll(e?: Event) { + this.scrollHeight = this._viewportScrollContainerY.scrollHeight; this.scrollTop = this._viewportScrollContainerY.scrollTop; this.scrollLeft = this._viewportScrollContainerX.scrollLeft; - return this._handleScroll(false); + return this._handleScroll(e ? 'scroll' : 'system'); } - protected _handleScroll(isMouseWheel: boolean) { + protected _handleScroll(eventType: 'mousewheel' | 'scroll' | 'system' = 'system') { let maxScrollDistanceY = this._viewportScrollContainerY.scrollHeight - this._viewportScrollContainerY.clientHeight; let maxScrollDistanceX = this._viewportScrollContainerY.scrollWidth - this._viewportScrollContainerY.clientWidth; @@ -5011,6 +5014,7 @@ export class SlickGrid = Column, O e // Ceiling the max scroll values if (this.scrollTop > maxScrollDistanceY) { this.scrollTop = maxScrollDistanceY; + this.scrollHeight = maxScrollDistanceY; } if (this.scrollLeft > maxScrollDistanceX) { this.scrollLeft = maxScrollDistanceX; @@ -5060,7 +5064,7 @@ export class SlickGrid = Column, O e this.vScrollDir = this.prevScrollTop < this.scrollTop ? 1 : -1; this.prevScrollTop = this.scrollTop; - if (isMouseWheel) { + if (eventType === 'mousewheel') { this._viewportScrollContainerY.scrollTop = this.scrollTop; } @@ -5105,7 +5109,12 @@ export class SlickGrid = Column, O e } } - this.trigger(this.onScroll, { scrollLeft: this.scrollLeft, scrollTop: this.scrollTop }); + this.trigger(this.onScroll, { + triggeredBy: eventType, + scrollHeight: this.scrollHeight, + scrollLeft: this.scrollLeft, + scrollTop: this.scrollTop, + }); if (hScrollDist || vScrollDist) { return true; } return false; @@ -5361,9 +5370,10 @@ export class SlickGrid = Column, O e // Interactivity protected handleMouseWheel(e: MouseEvent, _delta: number, deltaX: number, deltaY: number) { + this.scrollHeight = this._viewportScrollContainerY.scrollHeight; this.scrollTop = Math.max(0, this._viewportScrollContainerY.scrollTop - (deltaY * this._options.rowHeight!)); this.scrollLeft = this._viewportScrollContainerX.scrollLeft + (deltaX * 10); - const handled = this._handleScroll(true); + const handled = this._handleScroll('mousewheel'); if (handled) { e.preventDefault(); }