Skip to content

Commit 3e182d0

Browse files
committed
modify testcase
1 parent ffb2f5a commit 3e182d0

File tree

4 files changed

+289
-148
lines changed

4 files changed

+289
-148
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,8 @@ repos:
4242
additional_dependencies: [types-requests, types-tabulate, types-PyYAML, pandas-stubs<=2.2.3.241126]
4343
exclude: "^third_party"
4444
args: ["--check-untyped-defs", "--explicit-package-bases", "--ignore-missing-imports"]
45+
- repo: https://github.com/biomejs/pre-commit
46+
rev: v2.0.2
47+
hooks:
48+
- id: biome-check
49+
files: '\.js$'

bigframes/display/anywidget.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# limitations under the License.
1414

1515
from importlib import resources
16+
import functools
17+
import math
1618
from typing import Iterator
1719
import uuid
1820

@@ -28,7 +30,7 @@ class TableWidget(anywidget.AnyWidget):
2830
An interactive, paginated table widget for BigFrames DataFrames.
2931
"""
3032

31-
@property
33+
@functools.cached_property
3234
def _esm(self):
3335
"""Load JavaScript code from external file."""
3436
return resources.read_text(bigframes.display, "table_widget.js")
@@ -65,6 +67,15 @@ def __init__(self, dataframe):
6567
# get the initial page
6668
self._set_table_html()
6769

70+
@traitlets.validate("page")
71+
def _validate_page(self, proposal):
72+
"""Validate and clamp page number to valid range."""
73+
value = proposal["value"]
74+
if self.row_count == 0 or self.page_size == 0:
75+
return 0
76+
max_page = max(0, math.ceil(self.row_count / self.page_size) - 1)
77+
return max(0, min(value, max_page))
78+
6879
def _get_next_batch(self) -> bool:
6980
"""
7081
Gets the next batch of data from the generator and appends to cache.

bigframes/display/table_widget.js

Lines changed: 69 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
/**
2-
* Copyright 2025 Google LLC
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
1616

1717
const ModelProperty = {
18-
TABLE_HTML: 'table_html',
19-
ROW_COUNT: 'row_count',
20-
PAGE_SIZE: 'page_size',
21-
PAGE: 'page',
18+
TABLE_HTML: "table_html",
19+
ROW_COUNT: "row_count",
20+
PAGE_SIZE: "page_size",
21+
PAGE: "page",
2222
};
2323

2424
const Event = {
25-
CHANGE_TABLE_HTML: `change:${ModelProperty.TABLE_HTML}`,
26-
CLICK: 'click',
25+
CHANGE_TABLE_HTML: `change:${ModelProperty.TABLE_HTML}`,
26+
CLICK: "click",
2727
};
2828

2929
/**
@@ -33,62 +33,63 @@ const Event = {
3333
* el: !HTMLElement
3434
* }} options
3535
*/
36-
function render({model, el}) {
37-
const container = document.createElement('div');
38-
container.innerHTML = model.get(ModelProperty.TABLE_HTML);
36+
function render({ model, el }) {
37+
const container = document.createElement("div");
38+
container.innerHTML = model.get(ModelProperty.TABLE_HTML);
3939

40-
const buttonContainer = document.createElement('div');
41-
const prevPage = document.createElement('button');
42-
const label = document.createElement('span');
43-
const nextPage = document.createElement('button');
40+
const buttonContainer = document.createElement("div");
41+
const prevPage = document.createElement("button");
42+
const label = document.createElement("span");
43+
const nextPage = document.createElement("button");
4444

45-
prevPage.type = 'button';
46-
nextPage.type = 'button';
47-
prevPage.textContent = 'Prev';
48-
nextPage.textContent = 'Next';
45+
prevPage.type = "button";
46+
nextPage.type = "button";
47+
prevPage.textContent = "Prev";
48+
nextPage.textContent = "Next";
4949

50-
/** Updates the button states and page label based on the model. */
51-
function updateButtonStates() {
52-
const totalPages = Math.ceil(
53-
model.get(ModelProperty.ROW_COUNT) / model.get(ModelProperty.PAGE_SIZE));
54-
const currentPage = model.get(ModelProperty.PAGE);
50+
/** Updates the button states and page label based on the model. */
51+
function updateButtonStates() {
52+
const totalPages = Math.ceil(
53+
model.get(ModelProperty.ROW_COUNT) / model.get(ModelProperty.PAGE_SIZE),
54+
);
55+
const currentPage = model.get(ModelProperty.PAGE);
5556

56-
label.textContent = `Page ${currentPage + 1} of ${totalPages}`;
57-
prevPage.disabled = currentPage === 0;
58-
nextPage.disabled = currentPage >= totalPages - 1;
59-
}
57+
label.textContent = `Page ${currentPage + 1} of ${totalPages}`;
58+
prevPage.disabled = currentPage === 0;
59+
nextPage.disabled = currentPage >= totalPages - 1;
60+
}
6061

61-
/**
62-
* Updates the page in the model.
63-
* @param {number} direction -1 for previous, 1 for next.
64-
*/
65-
function handlePageChange(direction) {
66-
const currentPage = model.get(ModelProperty.PAGE);
67-
const newPage = Math.max(0, currentPage + direction);
68-
if (newPage !== currentPage) {
69-
model.set(ModelProperty.PAGE, newPage);
70-
model.save_changes();
71-
}
72-
}
62+
/**
63+
* Updates the page in the model.
64+
* @param {number} direction -1 for previous, 1 for next.
65+
*/
66+
function handlePageChange(direction) {
67+
const currentPage = model.get(ModelProperty.PAGE);
68+
const newPage = Math.max(0, currentPage + direction);
69+
if (newPage !== currentPage) {
70+
model.set(ModelProperty.PAGE, newPage);
71+
model.save_changes();
72+
}
73+
}
7374

74-
prevPage.addEventListener(Event.CLICK, () => handlePageChange(-1));
75-
nextPage.addEventListener(Event.CLICK, () => handlePageChange(1));
75+
prevPage.addEventListener(Event.CLICK, () => handlePageChange(-1));
76+
nextPage.addEventListener(Event.CLICK, () => handlePageChange(1));
7677

77-
model.on(Event.CHANGE_TABLE_HTML, () => {
78-
// Note: Using innerHTML can be a security risk if the content is
79-
// user-generated. Ensure 'table_html' is properly sanitized.
80-
container.innerHTML = model.get(ModelProperty.TABLE_HTML);
81-
updateButtonStates();
82-
});
78+
model.on(Event.CHANGE_TABLE_HTML, () => {
79+
// Note: Using innerHTML can be a security risk if the content is
80+
// user-generated. Ensure 'table_html' is properly sanitized.
81+
container.innerHTML = model.get(ModelProperty.TABLE_HTML);
82+
updateButtonStates();
83+
});
8384

84-
// Initial setup
85-
updateButtonStates();
85+
// Initial setup
86+
updateButtonStates();
8687

87-
buttonContainer.appendChild(prevPage);
88-
buttonContainer.appendChild(label);
89-
buttonContainer.appendChild(nextPage);
90-
el.appendChild(container);
91-
el.appendChild(buttonContainer);
88+
buttonContainer.appendChild(prevPage);
89+
buttonContainer.appendChild(label);
90+
buttonContainer.appendChild(nextPage);
91+
el.appendChild(container);
92+
el.appendChild(buttonContainer);
9293
}
9394

94-
export default {render};
95+
export default { render };

0 commit comments

Comments
 (0)