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

Handle cancelling and empty value edit events on Tabulator #4343

Merged
merged 4 commits into from
Jan 27, 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
20 changes: 16 additions & 4 deletions panel/models/tabulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ const datetimeEditor = function(cell: any, onRendered: any, success: any, cancel
export class DataTabulatorView extends PanelHTMLBoxView {
model: DataTabulator;
tabulator: any;
columns: Map<string, any> = new Map();
_tabulator_cell_updating: boolean=false
_updating_page: boolean = true
_updating_sort: boolean = false
Expand Down Expand Up @@ -685,6 +686,7 @@ export class DataTabulatorView extends PanelHTMLBoxView {
}

getColumns(): any {
this.columns = new Map()
const config_columns: (any[] | undefined) = this.model.configuration?.columns;
let columns = []
columns.push({field: '_index', frozen: true})
Expand Down Expand Up @@ -735,6 +737,7 @@ export class DataTabulatorView extends PanelHTMLBoxView {
}
if (tab_column == null)
tab_column = {field: column.field}
this.columns.set(column.field, tab_column)
if (tab_column.title == null)
tab_column.title = column.title
if (tab_column.width == null && column.width != null && column.width != 0)
Expand Down Expand Up @@ -781,6 +784,10 @@ export class DataTabulatorView extends PanelHTMLBoxView {
else if (ctype === "IntEditor" || ctype === "NumberEditor") {
tab_column.editor = "number"
tab_column.editorParams = {step: editor.step}
if (ctype === "IntEditor")
tab_column.validator = "integer"
else
tab_column.validator = "numeric"
} else if (ctype === "CheckboxEditor") {
tab_column.editor = "tickCross"
} else if (ctype === "DateEditor") {
Expand Down Expand Up @@ -829,7 +836,7 @@ export class DataTabulatorView extends PanelHTMLBoxView {
return columns
}

renderEditor(column: any, cell: any, onRendered: any, success: any, error: any): any {
renderEditor(column: any, cell: any, onRendered: any, success: any, cancel: any): any {
const editor = column.editor
const view = new editor.default_view({column: column, model: editor, parent: this, container: cell._cell.element})
view.initialize()
Expand All @@ -838,14 +845,14 @@ export class DataTabulatorView extends PanelHTMLBoxView {
view.setValue(cell.getValue())
})

view.inputEl.addEventListener('change', () => {
view.inputEl.addEventListener('input', () => {
const value = view.serializeValue()
const old_value = cell.getValue()
const validation = view.validate()
if (!validation.valid)
error(validation.msg)
cancel(validation.msg)
if (old_value != null && typeof value != typeof old_value)
error("Mismatching type")
cancel("Mismatching type")
else
success(view.serializeValue())
});
Expand Down Expand Up @@ -1134,8 +1141,13 @@ export class DataTabulatorView extends PanelHTMLBoxView {

cellEdited(cell: any): void {
const field = cell._cell.column.field;
const column_def = this.columns.get(field)
const index = cell.getData()._index
const value = cell._cell.value
if (column_def.validator === 'numeric' && value === '') {
cell.setValue(NaN, true)
return
}
this._tabulator_cell_updating = true
comm_settings.debounce = false
this.model.trigger_event(new TableEditEvent(field, index, true))
Expand Down
54 changes: 49 additions & 5 deletions panel/tests/ui/widgets/test_tabulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import datetime as dt
import time

import numpy as np
import param
import pytest

Expand All @@ -20,11 +21,6 @@

pytestmark = pytest.mark.ui

try:
import numpy as np
except ImportError:
pytestmark = pytest.mark.skip('numpy not available')

try:
import pandas as pd
except ImportError:
Expand Down Expand Up @@ -1898,6 +1894,54 @@ def test_tabulator_edit_event(page, port, df_mixed):
assert df_mixed.at['idx0', 'str'] == 'AA'


def test_tabulator_edit_event_abort(page, port, df_mixed):
widget = Tabulator(df_mixed)

values = []
widget.on_edit(lambda e: values.append((e.column, e.row, e.old, e.value)))

serve(widget, port=port, threaded=True, show=False)

time.sleep(0.2)

page.goto(f"http://localhost:{port}")

cell = page.locator('text="3.14"')
cell.click()
editable_cell = page.locator('input[type="number"]')
editable_cell.fill('0')
editable_cell.press('Escape')

time.sleep(0.2)

assert not values
assert cell.text_content() == '3.14'


def test_tabulator_edit_event_empty_to_nan(page, port, df_mixed):
widget = Tabulator(df_mixed)

values = []
widget.on_edit(lambda e: values.append((e.column, e.row, e.old, e.value)))

serve(widget, port=port, threaded=True, show=False)

time.sleep(0.5)

page.goto(f"http://localhost:{port}")

cell = page.locator('text="3.14"')
cell.click()
editable_cell = page.locator('input[type="number"]')
editable_cell.fill('')
editable_cell.press('Enter')

wait_until(lambda: len(values) == 1, page)
assert values[0][:-1] == ('float', 0, 3.14)
assert np.isnan(values[0][-1])
assert page.query_selector('text="-"') is not None


@pytest.mark.parametrize('pagination', ['remote', 'local'])
def test_tabulator_pagination(page, port, df_mixed, pagination):
page_size = 2
Expand Down