Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit d7b27ed

Browse files
committed
add CSS scoping
1 parent 1877e65 commit d7b27ed

2 files changed

Lines changed: 56 additions & 44 deletions

File tree

bigframes/display/table_widget.css

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,23 @@
1414
* limitations under the License.
1515
*/
1616

17-
.table-container {
17+
.bigframes-widget .table-container {
1818
max-height: 620px;
1919
overflow: auto;
2020
}
2121

22-
.footer {
22+
.bigframes-widget .footer {
2323
align-items: center;
2424
display: flex;
2525
font-size: 0.8rem;
26+
padding-top: 8px;
2627
}
2728

28-
.footer > * {
29+
.bigframes-widget .footer > * {
2930
flex: 1;
3031
}
3132

32-
.pagination {
33+
.bigframes-widget .pagination {
3334
align-items: center;
3435
display: flex;
3536
flex-direction: row;
@@ -38,29 +39,29 @@
3839
padding: 4px;
3940
}
4041

41-
.page-size {
42+
.bigframes-widget .page-size {
4243
align-items: center;
4344
display: flex;
4445
flex-direction: row;
4546
gap: 4px;
4647
justify-content: end;
4748
}
4849

49-
table {
50+
.bigframes-widget table {
5051
border-collapse: collapse;
5152
text-align: left;
5253
width: 100%;
5354
}
5455

55-
th {
56+
.bigframes-widget th {
5657
background-color: var(--colab-primary-surface-color, var(--jp-layout-color0));
57-
/* Uncomment once we support sorting cursor: pointer; */
58+
/* Uncomment once we support sorting: cursor: pointer; */
5859
position: sticky;
5960
top: 0;
6061
z-index: 1;
6162
}
6263

63-
button {
64+
.bigframes-widget button {
6465
cursor: pointer;
6566
display: inline-block;
6667
text-align: center;
@@ -69,7 +70,7 @@ button {
6970
vertical-align: middle;
7071
}
7172

72-
button:disabled {
73+
.bigframes-widget button:disabled {
7374
opacity: 0.65;
7475
pointer-events: none;
7576
}

bigframes/display/table_widget.js

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
*/
1616

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

2424
const Event = {
@@ -28,39 +28,48 @@ const Event = {
2828
};
2929

3030
/**
31-
* Renders a paginated table and its controls into a given element.
31+
* Renders the interactive table widget.
3232
* @param {{
33-
* model: !Backbone.Model,
34-
* el: !HTMLElement
33+
* model: any,
34+
* el: HTMLElement
3535
* }} options
3636
*/
3737
function render({ model, el }) {
38-
// Structure
38+
// Main container with a unique class for CSS scoping
3939
const container = document.createElement("div");
40+
container.classList.add("bigframes-widget");
41+
42+
// Structure
4043
const tableContainer = document.createElement("div");
4144
const footer = document.createElement("div");
42-
// Total rows label
45+
46+
// Footer: Total rows label
4347
const rowCountLabel = document.createElement("div");
44-
// Pagination controls
48+
49+
// Footer: Pagination controls
4550
const paginationContainer = document.createElement("div");
4651
const prevPage = document.createElement("button");
4752
const paginationLabel = document.createElement("span");
4853
const nextPage = document.createElement("button");
49-
// Page size controls
54+
55+
// Footer: Page size controls
5056
const pageSizeContainer = document.createElement("div");
5157
const pageSizeLabel = document.createElement("label");
5258
const pageSizeSelect = document.createElement("select");
5359

60+
// Add CSS classes
5461
tableContainer.classList.add("table-container");
5562
footer.classList.add("footer");
5663
paginationContainer.classList.add("pagination");
5764
pageSizeContainer.classList.add("page-size");
5865

66+
// Configure pagination buttons
5967
prevPage.type = "button";
6068
nextPage.type = "button";
6169
prevPage.textContent = "Prev";
6270
nextPage.textContent = "Next";
6371

72+
// Configure page size selector
6473
pageSizeLabel.textContent = "Page Size";
6574
for (const size of [10, 25, 50, 100]) {
6675
const option = document.createElement("option");
@@ -72,39 +81,34 @@ function render({ model, el }) {
7281
pageSizeSelect.appendChild(option);
7382
}
7483

75-
/** Updates the button states and page label based on the model. */
84+
/** Updates the footer states and page label based on the model. */
7685
function updateButtonStates() {
7786
const rowCount = model.get(ModelProperty.ROW_COUNT);
78-
rowCountLabel.textContent = `${rowCount.toLocaleString()} total rows`;
79-
80-
const totalPages = Math.ceil(
81-
model.get(ModelProperty.ROW_COUNT) / model.get(ModelProperty.PAGE_SIZE),
82-
);
87+
const pageSize = model.get(ModelProperty.PAGE_SIZE);
8388
const currentPage = model.get(ModelProperty.PAGE);
89+
const totalPages = Math.ceil(rowCount / pageSize);
8490

85-
paginationLabel.textContent = `Page ${currentPage + 1} of ${totalPages}`;
91+
rowCountLabel.textContent = `${rowCount.toLocaleString()} total rows`;
92+
paginationLabel.textContent = `Page ${currentPage + 1} of ${totalPages || 1}`;
8693
prevPage.disabled = currentPage === 0;
8794
nextPage.disabled = currentPage >= totalPages - 1;
88-
89-
// Update page size selector
90-
pageSizeSelect.value = model.get(ModelProperty.PAGE_SIZE);
95+
pageSizeSelect.value = pageSize;
9196
}
9297

9398
/**
94-
* Updates the page in the model.
95-
* @param {number} direction -1 for previous, 1 for next.
99+
* Increments or decrements the page in the model.
100+
* @param {number} direction - `1` for next, `-1` for previous.
96101
*/
97102
function handlePageChange(direction) {
98-
const currentPage = model.get(ModelProperty.PAGE);
99-
const newPage = Math.max(0, currentPage + direction);
100-
if (newPage !== currentPage) {
101-
model.set(ModelProperty.PAGE, newPage);
102-
model.save_changes();
103-
}
103+
const current = model.get(ModelProperty.PAGE);
104+
const next = current + direction;
105+
model.set(ModelProperty.PAGE, next);
106+
model.save_changes();
104107
}
105108

106-
/** Handles the page_size in the model.
107-
* @param {number} size - new size to set
109+
/**
110+
* Handles changes to the page size from the dropdown.
111+
* @param {number} size - The new page size.
108112
*/
109113
function handlePageSizeChange(size) {
110114
const currentSize = model.get(ModelProperty.PAGE_SIZE);
@@ -114,14 +118,15 @@ function render({ model, el }) {
114118
}
115119
}
116120

117-
/** Updates the HTML in the table container **/
121+
/** Updates the HTML in the table container and refreshes button states. */
118122
function handleTableHTMLChange() {
119-
// Note: Using innerHTML can be a security risk if the content is
120-
// user-generated. Ensure 'table_html' is properly sanitized.
123+
// Note: Using innerHTML is safe here because the content is generated
124+
// by a trusted backend (DataFrame.to_html).
121125
tableContainer.innerHTML = model.get(ModelProperty.TABLE_HTML);
122126
updateButtonStates();
123127
}
124128

129+
// Add event listeners
125130
prevPage.addEventListener(Event.CLICK, () => handlePageChange(-1));
126131
nextPage.addEventListener(Event.CLICK, () => handlePageChange(1));
127132
pageSizeSelect.addEventListener(Event.CHANGE, (e) => {
@@ -132,18 +137,24 @@ function render({ model, el }) {
132137
});
133138
model.on(Event.CHANGE_TABLE_HTML, handleTableHTMLChange);
134139

135-
// Initial setup
140+
// Assemble the DOM
136141
paginationContainer.appendChild(prevPage);
137142
paginationContainer.appendChild(paginationLabel);
138143
paginationContainer.appendChild(nextPage);
144+
139145
pageSizeContainer.appendChild(pageSizeLabel);
140146
pageSizeContainer.appendChild(pageSizeSelect);
147+
141148
footer.appendChild(rowCountLabel);
142149
footer.appendChild(paginationContainer);
143150
footer.appendChild(pageSizeContainer);
151+
144152
container.appendChild(tableContainer);
145153
container.appendChild(footer);
154+
146155
el.appendChild(container);
156+
157+
// Initial render
147158
handleTableHTMLChange();
148159
}
149160

0 commit comments

Comments
 (0)