Skip to content

Commit 15cafe3

Browse files
committed
feat(display): support max_columns option in TableWidget
1 parent cc994f3 commit 15cafe3

File tree

4 files changed

+108
-6
lines changed

4 files changed

+108
-6
lines changed

bigframes/display/anywidget.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ def _set_table_html(self) -> None:
348348
dataframe=page_data,
349349
table_id=f"table-{self._table_id}",
350350
orderable_columns=self.orderable_columns,
351+
max_columns=bigframes.options.display.max_columns,
351352
)
352353

353354
if new_page is not None:

bigframes/display/html.py

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,51 @@ def render_html(
4646
dataframe: pd.DataFrame,
4747
table_id: str,
4848
orderable_columns: list[str] | None = None,
49+
max_columns: int | None = None,
4950
) -> str:
5051
"""Render a pandas DataFrame to HTML with specific styling."""
5152
orderable_columns = orderable_columns or []
5253
classes = "dataframe table table-striped table-hover"
5354
table_html_parts = [f'<table border="1" class="{classes}" id="{table_id}">']
54-
table_html_parts.append(_render_table_header(dataframe, orderable_columns))
55-
table_html_parts.append(_render_table_body(dataframe))
55+
56+
# Handle column truncation
57+
columns = list(dataframe.columns)
58+
if max_columns is not None and max_columns > 0 and len(columns) > max_columns:
59+
half = max_columns // 2
60+
left_columns = columns[:half]
61+
# Ensure we don't take more than available if half is 0 or calculation is weird,
62+
# but typical case is safe.
63+
right_count = max_columns - half
64+
right_columns = columns[-right_count:] if right_count > 0 else []
65+
show_ellipsis = True
66+
else:
67+
left_columns = columns
68+
right_columns = []
69+
show_ellipsis = False
70+
71+
table_html_parts.append(
72+
_render_table_header(
73+
dataframe, orderable_columns, left_columns, right_columns, show_ellipsis
74+
)
75+
)
76+
table_html_parts.append(
77+
_render_table_body(dataframe, left_columns, right_columns, show_ellipsis)
78+
)
5679
table_html_parts.append("</table>")
5780
return "".join(table_html_parts)
5881

5982

60-
def _render_table_header(dataframe: pd.DataFrame, orderable_columns: list[str]) -> str:
83+
def _render_table_header(
84+
dataframe: pd.DataFrame,
85+
orderable_columns: list[str],
86+
left_columns: list[Any],
87+
right_columns: list[Any],
88+
show_ellipsis: bool,
89+
) -> str:
6190
"""Render the header of the HTML table."""
6291
header_parts = [" <thead>", " <tr>"]
63-
for col in dataframe.columns:
92+
93+
def render_col_header(col):
6494
th_classes = []
6595
if col in orderable_columns:
6696
th_classes.append("sortable")
@@ -69,19 +99,38 @@ def _render_table_header(dataframe: pd.DataFrame, orderable_columns: list[str])
6999
f' <th {class_str}><div class="bf-header-content">'
70100
f"{html.escape(str(col))}</div></th>"
71101
)
102+
103+
for col in left_columns:
104+
render_col_header(col)
105+
106+
if show_ellipsis:
107+
header_parts.append(
108+
' <th><div class="bf-header-content" style="cursor: default;">...</div></th>'
109+
)
110+
111+
for col in right_columns:
112+
render_col_header(col)
113+
72114
header_parts.extend([" </tr>", " </thead>"])
73115
return "\n".join(header_parts)
74116

75117

76-
def _render_table_body(dataframe: pd.DataFrame) -> str:
118+
def _render_table_body(
119+
dataframe: pd.DataFrame,
120+
left_columns: list[Any],
121+
right_columns: list[Any],
122+
show_ellipsis: bool,
123+
) -> str:
77124
"""Render the body of the HTML table."""
78125
body_parts = [" <tbody>"]
79126
precision = options.display.precision
80127

81128
for i in range(len(dataframe)):
82129
body_parts.append(" <tr>")
83130
row = dataframe.iloc[i]
84-
for col_name, value in row.items():
131+
132+
def render_col_cell(col_name):
133+
value = row[col_name]
85134
dtype = dataframe.dtypes.loc[col_name] # type: ignore
86135
align = "right" if _is_dtype_numeric(dtype) else "left"
87136

@@ -101,6 +150,17 @@ def _render_table_body(dataframe: pd.DataFrame) -> str:
101150
f' <td class="cell-align-{align}">'
102151
f"{html.escape(cell_content)}</td>"
103152
)
153+
154+
for col in left_columns:
155+
render_col_cell(col)
156+
157+
if show_ellipsis:
158+
# Ellipsis cell
159+
body_parts.append(' <td class="cell-align-center">...</td>')
160+
161+
for col in right_columns:
162+
render_col_cell(col)
163+
104164
body_parts.append(" </tr>")
105165
body_parts.append(" </tbody>")
106166
return "\n".join(body_parts)

bigframes/display/table_widget.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ body[data-theme='dark'] .bigframes-widget.bigframes-widget {
229229
text-align: left;
230230
}
231231

232+
.bigframes-widget .cell-align-center {
233+
text-align: center;
234+
}
235+
232236
.bigframes-widget .null-value {
233237
color: var(--bf-null-fg);
234238
}

tests/unit/display/test_html.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,40 @@ def test_render_html_precision():
148148
# Make sure we reset to default
149149
html = bf_html.render_html(dataframe=df, table_id="test-table")
150150
assert "3.141593" in html
151+
152+
153+
def test_render_html_max_columns_truncation():
154+
# Create a DataFrame with 10 columns
155+
data = {f"col_{i}": [i] for i in range(10)}
156+
df = pd.DataFrame(data)
157+
158+
# Test max_columns=4
159+
# max_columns=4 -> 2 left, 2 right. col_0, col_1 ... col_8, col_9
160+
html = bf_html.render_html(dataframe=df, table_id="test", max_columns=4)
161+
162+
assert "col_0" in html
163+
assert "col_1" in html
164+
assert "col_2" not in html
165+
assert "col_7" not in html
166+
assert "col_8" in html
167+
assert "col_9" in html
168+
assert "..." in html
169+
170+
# Test max_columns=3
171+
# 3 // 2 = 1. Left: col_0. Right: 3 - 1 = 2. col_8, col_9.
172+
# Total displayed: col_0, ..., col_8, col_9. (3 data cols + 1 ellipsis)
173+
html = bf_html.render_html(dataframe=df, table_id="test", max_columns=3)
174+
assert "col_0" in html
175+
assert "col_1" not in html
176+
assert "col_7" not in html
177+
assert "col_8" in html
178+
assert "col_9" in html
179+
180+
# Test max_columns=1
181+
# 1 // 2 = 0. Left: []. Right: 1. col_9.
182+
# Total: ..., col_9.
183+
html = bf_html.render_html(dataframe=df, table_id="test", max_columns=1)
184+
assert "col_0" not in html
185+
assert "col_8" not in html
186+
assert "col_9" in html
187+
assert "..." in html

0 commit comments

Comments
 (0)