diff --git a/panel/models/tabulator.ts b/panel/models/tabulator.ts index 84e86eed7a..563dbf4221 100644 --- a/panel/models/tabulator.ts +++ b/panel/models/tabulator.ts @@ -1034,15 +1034,15 @@ export class DataTabulatorView extends HTMLBoxView { } // Update table - setData(): void { + setData(): Promise { if (this._initializing || this._building || !this.tabulator.initialized) { - return + return Promise.resolve(undefined) } const data = this.getData() if (this.model.pagination != null) { - this.tabulator.rowManager.setData(data, true, false) + return this.tabulator.rowManager.setData(data, true, false) } else { - this.tabulator.setData(data) + return this.tabulator.setData(data) } } @@ -1050,9 +1050,20 @@ export class DataTabulatorView extends HTMLBoxView { const rows = this.tabulator.rowManager.getRows() const last_row = rows[rows.length-1] const start = ((last_row?.data._index) || 0) - this.setData() - if (this.model.follow && last_row) { - this.tabulator.scrollToRow(start, "top", false) + this._updating_page = true + const promise = this.setData() + if (this.model.follow) { + promise.then(() => { + if (this.model.pagination) { + this.tabulator.setPage(Math.ceil(this.tabulator.rowManager.getDataCount() / (this.model.page_size || 20))) + } + if (last_row) { + this.tabulator.scrollToRow(start, "top", false) + } + this._updating_page = false + }) + } else { + this._updating_page = true } } @@ -1099,7 +1110,7 @@ export class DataTabulatorView extends HTMLBoxView { } updatePage(pageno: number): void { - if (this.model.pagination === "local" && this.model.page !== pageno) { + if (this.model.pagination === "local" && this.model.page !== pageno && !this._updating_page) { this._updating_page = true this.model.page = pageno this._updating_page = false diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py index 2ee5f6476e..7f5671a410 100644 --- a/panel/tests/ui/widgets/test_tabulator.py +++ b/panel/tests/ui/widgets/test_tabulator.py @@ -2116,7 +2116,6 @@ def test_tabulator_streaming_default(page): height_start = page.locator('.pnx-tabulator.tabulator').bounding_box()['height'] - def stream_data(): widget.stream(df) # follow is True by default @@ -2131,6 +2130,24 @@ def stream_data(): assert page.locator('.pnx-tabulator.tabulator').bounding_box()['height'] > height_start +@pytest.mark.parametrize('pagination', ['remote', 'local']) +def test_tabulator_streaming_follow_pagination(page, pagination): + df = pd.DataFrame(np.random.random((3, 2)), columns=['A', 'B']) + widget = Tabulator(df, pagination=pagination, page_size=3) + + serve_component(page, widget) + + expect(page.locator('.tabulator-row')).to_have_count(len(df)) + + widget.stream(df) + + expect(page.locator('.tabulator-page.active')).to_have_text('2') + + widget.stream(df) + + expect(page.locator('.tabulator-page.active')).to_have_text('3') + + def test_tabulator_streaming_no_follow(page): nrows1 = 10 arr = np.random.randint(10, 20, (nrows1, 2)) diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py index 9aa320b3e2..d9f2ac63f9 100644 --- a/panel/widgets/tables.py +++ b/panel/widgets/tables.py @@ -1565,8 +1565,10 @@ def _stream(self, stream, rollover=None, follow=True): length = self._length nrows = self.page_size or self.initial_page_size max_page = max(length//nrows + bool(length%nrows), 1) - if self.page != max_page: + if self.page != max_page and not follow: return + self._processed, _ = self._get_data() + return super()._stream(stream, rollover) self._update_style() self._update_selectable() @@ -1575,13 +1577,13 @@ def _stream(self, stream, rollover=None, follow=True): def stream(self, stream_value, rollover=None, reset_index=True, follow=True): for ref, (model, _) in self._models.items(): self._apply_update([], {'follow': follow}, model, ref) + super().stream(stream_value, rollover, reset_index) + if follow and self.pagination: + self._update_max_page() if follow and self.pagination: length = self._length nrows = self.page_size or self.initial_page_size self.page = max(length//nrows + bool(length%nrows), 1) - super().stream(stream_value, rollover, reset_index) - if follow and self.pagination: - self._update_max_page() @updating def _patch(self, patch):