Added RowIndex in GridState.
Fixed content escaping
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
import copy
|
import copy
|
||||||
|
import html
|
||||||
import logging
|
import logging
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Literal, Any
|
from typing import Literal, Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from fasthtml.components import *
|
from fasthtml.components import *
|
||||||
from fasthtml.xtend import Script
|
from fasthtml.xtend import Script
|
||||||
@@ -121,14 +123,21 @@ class DataGrid(BaseComponent):
|
|||||||
else:
|
else:
|
||||||
return ColumnType.Text # Default to Text if no match
|
return ColumnType.Text # Default to Text if no match
|
||||||
|
|
||||||
|
def _init_columns(_df):
|
||||||
|
columns = [DataGridColumnState(make_safe_id(col_id),
|
||||||
|
col_index,
|
||||||
|
col_id,
|
||||||
|
_get_column_type(_df[make_safe_id(col_id)].dtype))
|
||||||
|
for col_index, col_id in enumerate(_df.columns)]
|
||||||
|
if self._state.row_index:
|
||||||
|
columns.insert(0, DataGridColumnState(make_safe_id(ROW_INDEX_ID), -1, " ", ColumnType.RowIndex))
|
||||||
|
|
||||||
|
return columns
|
||||||
|
|
||||||
self._df = df.copy()
|
self._df = df.copy()
|
||||||
self._df.columns = self._df.columns.map(make_safe_id) # make sure column names are trimmed
|
self._df.columns = self._df.columns.map(make_safe_id) # make sure column names are trimmed
|
||||||
self._state.rows = [DataGridRowState(row_id) for row_id in self._df.index]
|
self._state.rows = [DataGridRowState(row_id) for row_id in self._df.index]
|
||||||
self._state.columns = [DataGridColumnState(make_safe_id(col_id),
|
self._state.columns = _init_columns(self._df)
|
||||||
col_index,
|
|
||||||
col_id,
|
|
||||||
_get_column_type(self._df[make_safe_id(col_id)].dtype))
|
|
||||||
for col_index, col_id in enumerate(df.columns)]
|
|
||||||
self._fast_access = self._init_fast_access(self._df)
|
self._fast_access = self._init_fast_access(self._df)
|
||||||
self._total_rows = len(self._df) if self._df is not None else 0
|
self._total_rows = len(self._df) if self._df is not None else 0
|
||||||
|
|
||||||
@@ -560,7 +569,7 @@ class DataGrid(BaseComponent):
|
|||||||
return mk_my_ellipsis(_value, cls="dt2-cell-content-number")
|
return mk_my_ellipsis(_value, cls="dt2-cell-content-number")
|
||||||
|
|
||||||
def process_cell_content(_value):
|
def process_cell_content(_value):
|
||||||
value_str = str(_value)
|
value_str = html.escape(str(_value))
|
||||||
|
|
||||||
if FILTER_INPUT_CID not in self._state.filtered or (
|
if FILTER_INPUT_CID not in self._state.filtered or (
|
||||||
keyword := self._state.filtered[FILTER_INPUT_CID]) is None:
|
keyword := self._state.filtered[FILTER_INPUT_CID]) is None:
|
||||||
@@ -869,7 +878,9 @@ class DataGrid(BaseComponent):
|
|||||||
if df is None:
|
if df is None:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
return {col: df[col].to_numpy() for col in df.columns}
|
res = {col: df[col].to_numpy() for col in df.columns}
|
||||||
|
res[ROW_INDEX_ID] = df.index.to_numpy()
|
||||||
|
return res
|
||||||
|
|
||||||
@timed
|
@timed
|
||||||
def __ft__(self):
|
def __ft__(self):
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ DATAGRID_STATE_FOOTER = "footer"
|
|||||||
|
|
||||||
DATAGRID_PAGE_SIZE = 50
|
DATAGRID_PAGE_SIZE = 50
|
||||||
|
|
||||||
|
ROW_INDEX_ID = "__row_index__"
|
||||||
|
|
||||||
class Routes:
|
class Routes:
|
||||||
Filter = "/filter" # request the filtering in the grid
|
Filter = "/filter" # request the filtering in the grid
|
||||||
ResetFilter = "/reset_filter" #
|
ResetFilter = "/reset_filter" #
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ class DataGridSettings:
|
|||||||
class DataGridState:
|
class DataGridState:
|
||||||
sidebar_visible: bool = False
|
sidebar_visible: bool = False
|
||||||
selected_view: str = None
|
selected_view: str = None
|
||||||
|
row_index: bool = False
|
||||||
columns: list[DataGridColumnState] = dataclasses.field(default_factory=list)
|
columns: list[DataGridColumnState] = dataclasses.field(default_factory=list)
|
||||||
rows: list[DataGridRowState] = dataclasses.field(default_factory=list) # only the rows that have a specific state
|
rows: list[DataGridRowState] = dataclasses.field(default_factory=list) # only the rows that have a specific state
|
||||||
footers: list[DataGridFooterConf] = dataclasses.field(default_factory=list)
|
footers: list[DataGridFooterConf] = dataclasses.field(default_factory=list)
|
||||||
|
|||||||
@@ -509,3 +509,18 @@ def test_i_can_compute_footer_menu_position_when_not_enough_space(dg):
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert matches(menu, expected)
|
assert matches(menu, expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_content_of_the_cell_is_escaped(empty_dg):
|
||||||
|
df = pd.DataFrame({
|
||||||
|
'value': ['<div> My Content </div>'],
|
||||||
|
'value2': ['{My Content}'],
|
||||||
|
})
|
||||||
|
my_dg = empty_dg.init_from_dataframe(df)
|
||||||
|
|
||||||
|
actual = my_dg.__ft__()
|
||||||
|
table_content = extract_table_values_new(actual, header=True)
|
||||||
|
|
||||||
|
assert table_content == OrderedDict({
|
||||||
|
'value': ['<div> My Content </div>'],
|
||||||
|
'value2': ['{My Content}']})
|
||||||
|
|||||||
Reference in New Issue
Block a user