Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions panel/tests/widgets/test_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -3047,3 +3047,55 @@ def test_header_filters_categorial_dtype():
def test_tabulator_aggregators(document, comm, df_agg, aggs):
tabulator = Tabulator(df_agg, hierarchical=True, aggregators=aggs)
tabulator.get_root(document, comm)


def make_column_order_df():
return pd.DataFrame({
'col1': [1, 2, 3], 'col2': [4, 5, 6],
'col3': [7, 8, 9], 'col4': [10, 11, 12],
})


def test_tabulator_column_order_default():
tab = Tabulator(make_column_order_df())
fields = [c.field for c in tab._get_columns()]
assert fields == ['index', 'col1', 'col2', 'col3', 'col4']


def test_tabulator_column_order_filters_and_reorders():
tab = Tabulator(make_column_order_df(), column_order=['col3', 'col1'])
fields = [c.field for c in tab._get_columns()]
assert fields == ['index', 'col3', 'col1']


def test_tabulator_column_order_dynamic_update():
tab = Tabulator(make_column_order_df(), column_order=['col3', 'col1'])
tab.column_order = ['col2', 'col4', 'col1']
assert [c.field for c in tab._get_columns()] == ['index', 'col2', 'col4', 'col1']

tab.column_order = None
assert [c.field for c in tab._get_columns()] == ['index', 'col1', 'col2', 'col3', 'col4']


def test_tabulator_column_order_ignores_invalid_columns():
df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6], 'col3': [7, 8, 9]})
tab = Tabulator(df, column_order=['col3', 'nonexistent', 'col1'])
fields = [c.field for c in tab._get_columns()]
assert fields == ['index', 'col3', 'col1']


def test_tabulator_column_order_index_not_affected():
df = pd.DataFrame(
{'col1': [1, 2], 'col2': [3, 4]},
index=pd.Index([10, 20], name='my_index')
)
tab = Tabulator(df, column_order=['col2'])
fields = [c.field for c in tab._get_columns()]
assert fields == ['my_index', 'col2']


def test_tabulator_column_order_model_columns(document, comm):
tab = Tabulator(make_column_order_df(), column_order=['col3', 'col1'])
model = tab.get_root(document, comm)
fields = [c.field for c in model.columns]
assert fields == ['index', 'col3', 'col1']
30 changes: 28 additions & 2 deletions panel/widgets/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,14 @@ class Tabulator(BaseTable):
hidden_columns = param.List(default=[], nested_refs=True, doc="""
List of columns to hide.""")

column_order = param.List(default=None, allow_None=True, doc="""
An ordered list of columns to display. If None (default), all
columns are displayed in the order inherited from the underlying
data structure. If a list is provided, only the listed columns
will be displayed in the order they appear within the list.
Columns may be omitted to hide them. Index columns cannot be
reordered or hidden using this parameter.""")

layout = param.Selector(default='fit_data_table', objects=[
'fit_data', 'fit_data_fill', 'fit_data_stretch', 'fit_data_table',
'fit_columns'], doc="""
Expand Down Expand Up @@ -1356,15 +1364,16 @@ class Tabulator(BaseTable):

_content_params: ClassVar[list[str]] = _data_params + ['expanded', 'row_content', 'embed_content']

_manual_params: ClassVar[list[str]] = BaseTable._manual_params + _config_params
_manual_params: ClassVar[list[str]] = BaseTable._manual_params + _config_params + ['column_order']

_priority_changes: ClassVar[list[str]] = ['data', 'filters']

_rename: ClassVar[Mapping[str, str | None]] = {
'selection': None, 'row_content': None, 'row_height': None,
'text_align': None, 'header_align': None, 'header_filters': None,
'header_tooltips': None, 'styles': 'cell_styles',
'title_formatters': None, 'sortable': None, 'initial_page_size': None
'title_formatters': None, 'sortable': None, 'initial_page_size': None,
'column_order': None,
}

# Determines the maximum size limits beyond which (local, remote)
Expand Down Expand Up @@ -1404,6 +1413,23 @@ def __init__(self, value=None, **params):
self.style._todo = style._todo
self.param.selection.callable = self._get_selectable

def _get_columns(self) -> list:
if self.value is None:
return []
indexes = self.indexes
all_fields = self._get_fields()

if self.column_order is not None:
ordered_fields = indexes + [
col for col in self.column_order
if col in all_fields and col not in indexes
]
else:
ordered_fields = all_fields

df = self.value.reset_index() if len(indexes) > 1 else self.value
return self._get_column_definitions(ordered_fields, df)

@param.depends('value', watch=True, on_init=True)
def _apply_max_size(self):
"""
Expand Down
Loading