Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure updates to Tabulator formatter or editor updates model #4296

Merged
merged 2 commits into from
Jan 17, 2023
Merged
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
7 changes: 6 additions & 1 deletion panel/models/tabulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,12 @@ export class DataTabulatorView extends PanelHTMLBoxView {

const p = this.model.properties
const {configuration, layout, columns, theme, groupby} = p;
this.on_change([configuration, layout, columns, groupby], debounce(() => this.invalidate_render(), 20, false))
this.on_change([configuration, layout, groupby], debounce(() => this.invalidate_render(), 20, false))

this.on_change([columns], () => {
this.tabulator.setColumns(this.getColumns())
this.setHidden()
})

this.on_change([theme], () => this.setCSS())

Expand Down
26 changes: 26 additions & 0 deletions panel/tests/widgets/test_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -1936,6 +1936,32 @@ def test_tabulator_styling_empty_dataframe():
}
}

def test_tabulator_editor_property_change(dataframe, document, comm):
editor = SelectEditor(options=['A', 'B', 'C'])
table = Tabulator(dataframe, editors={'str': editor})
model = table.get_root(document, comm)

model_editor = model.columns[-1].editor
assert isinstance(model_editor, SelectEditor) is not editor
assert isinstance(model_editor, SelectEditor)
assert model_editor.options == editor.options

editor.options = ['D', 'E']
model_editor = model.columns[-1].editor
assert model_editor.options == editor.options

def test_tabulator_formatter_update(dataframe, document, comm):
formatter = NumberFormatter(format='0.0000')
table = Tabulator(dataframe, formatters={'float': formatter})
model = table.get_root(document, comm)
model_formatter = model.columns[2].formatter
assert model_formatter is not formatter
assert isinstance(model_formatter, NumberFormatter)
assert model_formatter.format == formatter.format

formatter.format = '0.0'
model_formatter = model.columns[2].formatter
assert model_formatter.format == formatter.format

def test_tabulator_pandas_import():
# Checks for: https://github.com/holoviz/panel/issues/4102
Expand Down
29 changes: 28 additions & 1 deletion panel/widgets/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import numpy as np
import param

from bokeh.model import Model
from bokeh.models import ColumnDataSource
from bokeh.models.widgets.tables import (
AvgAggregator, CellEditor, CellFormatter, CheckboxEditor, DataCube,
Expand Down Expand Up @@ -43,7 +44,6 @@
DataFrameType = TypeVar('DataFrameType')

from bokeh.document import Document
from bokeh.model import Model
from bokeh.models.sources import DataDict
from pyviz_comms import Comm

Expand Down Expand Up @@ -110,6 +110,9 @@ def __init__(self, value=None, **params):
self._filters = []
self._index_mapping = {}
super().__init__(value=value, **params)
self.param.watch(self._setup_on_change, ['editors', 'formatters'])
self.param.trigger('editors')
self.param.trigger('formatters')

@param.depends('value', watch=True, on_init=True)
def _compute_renamed_cols(self):
Expand Down Expand Up @@ -230,6 +233,30 @@ def _get_column_definitions(self, col_names: List[str], df: DataFrameType) -> Li
columns.append(column)
return columns

def _setup_on_change(self, event: param.parameterized.Event):
old, new = event.old, event.new
for model in (old if isinstance(old, dict) else {}).values():
if not isinstance(model, (CellEditor, CellFormatter)):
continue
change_fn = self._editor_change if isinstance(model, CellEditor) else self._formatter_change
for prop in (model.properties() - Model.properties()):
try:
model.remove_on_change(prop, change_fn)
except ValueError:
pass
for model in (new if isinstance(new, dict) else {}).values():
if not isinstance(model, (CellEditor, CellFormatter)):
continue
change_fn = self._editor_change if isinstance(model, CellEditor) else self._formatter_change
for prop in (model.properties() - Model.properties()):
model.on_change(prop, change_fn)

def _editor_change(self, attr: str, new: Any, old: Any):
self.param.trigger('editors')

def _formatter_change(self, attr: str, new: Any, old: Any):
self.param.trigger('formatters')

def _update_index_mapping(self):
if self._processed is None or isinstance(self._processed, list) and not self._processed:
self._index_mapping = {}
Expand Down