Skip to content

Commit 83513b5

Browse files
committed
Merge branch 'main' into shuowei-anywidget-html-repr
2 parents 1042a9e + f4a7206 commit 83513b5

File tree

5 files changed

+56
-15
lines changed

5 files changed

+56
-15
lines changed

bigframes/display/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
"""Interactive display objects for BigQuery DataFrames."""
16+
1517
from __future__ import annotations
1618

1719
try:

bigframes/display/anywidget.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
"""Interactive, paginated table widget for BigFrames DataFrames."""
16+
1517
from __future__ import annotations
1618

1719
from importlib import resources

bigframes/display/table_widget.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ function render({ model, el }) {
9696
// Known total rows
9797
const totalPages = Math.ceil(rowCount / pageSize);
9898
rowCountLabel.textContent = `${rowCount.toLocaleString()} total rows`;
99-
paginationLabel.textContent = `Page ${(currentPage + 1).toLocaleString()} of ${rowCount.toLocaleString()}`;
99+
paginationLabel.textContent = `Page ${(currentPage + 1).toLocaleString()} of ${totalPages.toLocaleString()}`;
100100
prevPage.disabled = currentPage === 0;
101101
nextPage.disabled = currentPage >= totalPages - 1;
102102
}

notebooks/dataframes/anywidget_mode.ipynb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@
394394
],
395395
"source": [
396396
"df"
397+
"df"
397398
]
398399
},
399400
{
@@ -572,7 +573,7 @@
572573
"data": {
573574
"text/html": [
574575
"✅ Completed. \n",
575-
" Query processed 85.9 kB in 12 seconds of slot time.\n",
576+
" Query processed 85.9 kB in 15 seconds of slot time.\n",
576577
" "
577578
],
578579
"text/plain": [

tests/system/small/test_anywidget.py

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
# limitations under the License.
1414

1515

16-
import unittest.mock as mock
16+
from typing import Any
17+
from unittest import mock
1718

1819
import pandas as pd
1920
import pytest
@@ -100,6 +101,33 @@ def small_widget(small_bf_df):
100101
yield TableWidget(small_bf_df)
101102

102103

104+
@pytest.fixture
105+
def unknown_row_count_widget(session):
106+
"""Fixture to create a TableWidget with an unknown row count."""
107+
from bigframes.core import blocks
108+
from bigframes.display import TableWidget
109+
110+
# Create a small DataFrame with known content
111+
test_data = pd.DataFrame(
112+
{
113+
"id": [0, 1, 2, 3, 4],
114+
"value": ["row_0", "row_1", "row_2", "row_3", "row_4"],
115+
}
116+
)
117+
bf_df = session.read_pandas(test_data)
118+
119+
# Simulate a scenario where total_rows is not available from the iterator
120+
with mock.patch.object(bf_df, "_to_pandas_batches") as mock_batches:
121+
# We need to provide an iterator of DataFrames, not Series
122+
batches_iterator = iter([test_data])
123+
mock_batches.return_value = blocks.PandasBatches(
124+
batches_iterator, total_rows=None
125+
)
126+
with bf.option_context("display.repr_mode", "anywidget", "display.max_rows", 2):
127+
widget = TableWidget(bf_df)
128+
yield widget
129+
130+
103131
@pytest.fixture(scope="module")
104132
def empty_pandas_df() -> pd.DataFrame:
105133
"""Create an empty DataFrame for edge case testing."""
@@ -131,7 +159,7 @@ def execution_metadata(self) -> ExecutionMetadata:
131159
return ExecutionMetadata()
132160

133161
@property
134-
def schema(self):
162+
def schema(self) -> Any:
135163
return schema
136164

137165
def batches(self) -> ResultsIterator:
@@ -183,10 +211,10 @@ def test_widget_initialization_should_default_to_page_zero(
183211
table_widget,
184212
):
185213
"""
186-
Given a new TableWidget, when it is initialized,
187-
then its page number should default to 0.
214+
A TableWidget should initialize with page 0 and the correct page size.
188215
"""
189-
216+
# The `table_widget` fixture already creates the widget.
217+
# Assert its state.
190218
assert table_widget.page == 0
191219
assert table_widget.page_size == EXPECTED_PAGE_SIZE
192220

@@ -309,15 +337,21 @@ def test_widget_with_few_rows_should_display_all_rows(small_widget, small_pandas
309337

310338
def test_navigation_beyond_last_page_should_be_clamped(small_widget):
311339
"""
312-
Given a DataFrame smaller than the page size,
313-
when navigating beyond the last page,
314-
then the page should be clamped to the last valid page (page 0).
340+
Given a DataFrame with a small number of rows, the widget should
341+
report the correct total row count and prevent navigation beyond
342+
the first page, ensuring the frontend correctly displays "Page 1 of 1".
315343
"""
316-
assert small_widget.page == 0
344+
# For a DataFrame with 2 rows and page_size 5 (from small_widget fixture),
345+
# the frontend should calculate 1 total page.
346+
assert small_widget.row_count == 2
317347

318-
small_widget.page = 1 # Attempt to navigate past the end
348+
# The widget should always be on page 0 for a single-page dataset.
349+
assert small_widget.page == 0
319350

320-
assert small_widget.page == 0 # Should be clamped back to the only valid page
351+
# Attempting to navigate to page 1 should be clamped back to page 0,
352+
# confirming that only one page is recognized by the backend.
353+
small_widget.page = 1
354+
assert small_widget.page == 0
321355

322356

323357
def test_global_options_change_should_not_affect_existing_widget_page_size(
@@ -434,8 +468,10 @@ def test_navigation_after_page_size_change_should_use_new_size(
434468

435469
@pytest.mark.parametrize("invalid_size", [0, -5], ids=["zero", "negative"])
436470
def test_setting_invalid_page_size_should_be_ignored(table_widget, invalid_size: int):
437-
"""When the page size is set to an invalid number (<=0), the change should
438-
be ignored."""
471+
"""
472+
When the page size is set to an invalid number (<=0), the change should
473+
be ignored.
474+
"""
439475
# Set the initial page to 2.
440476
initial_size = table_widget.page_size
441477
assert initial_size == 2

0 commit comments

Comments
 (0)