8000 Sorting number rather than string in tabulators (#3999) · holoviz/panel@9a5d7e2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9a5d7e2

Browse files
authored
Sorting number rather than string in tabulators (#3999)
1 parent fe9267b commit 9a5d7e2

File tree

3 files changed

+73
-48
lines changed

3 files changed

+73
-48
lines changed

panel/tests/ui/widgets/test_tabulator.py

10BC0
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3482,3 +3482,23 @@ def test_tabulator_python_filter_edit(page, port):
34823482
wait_until(lambda: len(values) == 2, page)
34833483
assert values[-1] == ('values', len(df) - 1, 'X', 'Y')
34843484
assert df.at['idx3', 'values'] == 'Y'
3485+
3486+
3487+
def test_tabulator_sorter_default_number(page, port):
3488+
df = pd.DataFrame({'x': []}).astype({'x': int})
3489+
widget = Tabulator(df, sorters=[{"field": "x", "dir": "desc"}])
3490+
3491+
serve(widget, port=port, threaded=True, show=False)
3492+
3493+
time.sleep(0.2)
3494+
3495+
page.goto(f"http://localhost:{port}")
3496+
3497+
df2 = pd.DataFrame({'x': [0, 96, 116]})
3498+
widget.value = df2
3499+
3500+
def x_values():
3501+
table_values = [int(v) for v in tabulator_column_values(page, 'x')]
3502+
assert table_values == list(df2['x'].sort_values(ascending=False))
3503+
3504+
wait_until(x_values, page)

panel/tests/widgets/test_tables.py

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,9 @@ def test_tabulator_multi_index(document, comm):
249249
model = table.get_root()
250250

251251
assert model.configuration['columns'] == [
252-
{'field': 'A'},
252+
{'field': 'A', 'sorter': 'number'},
253253
{'field': 'C'},
254-
{'field': 'B'},
254+
{'field': 'B', 'sorter': 'number'},
255255
{'field': 'D', 'sorter': 'timestamp'}
256256
]
257257

@@ -266,9 +266,9 @@ def test_tabulator_multi_index_remote_pagination(document, comm):
266266
model = table.get_root()
267267

268268
assert model.configuration['columns'] == [
269-
{'field': 'A'},
269+
{'field': 'A', 'sorter': 'number'},
270270
{'field': 'C'},
271-
{'field': 'B'},
271+
{'field': 'B', 'sorter': 'number'},
272272
{'field': 'D', 'sorter': 'timestamp'}
273273
]
274274

@@ -382,9 +382,9 @@ def test_tabulator_config_defaults(document, comm):
382382
model = table.get_root(document, comm)
383383

384384
assert model.configuration['columns'] == [
385-
{'field': 'index'},
386-
{'field': 'A'},
387-
{'field': 'B'},
385+
{'field': 'index', 'sorter': 'number'},
386+
{'field': 'A', 'sorter': 'number'},
387+
{'field': 'B', 'sorter': 'number'},
388388
{'field': 'C'},
389389
{'field': 'D', 'sorter': 'timestamp'}
390390
]
@@ -397,9 +397,9 @@ def test_tabulator_config_widths_percent(document, comm):
397397
model = table.get_root(document, comm)
398398

399399
assert model.configuration['columns'] == [
400-
{'field': 'index'},
401-
{'field': 'A', 'width': '22%'},
402-
{'field': 'B'},
400+
{'field': 'index', 'sorter': 'number'},
401+
{'field': 'A', 'sorter': 'number', 'width': '22%'},
402+
{'field': 'B', 'sorter': 'number'},
403403
{'field': 'C'},
404404
{'field': 'D', 'sorter': 'timestamp'}
405405
]
@@ -412,9 +412,9 @@ def test_tabulator_header_filters_config_boolean(document, comm):
412412
model = table.get_root(document, comm)
413413

414414
assert model.configuration['columns'] == [
415-
{'field': 'index', 'headerFilter': 'number'},
416-
{'field': 'A', 'headerFilter': True},
417-
{'field': 'B', 'headerFilter': True},
415+
{'field': 'index', 'sorter': 'number', 'headerFilter': 'number'},
416+
{'field': 'A', 'sorter': 'number', 'headerFilter': True},
417+
{'field': 'B', 'sorter': 'number', 'headerFilter': True},
418418
{'field': 'C', 'headerFilter': True},
419419
{'field': 'D', 'headerFilter': False, 'sorter': 'timestamp'} # Datetime header filtering not supported
420420
]
@@ -426,9 +426,9 @@ def test_tabulator_header_filters_column_config_list(document, comm):
426426
model = table.get_root(document, comm)
427427

428428
assert model.configuration['columns'] == [
429-
{'field': 'index'},
430-
{'field': 'A'},
431-
{'field': 'B'},
429+
{'field': 'index', 'sorter': 'number'},
430+
{'field': 'A', 'sorter': 'number'},
431+
{'field': 'B', 'sorter': 'number'},
432432
{'field': 'C', 'headerFilter': 'list', 'headerFilterParams': {'valuesLookup': True}},
433433
{'field': 'D', 'sorter': 'timestamp'}
434434
]
@@ -445,9 +445,9 @@ def test_tabulator_header_filters_column_config_select_autocomplete_backwards_co
445445
model = table.get_root(document, comm)
446446

447447
assert model.configuration['columns'] == [
448-
{'field': 'index'},
449-
{'field': 'A'},
450-
{'field': 'B'},
448+
{'field': 'index', 'sorter': 'number'},
449+
{'field': 'A', 'sorter': 'number'},
450+
{'field': 'B', 'sorter': 'number'},
451451
{'field': 'C', 'headerFilter': 'list', 'headerFilterParams': {'valuesLookup': True}},
452452
{'field': 'D', 'headerFilter': 'list', 'headerFilterParams': {'valuesLookup': True}, 'sorter': 'timestamp'},
453453
]
@@ -462,9 +462,9 @@ def test_tabulator_header_filters_column_config_dict(document, comm):
462462
model = table.get_root(document, comm)
463463

464464
assert model.configuration['columns'] == [
465-
{'field': 'index'},
466-
{'field': 'A'},
467-
{'field': 'B'},
465+
{'field': 'index', 'sorter': 'number'},
466+
{'field': 'A', 'sorter': 'number'},
467+
{'field': 'B', 'sorter': 'number'},
468468
{
469469
'field': 'C',
470470
'headerFilter': 'list',
@@ -527,7 +527,7 @@ def test_tabulator_config_formatter_string(document, comm):
527527

528528
model = table.get_root(document, comm)
529529

530-
assert model.configuration['columns'][2] == {'field': 'B', 'formatter': 'tickCross'}
530+
assert model.configuration['columns'][2] == {'field': 'B', 'sorter': 'number', 'formatter': 'tickCross'}
531531

532532

533533
def test_tabulator_config_formatter_dict(document, comm):
@@ -536,7 +536,7 @@ def test_tabulator_config_formatter_dict(document, comm):
536536

537537
model = table.get_root(document, comm)
538538

539-
assert model.configuration['columns'][2] == {'field': 'B', 'formatter': 'tickCross', 'formatterParams': {'tristate': True}}
539+
assert model.configuration['columns'][2] == {'field': 'B', 'sorter': 'number', 'formatter': 'tickCross', 'formatterParams': {'tristate': True}}
540540

541541

542542
def test_tabulator_config_editor_string_backwards_compat(document, comm):
@@ -545,7 +545,7 @@ def test_tabulator_config_editor_string_backwards_compat(document, comm):
545545

546546
model = table.get_root(document, comm)
547547

548-
assert model.configuration['columns'][2] == {'field': 'B', 'editor': 'list'}
548+
assert model.configuration['columns'][2] == {'field': 'B', 'sorter': 'number', 'editor': 'list'}
549549

550550

551551
def test_tabulator_config_editor_string(document, comm):
@@ -554,7 +554,7 @@ def test_tabulator_config_editor_string(document, comm):
554554

555555
model = table.get_root(document, comm)
556556

557-
assert model.configuration['columns'][2] == {'field': 'B', 'editor': 'list'}
557+
assert model.configuration['columns'][2] == {'field': 'B', 'sorter': 'number', 'editor': 'list'}
558558

559559

560560
def test_tabulator_config_editor_dict(document, comm):
@@ -563,7 +563,7 @@ def test_tabulator_config_editor_dict(document, comm):
563563

564564
model = table.get_root(document, comm)
565565

566-
assert model.configuration['columns'][2] == {'field': 'B', 'editor': 'list', 'editorParams': {'valuesLookup': True}}
566+
assert model.configuration['columns'][2] == {'field': 'B', 'sorter': 'number', 'editor': 'list', 'editorParams': {'valuesLookup': True}}
567567

568568

569569
def test_tabulator_groups(document, comm):
@@ -573,11 +573,11 @@ def test_tabulator_groups(document, comm):
573573
model = table.get_root(document, comm)
574574

575575
assert model.configuration['columns'] == [
576-
{'field': 'index'},
576+
{'field': 'index', 'sorter': 'number'},
577577
{'title': 'Number',
578578
'columns': [
579-
{'field': 'A'},
580-
{'field': 'B'}
579+
{'field': 'A', 'sorter': 'number'},
580+
{'field': 'B', 'sorter': 'number'}
581581
]},
582582
{'title': 'Other',
583583
'columns': [
@@ -588,19 +588,20 @@ def test_tabulator_groups(document, comm):
588588

589589

590590
def test_tabulator_numeric_groups(document, comm):
591+
print(document)
591592
df = pd.DataFrame(np.random.rand(10, 3))
592593
table = Tabulator(df, groups={'Number': [0, 1]})
593594

594595
model = table.get_root(document, comm)
595596

596597
assert model.configuration['columns'] == [
597-
{'field': 'index'},
598+
{'field': 'index', 'sorter': 'number'},
598599
{'title': 'Number',
599600
'columns': [
600-
{'field': '0'},
601-
{'field': '1'}
601+
{'field': '0', 'sorter': 'number'},
602+
{'field': '1', 'sorter': 'number'}
602603
]},
603-
{'field': '2'}
604+
{'field': '2', 'sorter': 'number'}
604605
]
605606

606607

@@ -611,9 +612,9 @@ def test_tabulator_frozen_cols(document, comm):
611612
model = table.get_root(document, comm)
612613

613614
assert model.configuration['columns'] == [
614-
{'field': 'index', 'frozen': True},
615-
{'field': 'A'},
616-
{'field': 'B'},
615+
{'field': 'index', 'sorter': 'number', 'frozen': True},
616+
{'field': 'A', 'sorter': 'number'},
617+
{'field': 'B', 'sorter': 'number'},
617618
{'field': 'C'},
618619
{'field': 'D', 'sorter': 'timestamp'}
619620
]
@@ -777,7 +778,7 @@ def test_tabulator_sorters_unnamed_index(document, comm):
777778
df = pd.DataFrame(np.random.rand(10, 4))
778779
table = Tabulator(df)
779780

780-
table.sorters = [{'field': 'index', 'dir': 'desc'}]
781+
table.sorters = [{'field': 'index', 'sorter': 'number', 'dir': 'desc'}]
781782

782783
pd.testing.assert_frame_equal(
783784
table.current_view,
@@ -1055,7 +1056,7 @@ def test_tabulator_patch_scalars_not_as_index(document, comm):
10551056

10561057
def test_tabulator_patch_with_filters(document, comm):
10571058
df = makeMixedDataFrame()
1058-
table = Tabulator(df, filters=[{'field': 'A', 'type': '>', 'value': '2'}])
1059+
table = Tabulator(df, filters=[{'field': 'A', 'sorter': 'number', 'type': '>', 'value': '2'}])
10591060

10601061
model = table.get_root(document, comm)
10611062

@@ -1090,7 +1091,7 @@ def test_tabulator_patch_with_filters(document, comm):
10901091

10911092
def test_tabulator_patch_with_sorters(document, comm):
10921093
df = makeMixedDataFrame()
1093-
table = Tabulator(df, sorters=[{'field': 'A', 'dir': 'desc'}])
1094+
table = Tabulator(df, sorters=[{'field': 'A', 'sorter': 'number', 'dir': 'desc'}])
10941095

10951096
model = table.get_root(document, comm)
10961097

@@ -1130,7 +1131,7 @@ def test_tabulator_patch_with_sorters(document, comm):
11301131
def test_tabulator_patch_with_sorters_and_pagination(document, comm):
11311132
df = makeMixedDataFrame()
11321133
table = Tabulator(
1133-
df, sorters=[{'field': 'A', 'dir': 'desc'}],
1134+
df, sorters=[{'field': 'A', 'sorter': 'number', 'dir': 'desc'}],
11341135
pagination='remote', page_size=3, page=2
11351136
)
11361137

@@ -1255,7 +1256,7 @@ def test_tabulator_paginated_sorted_selection(document, comm):
12551256
df = makeMixedDataFrame()
12561257
table = Tabulator(df, pagination='remote', page_size=2)
12571258

1258-
table.sorters = [{'field': 'A', 'dir': 'dec'}]
1259+
table.sorters = [{'field': 'A', 'sorter': 'number', 'dir': 'dec'}]
12591260

12601261
model = table.get_root(document, comm)
12611262

@@ -1277,7 +1278,7 @@ def test_tabulator_paginated_sorted_selection(document, comm):
12771278
table._process_events({'indices': [1]})
12781279
assert table.selection == [3]
12791280

1280-
table.sorters = [{'field': 'A', 'dir': 'asc'}]
1281+
table.sorters = [{'field': 'A', 'sorter': 'number', 'dir': 'asc'}]
12811282
table._process_events({'indices': [1]})
12821283
assert table.selection == [1]
12831284

@@ -1335,7 +1336,7 @@ def test_tabulator_constant_scalar_filter_on_index_client_side(document, comm):
13351336
df = makeMixedDataFrame()
13361337
table = Tabulator(df)
13371338

1338-
table.filters = [{'field': 'index', 'type': '=', 'value': 2}]
1339+
table.filters = [{'field': 'index', 'sorter': 'number', 'type': '=', 'value': 2}]
13391340

13401341
expected = pd.DataFrame({
13411342
'A': np.array([2.]),
@@ -1351,7 +1352,7 @@ def test_tabulator_constant_scalar_filter_on_multi_index_client_side(document, c
13511352
table = Tabulator(df.set_index(['A', 'C']))
13521353

13531354
table.filters = [
1354-
{'field': 'A', 'type': '=', 'value': 2},
1355+
{'field': 'A', 'sorter': 'number', 'type': '=', 'value': 2},
13551356
{'field': 'C', 'type': '=', 'value': 'foo3'}
13561357
]
13571358

@@ -1436,7 +1437,7 @@ def test_tabulator_constant_scalar_filter_on_index_with_pagination_client_side(d
14361437

14371438
model = table.get_root(document, comm)
14381439

1439-
table.filters = [{'field': 'index', 'type': '=', 'value': 2}]
1440+
table.filters = [{'field': 'index', 'sorter': 'number', 'type': '=', 'value': 2}]
14401441

14411442
expected = {
14421443
'index': np.array([2]),
@@ -1456,7 +1457,7 @@ def test_tabulator_constant_scalar_filter_on_multi_index_with_pagination_client_
14561457
model = table.get_root(document, comm)
14571458

14581459
table.filters = [
1459-
{'field': 'A', 'type': '=', 'value': 2},
1460+
{'field': 'A', 'sorter': 'number', 'type': '=', 'value': 2},
14601461
{'field': 'C', 'type': '=', 'value': 'foo3'}
14611462
]
14621463

@@ -1884,7 +1885,7 @@ def test_tabulator_pagination_remote_cell_click_event():
18841885

18851886
def test_tabulator_cell_click_event_error_duplicate_index():
18861887
df = pd.DataFrame(data={'A': [1, 2]}, index=['a', 'a'])
1887-
table = Tabulator(df, sorters=[{'field': 'A', 'dir': 'desc'}])
1888+
table = Tabulator(df, sorters=[{'field': 'A', 'sorter': 'number', 'dir': 'desc'}])
18881889

18891890
values = []
18901891
table.on_click(lambda e: values.append((e.column, e.row, e.value)))

panel/widgets/tables.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,10 @@ def _config_columns(self, column_objs: List[TableColumn]) -> List[Dict[str, Any]
17201720
dtype = self.value.dtypes[col_name]
17211721
if dtype.kind == 'M':
17221722
col_dict['sorter'] = 'timestamp'
1723+
elif dtype.kind in 'iuf':
1724+
col_dict['sorter'] = 'number'
1725+
elif dtype.kind == 'b':
1726+
col_dict['sorter'] = 'boolean'
17231727
editor = self.editors.get(column.field)
17241728
if column.field in self.editors and editor is None:
17251729
col_dict['editable'] = False

0 commit comments

Comments
 (0)
0